diff --git a/Adjust/.gitignore b/Adjust/.gitignore
new file mode 100644
index 000000000..afbdab33e
--- /dev/null
+++ b/Adjust/.gitignore
@@ -0,0 +1,6 @@
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
diff --git a/Adjust/AndroidManifest.xml b/Adjust/AndroidManifest.xml
deleted file mode 100644
index 9c1ed24f4..000000000
--- a/Adjust/AndroidManifest.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/Adjust/adjust/.gitignore b/Adjust/adjust/.gitignore
new file mode 100644
index 000000000..796b96d1c
--- /dev/null
+++ b/Adjust/adjust/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Adjust/adjust/build.gradle b/Adjust/adjust/build.gradle
new file mode 100644
index 000000000..92255c2ab
--- /dev/null
+++ b/Adjust/adjust/build.gradle
@@ -0,0 +1,21 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion "21.1.2"
+
+ defaultConfig {
+ minSdkVersion 9
+ targetSdkVersion 21
+ versionCode 1
+ versionName "4.0.0"
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+}
+
+task jar(type: Jar) {
+ from 'src/main/java'
+}
\ No newline at end of file
diff --git a/Adjust/adjust/src/main/AndroidManifest.xml b/Adjust/adjust/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..d3e803eec
--- /dev/null
+++ b/Adjust/adjust/src/main/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityHandler.java
new file mode 100644
index 000000000..236c9ea4e
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityHandler.java
@@ -0,0 +1,759 @@
+//
+// ActivityHandler.java
+// Adjust
+//
+// Created by Christian Wellenbrock on 2013-06-25.
+// Copyright (c) 2013 adjust GmbH. All rights reserved.
+// See the file MIT-LICENSE for copying permission.
+//
+
+package com.adjust.sdk;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+
+import org.json.JSONObject;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import static com.adjust.sdk.Constants.ACTIVITY_STATE_FILENAME;
+import static com.adjust.sdk.Constants.ATTRIBUTION_FILENAME;
+import static com.adjust.sdk.Constants.LOGTAG;
+
+public class ActivityHandler extends HandlerThread implements IActivityHandler {
+
+ private static long TIMER_INTERVAL;
+ private static long TIMER_START;
+ private static long SESSION_INTERVAL;
+ private static long SUBSESSION_INTERVAL;
+ private static final String TIME_TRAVEL = "Time travel!";
+ private static final String ADJUST_PREFIX = "adjust_";
+ private static final String ACTIVITY_STATE_NAME = "Activity state";
+ private static final String ATTRIBUTION_NAME = "Attribution";
+
+ private SessionHandler sessionHandler;
+ private IPackageHandler packageHandler;
+ private ActivityState activityState;
+ private ILogger logger;
+ private static ScheduledExecutorService timer;
+ private boolean enabled;
+ private boolean offline;
+
+ private DeviceInfo deviceInfo;
+ private AdjustConfig adjustConfig; // always valid after construction
+ private AdjustAttribution attribution;
+ private IAttributionHandler attributionHandler;
+
+ private ActivityHandler(AdjustConfig adjustConfig) {
+ super(LOGTAG, MIN_PRIORITY);
+ setDaemon(true);
+ start();
+
+ logger = AdjustFactory.getLogger();
+ sessionHandler = new SessionHandler(getLooper(), this);
+ enabled = true;
+ init(adjustConfig);
+
+ Message message = Message.obtain();
+ message.arg1 = SessionHandler.INIT;
+ sessionHandler.sendMessage(message);
+ }
+
+ @Override
+ public void init(AdjustConfig adjustConfig) {
+ this.adjustConfig = adjustConfig;
+ }
+
+ public static ActivityHandler getInstance(AdjustConfig adjustConfig) {
+ if (adjustConfig == null) {
+ AdjustFactory.getLogger().error("AdjustConfig missing");
+ return null;
+ }
+
+ if (!adjustConfig.isValid()) {
+ AdjustFactory.getLogger().error("AdjustConfig not initialized correctly");
+ return null;
+ }
+
+ ActivityHandler activityHandler = new ActivityHandler(adjustConfig);
+ return activityHandler;
+ }
+
+ @Override
+ public void trackSubsessionStart() {
+ Message message = Message.obtain();
+ message.arg1 = SessionHandler.START;
+ sessionHandler.sendMessage(message);
+ }
+
+ @Override
+ public void trackSubsessionEnd() {
+ Message message = Message.obtain();
+ message.arg1 = SessionHandler.END;
+ sessionHandler.sendMessage(message);
+ }
+
+ @Override
+ public void trackEvent(AdjustEvent event) {
+ Message message = Message.obtain();
+ message.arg1 = SessionHandler.EVENT;
+ message.obj = event;
+ sessionHandler.sendMessage(message);
+ }
+
+ @Override
+ public void finishedTrackingActivity(JSONObject jsonResponse) {
+ if (jsonResponse == null) {
+ return;
+ }
+
+ Message message = Message.obtain();
+ message.arg1 = SessionHandler.FINISH_TRACKING;
+ message.obj = jsonResponse;
+ sessionHandler.sendMessage(message);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (enabled == this.enabled) {
+ if (enabled) {
+ logger.debug("Adjust already enabled");
+ } else {
+ logger.debug("Adjust already disabled");
+ }
+ return;
+ }
+ this.enabled = enabled;
+ if (activityState != null) {
+ activityState.enabled = enabled;
+ }
+ if (enabled) {
+ if (toPause()) {
+ logger.info("Package and attribution handler remain paused due to the SDK is offline");
+ } else {
+ logger.info("Resuming package handler and attribution handler to enabled the SDK");
+ }
+ trackSubsessionStart();
+ } else {
+ logger.info("Pausing package handler and attribution handler to disable the SDK");
+ trackSubsessionEnd();
+ }
+ }
+
+ @Override
+ public void setOfflineMode(boolean offline) {
+ if (offline == this.offline) {
+ if (offline) {
+ logger.debug("Adjust already in offline mode");
+ } else {
+ logger.debug("Adjust already in online mode");
+ }
+ return;
+ }
+ this.offline = offline;
+ if (offline) {
+ logger.info("Pausing package and attribution handler to put in offline mode");
+ } else {
+ if (toPause()) {
+ logger.info("Package and attribution handler remain paused because the SDK is disabled");
+ } else {
+ logger.info("Resuming package handler and attribution handler to put in online mode");
+ }
+ }
+ updateStatus();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ if (activityState != null) {
+ return activityState.enabled;
+ } else {
+ return enabled;
+ }
+ }
+
+ @Override
+ public void readOpenUrl(Uri url, long clickTime) {
+ Message message = Message.obtain();
+ message.arg1 = SessionHandler.DEEP_LINK;
+ UrlClickTime urlClickTime = new UrlClickTime(url, clickTime);
+ message.obj = urlClickTime;
+ sessionHandler.sendMessage(message);
+ }
+
+ @Override
+ public boolean tryUpdateAttribution(AdjustAttribution attribution) {
+ if (attribution == null) return false;
+
+ if (attribution.equals(this.attribution)) {
+ return false;
+ }
+
+ saveAttribution(attribution);
+ launchAttributionListener();
+ return true;
+ }
+
+ private void saveAttribution(AdjustAttribution attribution) {
+ this.attribution = attribution;
+ writeAttribution();
+ }
+
+ private void launchAttributionListener() {
+ if (adjustConfig.onAttributionChangedListener == null) {
+ return;
+ }
+ Handler handler = new Handler(adjustConfig.context.getMainLooper());
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ adjustConfig.onAttributionChangedListener.onAttributionChanged(attribution);
+ }
+ };
+ handler.post(runnable);
+ }
+
+ @Override
+ public void setAskingAttribution(boolean askingAttribution) {
+ activityState.askingAttribution = askingAttribution;
+ writeActivityState();
+ }
+
+ @Override
+ public ActivityPackage getAttributionPackage() {
+ long now = System.currentTimeMillis();
+ PackageBuilder attributionBuilder = new PackageBuilder(adjustConfig,
+ deviceInfo,
+ activityState,
+ now);
+ return attributionBuilder.buildAttributionPackage();
+ }
+
+ @Override
+ public void sendReferrer(String referrer, long clickTime) {
+ Message message = Message.obtain();
+ message.arg1 = SessionHandler.SEND_REFERRER;
+ ReferrerClickTime referrerClickTime = new ReferrerClickTime(referrer, clickTime);
+ message.obj = referrerClickTime;
+ sessionHandler.sendMessage(message);
+ }
+
+ private class UrlClickTime {
+ Uri url;
+ long clickTime;
+
+ UrlClickTime(Uri url, long clickTime) {
+ this.url = url;
+ this.clickTime = clickTime;
+ }
+ }
+
+ private class ReferrerClickTime {
+ String referrer;
+ long clickTime;
+
+ ReferrerClickTime(String referrer, long clickTime) {
+ this.referrer = referrer;
+ this.clickTime = clickTime;
+ }
+ }
+
+ private void updateStatus() {
+ Message message = Message.obtain();
+ message.arg1 = SessionHandler.UPDATE_STATUS;
+ sessionHandler.sendMessage(message);
+ }
+
+ private static final class SessionHandler extends Handler {
+ private static final int BASE_ADDRESS = 72630;
+ private static final int INIT = BASE_ADDRESS + 1;
+ private static final int START = BASE_ADDRESS + 2;
+ private static final int END = BASE_ADDRESS + 3;
+ private static final int EVENT = BASE_ADDRESS + 4;
+ private static final int FINISH_TRACKING = BASE_ADDRESS + 5;
+ private static final int DEEP_LINK = BASE_ADDRESS + 6;
+ private static final int SEND_REFERRER = BASE_ADDRESS + 7;
+ private static final int UPDATE_STATUS = BASE_ADDRESS + 8;
+
+ private final WeakReference sessionHandlerReference;
+
+ protected SessionHandler(Looper looper, ActivityHandler sessionHandler) {
+ super(looper);
+ this.sessionHandlerReference = new WeakReference(sessionHandler);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ super.handleMessage(message);
+
+ ActivityHandler sessionHandler = sessionHandlerReference.get();
+ if (sessionHandler == null) {
+ return;
+ }
+
+ switch (message.arg1) {
+ case INIT:
+ sessionHandler.initInternal();
+ break;
+ case START:
+ sessionHandler.startInternal();
+ break;
+ case END:
+ sessionHandler.endInternal();
+ break;
+ case EVENT:
+ AdjustEvent event = (AdjustEvent) message.obj;
+ sessionHandler.trackEventInternal(event);
+ break;
+ case FINISH_TRACKING:
+ JSONObject jsonResponse = (JSONObject) message.obj;
+ sessionHandler.finishedTrackingActivityInternal(jsonResponse);
+ break;
+ case DEEP_LINK:
+ UrlClickTime urlClickTime = (UrlClickTime) message.obj;
+ sessionHandler.readOpenUrlInternal(urlClickTime.url, urlClickTime.clickTime);
+ break;
+ case SEND_REFERRER:
+ ReferrerClickTime referrerClickTime = (ReferrerClickTime) message.obj;
+ sessionHandler.sendReferrerInternal(referrerClickTime.referrer, referrerClickTime.clickTime);
+ break;
+ case UPDATE_STATUS:
+ sessionHandler.updateStatusInternal();
+ break;
+ }
+ }
+ }
+
+ private void initInternal() {
+ TIMER_INTERVAL = AdjustFactory.getTimerInterval();
+ TIMER_START = AdjustFactory.getTimerStart();
+ SESSION_INTERVAL = AdjustFactory.getSessionInterval();
+ SUBSESSION_INTERVAL = AdjustFactory.getSubsessionInterval();
+
+ deviceInfo = new DeviceInfo(adjustConfig.context, adjustConfig.sdkPrefix);
+
+ if (adjustConfig.environment == AdjustConfig.ENVIRONMENT_PRODUCTION) {
+ logger.setLogLevel(LogLevel.ASSERT);
+ } else {
+ logger.setLogLevel(adjustConfig.logLevel);
+ }
+
+ if (adjustConfig.eventBufferingEnabled) {
+ logger.info("Event buffering is enabled");
+ }
+
+ String playAdId = Util.getPlayAdId(adjustConfig.context);
+ if (playAdId == null) {
+ logger.info("Unable to get Google Play Services Advertising ID at start time");
+ }
+
+ if (adjustConfig.defaultTracker != null) {
+ logger.info("Default tracker: '%s'", adjustConfig.defaultTracker);
+ }
+
+ if (adjustConfig.referrer != null) {
+ sendReferrer(adjustConfig.referrer, adjustConfig.referrerClickTime); // send to background queue to make sure that activityState is valid
+ }
+
+ readAttribution();
+ readActivityState();
+
+ packageHandler = AdjustFactory.getPackageHandler(this, adjustConfig.context, toPause());
+
+ startInternal();
+ }
+
+ private void startInternal() {
+ // it shouldn't start if it was disabled after a first session
+ if (activityState != null
+ && !activityState.enabled) {
+ return;
+ }
+
+ updateStatusInternal();
+
+ processSession();
+
+ checkAttributionState();
+
+ startTimer();
+ }
+
+ private void processSession() {
+ long now = System.currentTimeMillis();
+
+ // very first session
+ if (activityState == null) {
+ activityState = new ActivityState();
+ activityState.sessionCount = 1; // this is the first session
+
+ transferSessionPackage(now);
+ activityState.resetSessionAttributes(now);
+ activityState.enabled = this.enabled;
+ writeActivityState();
+ return;
+ }
+
+ long lastInterval = now - activityState.lastActivity;
+
+ if (lastInterval < 0) {
+ logger.error(TIME_TRAVEL);
+ activityState.lastActivity = now;
+ writeActivityState();
+ return;
+ }
+
+ // new session
+ if (lastInterval > SESSION_INTERVAL) {
+ activityState.sessionCount++;
+ activityState.lastInterval = lastInterval;
+
+ transferSessionPackage(now);
+ activityState.resetSessionAttributes(now);
+ writeActivityState();
+ return;
+ }
+
+ // new subsession
+ if (lastInterval > SUBSESSION_INTERVAL) {
+ activityState.subsessionCount++;
+ activityState.sessionLength += lastInterval;
+ activityState.lastActivity = now;
+ writeActivityState();
+ logger.info("Started subsession %d of session %d",
+ activityState.subsessionCount,
+ activityState.sessionCount);
+ }
+ }
+
+ private void checkAttributionState() {
+ // if there is no attribution saved, or there is one being asked
+ if (attribution == null || activityState.askingAttribution) {
+ getAttributionHandler().getAttribution();
+ }
+ }
+
+ private void endInternal() {
+ packageHandler.pauseSending();
+ getAttributionHandler().pauseSending();
+ stopTimer();
+ if (updateActivityState(System.currentTimeMillis())) {
+ writeActivityState();
+ }
+ }
+
+ private void trackEventInternal(AdjustEvent event) {
+ if (!checkEvent(event)) return;
+ if (!activityState.enabled) return;
+
+ long now = System.currentTimeMillis();
+
+ activityState.eventCount++;
+ updateActivityState(now);
+
+ PackageBuilder eventBuilder = new PackageBuilder(adjustConfig, deviceInfo, activityState, now);
+ ActivityPackage eventPackage = eventBuilder.buildEventPackage(event);
+ packageHandler.addPackage(eventPackage);
+
+ if (adjustConfig.eventBufferingEnabled) {
+ logger.info("Buffered event %s", eventPackage.getSuffix());
+ } else {
+ packageHandler.sendFirstPackage();
+ }
+
+ writeActivityState();
+ }
+
+ private void finishedTrackingActivityInternal(JSONObject jsonResponse) {
+ if (jsonResponse == null) {
+ return;
+ }
+
+ String deeplink = jsonResponse.optString("deeplink", null);
+ launchDeeplinkMain(deeplink);
+ getAttributionHandler().checkAttribution(jsonResponse);
+ }
+
+ private void sendReferrerInternal(String referrer, long clickTime) {
+ ActivityPackage clickPackage = buildQueryStringClickPackage(referrer,
+ "reftag",
+ clickTime);
+ if (clickPackage == null) {
+ return;
+ }
+
+ getAttributionHandler().getAttribution();
+
+ packageHandler.sendClickPackage(clickPackage);
+ }
+
+ private void readOpenUrlInternal(Uri url, long clickTime) {
+ if (url == null) {
+ return;
+ }
+
+ String queryString = url.getQuery();
+
+ ActivityPackage clickPackage = buildQueryStringClickPackage(queryString, "deeplink", clickTime);
+ if (clickPackage == null) {
+ return;
+ }
+
+ getAttributionHandler().getAttribution();
+
+ packageHandler.sendClickPackage(clickPackage);
+ }
+
+ private ActivityPackage buildQueryStringClickPackage(String queryString, String source, long clickTime) {
+ if (queryString == null) {
+ return null;
+ }
+
+ long now = System.currentTimeMillis();
+ Map queryStringParameters = new HashMap();
+ AdjustAttribution queryStringAttribution = new AdjustAttribution();
+ boolean hasAdjustTags = false;
+
+ String[] queryPairs = queryString.split("&");
+ for (String pair : queryPairs) {
+ if (readQueryString(pair, queryStringParameters, queryStringAttribution)) {
+ hasAdjustTags = true;
+ }
+ }
+
+ if (!hasAdjustTags) {
+ return null;
+ }
+
+ String reftag = queryStringParameters.remove("reftag");
+
+ PackageBuilder builder = new PackageBuilder(adjustConfig, deviceInfo, activityState, now);
+ builder.extraParameters = queryStringParameters;
+ builder.attribution = queryStringAttribution;
+ builder.reftag = reftag;
+ ActivityPackage clickPackage = builder.buildClickPackage(source, clickTime);
+ return clickPackage;
+ }
+
+ private boolean readQueryString(String queryString,
+ Map extraParameters,
+ AdjustAttribution queryStringAttribution) {
+ String[] pairComponents = queryString.split("=");
+ if (pairComponents.length != 2) return false;
+
+ String key = pairComponents[0];
+ if (!key.startsWith(ADJUST_PREFIX)) return false;
+
+ String value = pairComponents[1];
+ if (value.length() == 0) return false;
+
+ String keyWOutPrefix = key.substring(ADJUST_PREFIX.length());
+ if (keyWOutPrefix.length() == 0) return false;
+
+ if (!trySetAttribution(queryStringAttribution, keyWOutPrefix, value)) {
+ extraParameters.put(keyWOutPrefix, value);
+ }
+
+ return true;
+ }
+
+ private boolean trySetAttribution(AdjustAttribution queryStringAttribution,
+ String key,
+ String value) {
+ if (key.equals("tracker")) {
+ queryStringAttribution.trackerName = value;
+ return true;
+ }
+
+ if (key.equals("campaign")) {
+ queryStringAttribution.campaign = value;
+ return true;
+ }
+
+ if (key.equals("adgroup")) {
+ queryStringAttribution.adgroup = value;
+ return true;
+ }
+
+ if (key.equals("creative")) {
+ queryStringAttribution.creative = value;
+ return true;
+ }
+
+ return false;
+ }
+
+ private void updateStatusInternal() {
+ updateAttributionHandlerStatus();
+ updatePackageHandlerStatus();
+ }
+
+ private void updateAttributionHandlerStatus() {
+ if (attributionHandler == null) {
+ return;
+ }
+ if (toPause()) {
+ attributionHandler.pauseSending();
+ } else {
+ attributionHandler.resumeSending();
+ }
+ }
+
+ private void updatePackageHandlerStatus() {
+ if (packageHandler == null) {
+ return;
+ }
+ if (toPause()) {
+ packageHandler.pauseSending();
+ } else {
+ packageHandler.resumeSending();
+ }
+ }
+
+ private void launchDeeplinkMain(String deeplink) {
+ if (deeplink == null) return;
+
+ Uri location = Uri.parse(deeplink);
+ Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
+ mapIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ // Verify it resolves
+ PackageManager packageManager = adjustConfig.context.getPackageManager();
+ List activities = packageManager.queryIntentActivities(mapIntent, 0);
+ boolean isIntentSafe = activities.size() > 0;
+
+ // Start an activity if it's safe
+ if (!isIntentSafe) {
+ logger.error("Unable to open deep link (%s)", deeplink);
+ return;
+ }
+
+ logger.info("Open deep link (%s)", deeplink);
+ adjustConfig.context.startActivity(mapIntent);
+ }
+
+ private boolean updateActivityState(long now) {
+ long lastInterval = now - activityState.lastActivity;
+ // ignore late updates
+ if (lastInterval > SESSION_INTERVAL) {
+ return false;
+ }
+ activityState.lastActivity = now;
+
+ if (lastInterval < 0) {
+ logger.error(TIME_TRAVEL);
+ } else {
+ activityState.sessionLength += lastInterval;
+ activityState.timeSpent += lastInterval;
+ }
+ return true;
+ }
+
+ public static boolean deleteActivityState(Context context) {
+ return context.deleteFile(ACTIVITY_STATE_FILENAME);
+ }
+
+ public static boolean deleteAttribution(Context context) {
+ return context.deleteFile(ATTRIBUTION_FILENAME);
+ }
+
+ private void transferSessionPackage(long now) {
+ PackageBuilder builder = new PackageBuilder(adjustConfig, deviceInfo, activityState, now);
+ ActivityPackage sessionPackage = builder.buildSessionPackage();
+ packageHandler.addPackage(sessionPackage);
+ packageHandler.sendFirstPackage();
+ }
+
+ private void startTimer() {
+ stopTimer();
+
+ if (!activityState.enabled) {
+ return;
+ }
+ timer = Executors.newSingleThreadScheduledExecutor();
+ timer.scheduleWithFixedDelay(new Runnable() {
+ @Override
+ public void run() {
+ timerFired();
+ }
+ }, TIMER_START, TIMER_INTERVAL, TimeUnit.MILLISECONDS);
+ }
+
+ private void stopTimer() {
+ if (timer != null) {
+ timer.shutdown();
+ timer = null;
+ }
+ }
+
+ private void timerFired() {
+ if (!activityState.enabled) {
+ stopTimer();
+ return;
+ }
+
+ packageHandler.sendFirstPackage();
+
+ if (updateActivityState(System.currentTimeMillis())) {
+ writeActivityState();
+ }
+ }
+
+ private void readActivityState() {
+ activityState = Util.readObject(adjustConfig.context, ACTIVITY_STATE_FILENAME, ACTIVITY_STATE_NAME);
+ }
+
+ private void readAttribution() {
+ attribution = Util.readObject(adjustConfig.context, ATTRIBUTION_FILENAME, ATTRIBUTION_NAME);
+ }
+
+ private void writeActivityState() {
+ Util.writeObject(activityState, adjustConfig.context, ACTIVITY_STATE_FILENAME, ACTIVITY_STATE_NAME);
+ }
+
+ private void writeAttribution() {
+ Util.writeObject(attribution, adjustConfig.context, ATTRIBUTION_FILENAME, ATTRIBUTION_NAME);
+ }
+
+ private boolean checkEvent(AdjustEvent event) {
+ if (event == null) {
+ logger.error("Event missing");
+ return false;
+ }
+
+ if (!event.isValid()) {
+ logger.error("Event not initialized correctly");
+ return false;
+ }
+
+ return true;
+ }
+
+ // lazy initialization to prevent null activity state before first session
+ private IAttributionHandler getAttributionHandler() {
+ if (attributionHandler == null) {
+ ActivityPackage attributionPackage = getAttributionPackage();
+ attributionHandler = AdjustFactory.getAttributionHandler(this,
+ attributionPackage,
+ toPause());
+ }
+ return attributionHandler;
+ }
+
+ private boolean toPause() {
+ return offline || !isEnabled();
+ }
+}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityKind.java b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityKind.java
new file mode 100644
index 000000000..a255b83a9
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityKind.java
@@ -0,0 +1,35 @@
+package com.adjust.sdk;
+
+public enum ActivityKind {
+ UNKNOWN, SESSION, EVENT, CLICK, ATTRIBUTION;
+
+ public static ActivityKind fromString(String string) {
+ if ("session".equals(string)) {
+ return SESSION;
+ } else if ("event".equals(string)) {
+ return EVENT;
+ } else if ("click".equals(string)) {
+ return CLICK;
+ } else if ("attribution".equals(string)) {
+ return ATTRIBUTION;
+ } else {
+ return UNKNOWN;
+ }
+ }
+
+ @Override
+ public String toString() {
+ switch (this) {
+ case SESSION:
+ return "session";
+ case EVENT:
+ return "event";
+ case CLICK:
+ return "click";
+ case ATTRIBUTION:
+ return "attribution";
+ default:
+ return "unknown";
+ }
+ }
+}
diff --git a/Adjust/src/com/adjust/sdk/ActivityPackage.java b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityPackage.java
similarity index 80%
rename from Adjust/src/com/adjust/sdk/ActivityPackage.java
rename to Adjust/adjust/src/main/java/com/adjust/sdk/ActivityPackage.java
index 04a442194..27ab969fd 100644
--- a/Adjust/src/com/adjust/sdk/ActivityPackage.java
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityPackage.java
@@ -16,14 +16,13 @@ public class ActivityPackage implements Serializable {
private static final long serialVersionUID = -35935556512024097L;
// data
- private String path;
- private String userAgent;
- private String clientSdk;
+ private String path;
+ private String clientSdk;
private Map parameters;
// logs
private ActivityKind activityKind;
- private String suffix;
+ private String suffix;
public String getPath() {
return path;
@@ -33,14 +32,6 @@ public void setPath(String path) {
this.path = path;
}
- public String getUserAgent() {
- return userAgent;
- }
-
- public void setUserAgent(String userAgent) {
- this.userAgent = userAgent;
- }
-
public String getClientSdk() {
return clientSdk;
}
@@ -61,7 +52,7 @@ public ActivityKind getActivityKind() {
return activityKind;
}
- public void setActivityKind(ActivityKind activityKind ) {
+ public void setActivityKind(ActivityKind activityKind) {
this.activityKind = activityKind;
}
@@ -80,13 +71,12 @@ public String toString() {
public String getExtendedString() {
StringBuilder builder = new StringBuilder();
builder.append(String.format("Path: %s\n", path));
- builder.append(String.format("UserAgent: %s\n", userAgent));
builder.append(String.format("ClientSdk: %s\n", clientSdk));
if (parameters != null) {
builder.append("Parameters:");
- for (Map.Entry entity : parameters.entrySet()) {
- builder.append(String.format("\n\t%-16s %s", entity.getKey(), entity.getValue()));
+ for (Map.Entry entry : parameters.entrySet()) {
+ builder.append(String.format("\n\t%-16s %s", entry.getKey(), entry.getValue()));
}
}
return builder.toString();
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityState.java b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityState.java
new file mode 100644
index 000000000..41ad2ca3b
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityState.java
@@ -0,0 +1,151 @@
+//
+// ActivityState.java
+// Adjust
+//
+// Created by Christian Wellenbrock on 2013-06-25.
+// Copyright (c) 2013 adjust GmbH. All rights reserved.
+// See the file MIT-LICENSE for copying permission.
+//
+
+package com.adjust.sdk;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectInputStream.GetField;
+import java.io.Serializable;
+import java.util.Calendar;
+import java.util.Locale;
+
+public class ActivityState implements Serializable, Cloneable {
+ private static final long serialVersionUID = 9039439291143138148L;
+ private transient String readErrorMessage = "Unable to read '%s' field in migration device with message (%s)";
+ private transient ILogger logger;
+
+ // persistent data
+ protected String uuid;
+ protected boolean enabled;
+ protected boolean askingAttribution;
+
+ // global counters
+ protected int eventCount;
+ protected int sessionCount;
+
+ // session attributes
+ protected int subsessionCount;
+ protected long sessionLength; // all durations in milliseconds
+ protected long timeSpent;
+ protected long lastActivity; // all times in milliseconds since 1970
+
+ protected long lastInterval;
+
+ protected ActivityState() {
+ logger = AdjustFactory.getLogger();
+ // create UUID for new devices
+ uuid = Util.createUuid();
+ enabled = true;
+ askingAttribution = false;
+
+ eventCount = 0; // no events yet
+ sessionCount = 0; // the first session just started
+ subsessionCount = -1; // we don't know how many subsessions this first session will have
+ sessionLength = -1; // same for session length and time spent
+ timeSpent = -1; // this information will be collected and attached to the next session
+ lastActivity = -1;
+ lastInterval = -1;
+ }
+
+ protected void resetSessionAttributes(long now) {
+ subsessionCount = 1; // first subsession
+ sessionLength = 0; // no session length yet
+ timeSpent = 0; // no time spent yet
+ lastActivity = now;
+ lastInterval = -1;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(Locale.US,
+ "ec:%d sc:%d ssc:%d sl:%.1f ts:%.1f la:%s uuid:%s",
+ eventCount, sessionCount, subsessionCount,
+ sessionLength / 1000.0, timeSpent / 1000.0,
+ stamp(lastActivity), uuid);
+ }
+
+ @Override
+ public ActivityState clone() {
+ try {
+ return (ActivityState) super.clone();
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
+
+
+ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
+ GetField fields = stream.readFields();
+
+ eventCount = readIntField(fields, "eventCount", 0);
+ sessionCount = readIntField(fields, "sessionCount", 0);
+ subsessionCount = readIntField(fields, "subsessionCount", -1);
+ sessionLength = readLongField(fields, "sessionLength", -1l);
+ timeSpent = readLongField(fields, "timeSpent", -1l);
+ lastActivity = readLongField(fields, "lastActivity", -1l);
+ lastInterval = readLongField(fields, "lastInterval", -1l);
+
+ // new fields
+ uuid = readStringField(fields, "uuid", null);
+ enabled = readBooleanField(fields, "enabled", true);
+ askingAttribution = readBooleanField(fields, "askingAttribution", false);
+
+ // create UUID for migrating devices
+ if (uuid == null) {
+ uuid = Util.createUuid();
+ }
+ }
+
+ private String readStringField(GetField fields, String name, String defaultValue) {
+ try {
+ return (String) fields.get(name, defaultValue);
+ } catch (Exception e) {
+ logger.debug(readErrorMessage, name, e.getMessage());
+ return defaultValue;
+ }
+ }
+
+ private boolean readBooleanField(GetField fields, String name, boolean defaultValue) {
+ try {
+ return fields.get(name, defaultValue);
+ } catch (Exception e) {
+ logger.debug(readErrorMessage, name, e.getMessage());
+ return defaultValue;
+ }
+ }
+
+ private int readIntField(GetField fields, String name, int defaultValue) {
+ try {
+ return fields.get(name, defaultValue);
+ } catch (Exception e) {
+ logger.debug(readErrorMessage, name, e.getMessage());
+ return defaultValue;
+ }
+ }
+
+ private long readLongField(GetField fields, String name, long defaultValue) {
+ try {
+ return fields.get(name, defaultValue);
+ } catch (Exception e) {
+ logger.debug(readErrorMessage, name, e.getMessage());
+ return defaultValue;
+ }
+ }
+
+ private static String stamp(long dateMillis) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(dateMillis);
+ return String.format(Locale.US,
+ "%02d:%02d:%02d",
+ calendar.HOUR_OF_DAY,
+ calendar.MINUTE,
+ calendar.SECOND);
+ }
+}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/Adjust.java b/Adjust/adjust/src/main/java/com/adjust/sdk/Adjust.java
new file mode 100644
index 000000000..3b81a077b
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/Adjust.java
@@ -0,0 +1,79 @@
+//
+// Adjust.java
+// Adjust
+//
+// Created by Christian Wellenbrock on 2012-10-11.
+// Copyright (c) 2012-2014 adjust GmbH. All rights reserved.
+// See the file MIT-LICENSE for copying permission.
+//
+
+package com.adjust.sdk;
+
+import android.net.Uri;
+
+/**
+ * The main interface to Adjust.
+ * Use the methods of this class to tell Adjust about the usage of your app.
+ * See the README for details.
+ */
+public class Adjust {
+
+ private static AdjustInstance defaultInstance;
+
+ private Adjust() {
+ }
+
+ public static synchronized AdjustInstance getDefaultInstance() {
+ if (defaultInstance == null) {
+ defaultInstance = new AdjustInstance();
+ }
+ return defaultInstance;
+ }
+
+ public static void onCreate(AdjustConfig adjustConfig) {
+ AdjustInstance adjustInstance = Adjust.getDefaultInstance();
+ adjustInstance.onCreate(adjustConfig);
+ }
+
+ public static void trackEvent(AdjustEvent event) {
+ AdjustInstance adjustInstance = Adjust.getDefaultInstance();
+ adjustInstance.trackEvent(event);
+ }
+
+ public static void onResume() {
+ AdjustInstance adjustInstance = Adjust.getDefaultInstance();
+ adjustInstance.onResume();
+ }
+
+ public static void onPause() {
+ AdjustInstance adjustInstance = Adjust.getDefaultInstance();
+ adjustInstance.onPause();
+ }
+
+ public static void setEnabled(boolean enabled) {
+ AdjustInstance adjustInstance = Adjust.getDefaultInstance();
+ adjustInstance.setEnabled(enabled);
+ }
+
+ public static boolean isEnabled() {
+ AdjustInstance adjustInstance = Adjust.getDefaultInstance();
+ return adjustInstance.isEnabled();
+ }
+
+ public static void appWillOpenUrl(Uri url) {
+ AdjustInstance adjustInstance = Adjust.getDefaultInstance();
+ adjustInstance.appWillOpenUrl(url);
+ }
+
+ public static void setReferrer(String referrer) {
+ AdjustInstance adjustInstance = Adjust.getDefaultInstance();
+ adjustInstance.sendReferrer(referrer);
+ }
+
+ public static void setOfflineMode(boolean enabled) {
+ AdjustInstance adjustInstance = Adjust.getDefaultInstance();
+ adjustInstance.setOfflineMode(enabled);
+ }
+}
+
+
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustAttribution.java b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustAttribution.java
new file mode 100644
index 000000000..4e3abb017
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustAttribution.java
@@ -0,0 +1,62 @@
+package com.adjust.sdk;
+
+import org.json.JSONObject;
+
+import java.io.Serializable;
+
+/**
+ * Created by pfms on 07/11/14.
+ */
+public class AdjustAttribution implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public String trackerToken;
+ public String trackerName;
+ public String network;
+ public String campaign;
+ public String adgroup;
+ public String creative;
+
+ public static AdjustAttribution fromJson(JSONObject jsonObject) {
+ if (jsonObject == null) return null;
+
+ AdjustAttribution attribution = new AdjustAttribution();
+
+ attribution.trackerToken = jsonObject.optString("tracker_token", null);
+ attribution.trackerName = jsonObject.optString("tracker_name", null);
+ attribution.network = jsonObject.optString("network", null);
+ attribution.campaign = jsonObject.optString("campaign", null);
+ attribution.adgroup = jsonObject.optString("adgroup", null);
+ attribution.creative = jsonObject.optString("creative", null);
+
+ return attribution;
+ }
+
+ public boolean equals(Object other) {
+ if (other == this) return true;
+ if (other == null) return false;
+ if (getClass() != other.getClass()) return false;
+ AdjustAttribution otherAttribution = (AdjustAttribution) other;
+
+ if (!equalString(trackerToken, otherAttribution.trackerToken)) return false;
+ if (!equalString(trackerName, otherAttribution.trackerName)) return false;
+ if (!equalString(network, otherAttribution.network)) return false;
+ if (!equalString(campaign, otherAttribution.campaign)) return false;
+ if (!equalString(adgroup, otherAttribution.adgroup)) return false;
+ if (!equalString(creative, otherAttribution.creative)) return false;
+ return true;
+ }
+
+ private boolean equalString(String first, String second) {
+ if (first == null || second == null) {
+ return first == null && second == null;
+ }
+ return first.equals(second);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("tt:%s tn:%s net:%s cam:%s adg:%s cre:%s",
+ trackerToken, trackerName, network, campaign, adgroup, creative);
+ }
+}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustConfig.java b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustConfig.java
new file mode 100644
index 000000000..148a5f670
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustConfig.java
@@ -0,0 +1,128 @@
+package com.adjust.sdk;
+
+import android.content.Context;
+
+/**
+ * Created by pfms on 06/11/14.
+ */
+public class AdjustConfig {
+ Context context;
+ String appToken;
+ String environment;
+ LogLevel logLevel;
+ String sdkPrefix;
+ Boolean eventBufferingEnabled;
+ String defaultTracker;
+ OnAttributionChangedListener onAttributionChangedListener;
+ String referrer;
+ long referrerClickTime;
+ Boolean knownDevice;
+
+ public static final String ENVIRONMENT_SANDBOX = "sandbox";
+ public static final String ENVIRONMENT_PRODUCTION = "production";
+
+ public AdjustConfig(Context context, String appToken, String environment) {
+ if (!isValid(context, appToken, environment)) {
+ return;
+ }
+
+ this.context = context.getApplicationContext();
+ this.appToken = appToken;
+ this.environment = environment;
+
+ // default values
+ this.logLevel = LogLevel.INFO;
+ this.eventBufferingEnabled = false;
+ }
+
+ public void setEventBufferingEnabled(Boolean eventBufferingEnabled) {
+ this.eventBufferingEnabled = eventBufferingEnabled;
+ }
+
+ public void setLogLevel(LogLevel logLevel) {
+ this.logLevel = logLevel;
+ }
+
+ public void setSdkPrefix(String sdkPrefix) {
+ this.sdkPrefix = sdkPrefix;
+ }
+
+ public void setDefaultTracker(String defaultTracker) {
+ this.defaultTracker = defaultTracker;
+ }
+
+ public void setOnAttributionChangedListener(OnAttributionChangedListener onAttributionChangedListener) {
+ this.onAttributionChangedListener = onAttributionChangedListener;
+ }
+
+ public boolean hasListener() {
+ return onAttributionChangedListener != null;
+ }
+
+ public boolean isValid() {
+ return appToken != null;
+ }
+
+ private boolean isValid(Context context, String appToken, String environment) {
+ if (!checkAppToken(appToken)) return false;
+ if (!checkEnvironment(environment)) return false;
+ if (!checkContext(context)) return false;
+
+ return true;
+ }
+
+ private static boolean checkContext(Context context) {
+ ILogger logger = AdjustFactory.getLogger();
+ if (context == null) {
+ logger.error("Missing context");
+ return false;
+ }
+
+ if (!Util.checkPermission(context, android.Manifest.permission.INTERNET)) {
+ logger.error("Missing permission: INTERNET");
+ return false;
+ }
+
+ return true;
+ }
+
+ private static boolean checkAppToken(String appToken) {
+ ILogger logger = AdjustFactory.getLogger();
+ if (appToken == null) {
+ logger.error("Missing App Token.");
+ return false;
+ }
+
+ if (appToken.length() != 12) {
+ logger.error("Malformed App Token '%s'", appToken);
+ return false;
+ }
+
+ return true;
+ }
+
+ private static boolean checkEnvironment(String environment) {
+ ILogger logger = AdjustFactory.getLogger();
+ if (environment == null) {
+ logger.error("Missing environment");
+ return false;
+ }
+
+ if (environment == AdjustConfig.ENVIRONMENT_SANDBOX) {
+ logger.Assert("SANDBOX: Adjust is running in Sandbox mode. " +
+ "Use this setting for testing. " +
+ "Don't forget to set the environment to `production` before publishing!");
+ return true;
+ }
+ if (environment == AdjustConfig.ENVIRONMENT_PRODUCTION) {
+ logger.Assert(
+ "PRODUCTION: Adjust is running in Production mode. " +
+ "Use this setting only for the build that you want to publish. " +
+ "Set the environment to `sandbox` if you want to test your app!");
+ return true;
+ }
+
+ logger.error("Unknown environment '%s'", environment);
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustEvent.java b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustEvent.java
new file mode 100644
index 000000000..f03718183
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustEvent.java
@@ -0,0 +1,112 @@
+package com.adjust.sdk;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by pfms on 05/11/14.
+ */
+public class AdjustEvent {
+ String eventToken;
+ Double revenue;
+ String currency;
+ Map callbackParameters;
+ Map partnerParameters;
+
+ private static ILogger logger = AdjustFactory.getLogger();
+
+ public AdjustEvent(String eventToken) {
+ if (!checkEventToken(eventToken, logger)) return;
+
+ this.eventToken = eventToken;
+ }
+
+ public void setRevenue(double revenue, String currency) {
+ if (!checkRevenue(revenue, currency)) return;
+
+ this.revenue = revenue;
+ this.currency = currency;
+ }
+
+ public void addCallbackParameter(String key, String value) {
+ if (!isValidParameter(key, "key", "Callback")) return;
+ if (!isValidParameter(value, "value", "Callback")) return;
+
+ if (callbackParameters == null) {
+ callbackParameters = new HashMap();
+ }
+
+ String previousValue = callbackParameters.put(key, value);
+
+ if (previousValue != null) {
+ logger.warn("key %s was overwritten", key);
+ }
+ }
+
+ public void addPartnerParameter(String key, String value) {
+ if (!isValidParameter(key, "key", "Partner")) return;
+ if (!isValidParameter(value, "value", "Partner")) return;
+
+ if (partnerParameters == null) {
+ partnerParameters = new HashMap();
+ }
+
+ String previousValue = partnerParameters.put(key, value);
+
+ if (previousValue != null) {
+ logger.warn("key %s was overwritten", key);
+ }
+ }
+
+ public boolean isValid() {
+ return eventToken != null;
+ }
+
+ private static boolean checkEventToken(String eventToken, ILogger logger) {
+ if (eventToken == null) {
+ logger.error("Missing Event Token");
+ return false;
+ }
+ if (eventToken.length() != 6) {
+ logger.error("Malformed Event Token '%s'", eventToken);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean checkRevenue(Double revenue, String currency) {
+ if (revenue != null) {
+ if (revenue < 0.0) {
+ logger.error("Invalid amount %.4f", revenue);
+ return false;
+ }
+
+ if (currency == null) {
+ logger.error("Currency must be set with revenue");
+ return false;
+ }
+ if (currency == "") {
+ logger.error("Currency is empty");
+ return false;
+ }
+
+ } else if (currency != null) {
+ logger.error("Revenue must be set with currency");
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isValidParameter(String attribute, String attributeType, String parameterName) {
+ if (attribute == null) {
+ logger.error("%s parameter %s is missing", parameterName, attributeType);
+ return false;
+ }
+ if (attribute == "") {
+ logger.error("%s parameter %s is empty", parameterName, attributeType);
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/Adjust/src/com/adjust/sdk/AdjustFactory.java b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustFactory.java
similarity index 57%
rename from Adjust/src/com/adjust/sdk/AdjustFactory.java
rename to Adjust/adjust/src/main/java/com/adjust/sdk/AdjustFactory.java
index d54ac1a43..1ad772b24 100644
--- a/Adjust/src/com/adjust/sdk/AdjustFactory.java
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustFactory.java
@@ -1,25 +1,31 @@
package com.adjust.sdk;
+import android.content.Context;
+
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
-import android.content.Context;
-
public class AdjustFactory {
private static IPackageHandler packageHandler = null;
private static IRequestHandler requestHandler = null;
- private static Logger logger = null;
+ private static IAttributionHandler attributionHandler = null;
+ private static IActivityHandler activityHandler = null;
+ private static ILogger logger = null;
private static HttpClient httpClient = null;
private static long timerInterval = -1;
+ private static long timerStart = -1;
private static long sessionInterval = -1;
private static long subsessionInterval = -1;
- public static IPackageHandler getPackageHandler(ActivityHandler activityHandler, Context context, boolean dropOfflineActivities) {
+ public static IPackageHandler getPackageHandler(ActivityHandler activityHandler,
+ Context context,
+ boolean startPaused) {
if (packageHandler == null) {
- return new PackageHandler(activityHandler, context, dropOfflineActivities);
+ return new PackageHandler(activityHandler, context, startPaused);
}
+ packageHandler.init(activityHandler, context, startPaused);
return packageHandler;
}
@@ -27,13 +33,14 @@ public static IRequestHandler getRequestHandler(IPackageHandler packageHandler)
if (requestHandler == null) {
return new RequestHandler(packageHandler);
}
+ requestHandler.init(packageHandler);
return requestHandler;
}
- public static Logger getLogger() {
+ public static ILogger getLogger() {
if (logger == null) {
// Logger needs to be "static" to retain the configuration throughout the app
- logger = new LogCatLogger();
+ logger = new Logger();
}
return logger;
}
@@ -52,6 +59,13 @@ public static long getTimerInterval() {
return timerInterval;
}
+ public static long getTimerStart() {
+ if (timerStart == -1) {
+ return 0;
+ }
+ return timerStart;
+ }
+
public static long getSessionInterval() {
if (sessionInterval == -1) {
return Constants.THIRTY_MINUTES;
@@ -66,6 +80,24 @@ public static long getSubsessionInterval() {
return subsessionInterval;
}
+ public static IActivityHandler getActivityHandler(AdjustConfig config) {
+ if (activityHandler == null) {
+ return ActivityHandler.getInstance(config);
+ }
+ activityHandler.init(config);
+ return activityHandler;
+ }
+
+ public static IAttributionHandler getAttributionHandler(IActivityHandler activityHandler,
+ ActivityPackage attributionPackage,
+ boolean startPaused) {
+ if (attributionHandler == null) {
+ return new AttributionHandler(activityHandler, attributionPackage, startPaused);
+ }
+ attributionHandler.init(activityHandler, attributionPackage, startPaused);
+ return attributionHandler;
+ }
+
public static void setPackageHandler(IPackageHandler packageHandler) {
AdjustFactory.packageHandler = packageHandler;
}
@@ -74,7 +106,7 @@ public static void setRequestHandler(IRequestHandler requestHandler) {
AdjustFactory.requestHandler = requestHandler;
}
- public static void setLogger(Logger logger) {
+ public static void setLogger(ILogger logger) {
AdjustFactory.logger = logger;
}
@@ -86,6 +118,10 @@ public static void setTimerInterval(long timerInterval) {
AdjustFactory.timerInterval = timerInterval;
}
+ public static void setTimerStart(long timerStart) {
+ AdjustFactory.timerStart = timerStart;
+ }
+
public static void setSessionInterval(long sessionInterval) {
AdjustFactory.sessionInterval = sessionInterval;
}
@@ -94,4 +130,12 @@ public static void setSubsessionInterval(long subsessionInterval) {
AdjustFactory.subsessionInterval = subsessionInterval;
}
+ public static void setActivityHandler(IActivityHandler activityHandler) {
+ AdjustFactory.activityHandler = activityHandler;
+ }
+
+ public static void setAttributionHandler(IAttributionHandler attributionHandler) {
+ AdjustFactory.attributionHandler = attributionHandler;
+ }
+
}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustInstance.java b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustInstance.java
new file mode 100644
index 000000000..158fb7ca1
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustInstance.java
@@ -0,0 +1,86 @@
+package com.adjust.sdk;
+
+import android.net.Uri;
+
+/**
+ * Created by pfms on 04/12/14.
+ */
+public class AdjustInstance {
+
+ private String referrer;
+ private long referrerClickTime;
+ private ActivityHandler activityHandler;
+
+ private static ILogger getLogger() {
+ return AdjustFactory.getLogger();
+ }
+
+ public void onCreate(AdjustConfig adjustConfig) {
+ if (activityHandler != null) {
+ getLogger().error("Adjust already initialized");
+ return;
+ }
+
+ adjustConfig.referrer = this.referrer;
+ adjustConfig.referrerClickTime = this.referrerClickTime;
+
+ activityHandler = ActivityHandler.getInstance(adjustConfig);
+ }
+
+ public void trackEvent(AdjustEvent event) {
+ if (!checkActivityHandler()) return;
+ activityHandler.trackEvent(event);
+ }
+
+ public void onResume() {
+ if (!checkActivityHandler()) return;
+ activityHandler.trackSubsessionStart();
+ }
+
+ public void onPause() {
+ if (!checkActivityHandler()) return;
+ activityHandler.trackSubsessionEnd();
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (!checkActivityHandler()) return;
+ activityHandler.setEnabled(enabled);
+ }
+
+ public boolean isEnabled() {
+ if (!checkActivityHandler()) return false;
+ return activityHandler.isEnabled();
+ }
+
+ public void appWillOpenUrl(Uri url) {
+ if (!checkActivityHandler()) return;
+ long clickTime = System.currentTimeMillis();
+ activityHandler.readOpenUrl(url, clickTime);
+ }
+
+ public void sendReferrer(String referrer) {
+ long clickTime = System.currentTimeMillis();
+ // sendReferrer might be triggered before Adjust
+ if (activityHandler == null) {
+ // save it to inject in the config before launch
+ this.referrer = referrer;
+ this.referrerClickTime = clickTime;
+ } else {
+ activityHandler.sendReferrer(referrer, clickTime);
+ }
+ }
+
+ public void setOfflineMode(boolean enabled) {
+ if (!checkActivityHandler()) return;
+ activityHandler.setOfflineMode(enabled);
+ }
+
+ private boolean checkActivityHandler() {
+ if (activityHandler == null) {
+ getLogger().error("Please initialize Adjust by calling 'onCreate' before");
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
diff --git a/Adjust/src/com/adjust/sdk/ReferrerReceiver.java b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustReferrerReceiver.java
similarity index 70%
rename from Adjust/src/com/adjust/sdk/ReferrerReceiver.java
rename to Adjust/adjust/src/main/java/com/adjust/sdk/AdjustReferrerReceiver.java
index eab1536fb..cfeecd8d0 100644
--- a/Adjust/src/com/adjust/sdk/ReferrerReceiver.java
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustReferrerReceiver.java
@@ -3,22 +3,18 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import static com.adjust.sdk.Constants.ENCODING;
-import static com.adjust.sdk.Constants.MALFORMED;
-import static com.adjust.sdk.Constants.REFERRER;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
+import static com.adjust.sdk.Constants.ENCODING;
+import static com.adjust.sdk.Constants.MALFORMED;
+import static com.adjust.sdk.Constants.REFERRER;
+
// support multiple BroadcastReceivers for the INSTALL_REFERRER:
// http://blog.appington.com/2012/08/01/giving-credit-for-android-app-installs
-public class ReferrerReceiver extends BroadcastReceiver {
-
- protected static final String REFERRER_KEY = "AdjustInstallReferrer";
-
+public class AdjustReferrerReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String rawReferrer = intent.getStringExtra(REFERRER);
@@ -33,7 +29,7 @@ public void onReceive(Context context, Intent intent) {
referrer = MALFORMED;
}
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
- preferences.edit().putString(REFERRER_KEY, referrer).commit();
+ AdjustInstance adjust = Adjust.getDefaultInstance();
+ adjust.sendReferrer(referrer);
}
}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/AttributionHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/AttributionHandler.java
new file mode 100644
index 000000000..2a2bef03d
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/AttributionHandler.java
@@ -0,0 +1,155 @@
+package com.adjust.sdk;
+
+import android.net.Uri;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.json.JSONObject;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by pfms on 07/11/14.
+ */
+public class AttributionHandler implements IAttributionHandler {
+ private ScheduledExecutorService scheduler;
+ private IActivityHandler activityHandler;
+ private ILogger logger;
+ private ActivityPackage attributionPackage;
+ private ScheduledFuture waitingTask;
+ private HttpClient httpClient;
+ private boolean paused;
+
+ public AttributionHandler(IActivityHandler activityHandler,
+ ActivityPackage attributionPackage,
+ boolean startPaused) {
+ scheduler = Executors.newSingleThreadScheduledExecutor();
+ logger = AdjustFactory.getLogger();
+ httpClient = Util.getHttpClient();
+ init(activityHandler, attributionPackage, startPaused);
+ }
+
+ @Override
+ public void init(IActivityHandler activityHandler,
+ ActivityPackage attributionPackage,
+ boolean startPaused) {
+ this.activityHandler = activityHandler;
+ this.attributionPackage = attributionPackage;
+ this.paused = startPaused;
+ }
+
+ @Override
+ public void getAttribution() {
+ getAttribution(0);
+ }
+
+ @Override
+ public void checkAttribution(final JSONObject jsonResponse) {
+ scheduler.submit(new Runnable() {
+ @Override
+ public void run() {
+ checkAttributionInternal(jsonResponse);
+ }
+ });
+ }
+
+ @Override
+ public void pauseSending() {
+ paused = true;
+ }
+
+ @Override
+ public void resumeSending() {
+ paused = false;
+ }
+
+ private void getAttribution(int delayInMilliseconds) {
+ if (waitingTask != null) {
+ waitingTask.cancel(false);
+ }
+
+ if (delayInMilliseconds != 0) {
+ logger.debug("Waiting to query attribution in %d milliseconds", delayInMilliseconds);
+ }
+
+ waitingTask = scheduler.schedule(new Runnable() {
+ @Override
+ public void run() {
+ getAttributionInternal();
+ }
+ }, delayInMilliseconds, TimeUnit.MILLISECONDS);
+ }
+
+ private void checkAttributionInternal(JSONObject jsonResponse) {
+ if (jsonResponse == null) return;
+
+ JSONObject attributionJson = jsonResponse.optJSONObject("attribution");
+ AdjustAttribution attribution = AdjustAttribution.fromJson(attributionJson);
+
+ int timerMilliseconds = jsonResponse.optInt("ask_in", -1);
+
+ // without ask_in attribute
+ if (timerMilliseconds < 0) {
+ activityHandler.tryUpdateAttribution(attribution);
+
+ activityHandler.setAskingAttribution(false);
+
+ return;
+ }
+
+ activityHandler.setAskingAttribution(true);
+
+ getAttribution(timerMilliseconds);
+ }
+
+ private void getAttributionInternal() {
+ if (paused) {
+ logger.debug("Attribution Handler is paused");
+ return;
+ }
+ logger.verbose("%s", attributionPackage.getExtendedString());
+ HttpResponse httpResponse = null;
+ try {
+ HttpGet request = getRequest(attributionPackage);
+ httpResponse = httpClient.execute(request);
+ } catch (Exception e) {
+ logger.error("Failed to get attribution (%s)", e.getMessage());
+ return;
+ }
+
+ JSONObject jsonResponse = Util.parseJsonResponse(httpResponse, logger);
+
+ checkAttributionInternal(jsonResponse);
+ }
+
+ private Uri buildUri(ActivityPackage attributionPackage) {
+ Uri.Builder uriBuilder = new Uri.Builder();
+
+ uriBuilder.scheme(Constants.SCHEME);
+ uriBuilder.authority(Constants.AUTHORITY);
+ uriBuilder.appendPath(attributionPackage.getPath());
+
+ for (Map.Entry entry : attributionPackage.getParameters().entrySet()) {
+ uriBuilder.appendQueryParameter(entry.getKey(), entry.getValue());
+ }
+
+ return uriBuilder.build();
+ }
+
+ private HttpGet getRequest(ActivityPackage attributionPackage) throws URISyntaxException {
+ HttpGet request = new HttpGet();
+ Uri uri = buildUri(attributionPackage);
+ request.setURI(new URI(uri.toString()));
+
+ request.addHeader("Client-SDK", attributionPackage.getClientSdk());
+
+ return request;
+ }
+}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/Constants.java b/Adjust/adjust/src/main/java/com/adjust/sdk/Constants.java
new file mode 100644
index 000000000..7a97cb2f4
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/Constants.java
@@ -0,0 +1,53 @@
+//
+// Constants.java
+// Adjust
+//
+// Created by keyboardsurfer on 2013-11-08.
+// Copyright (c) 2012-2014 adjust GmbH. All rights reserved.
+// See the file MIT-LICENSE for copying permission.
+//
+
+package com.adjust.sdk;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author keyboardsurfer
+ * @since 8.11.13
+ */
+public interface Constants {
+ int ONE_SECOND = 1000;
+ int ONE_MINUTE = 60 * ONE_SECOND;
+ int THIRTY_MINUTES = 30 * ONE_MINUTE;
+
+ int CONNECTION_TIMEOUT = Constants.ONE_MINUTE;
+ int SOCKET_TIMEOUT = Constants.ONE_MINUTE;
+
+ String BASE_URL = "https://app.adjust.com";
+ String SCHEME = "https";
+ String AUTHORITY = "app.adjust.com";
+ String CLIENT_SDK = "android4.0.0";
+ String LOGTAG = "Adjust";
+
+ String ACTIVITY_STATE_FILENAME = "AdjustIoActivityState";
+ String ATTRIBUTION_FILENAME = "AdjustAttribution";
+
+ String MALFORMED = "malformed";
+ String SMALL = "small";
+ String NORMAL = "normal";
+ String LONG = "long";
+ String LARGE = "large";
+ String XLARGE = "xlarge";
+ String LOW = "low";
+ String MEDIUM = "medium";
+ String HIGH = "high";
+ String REFERRER = "referrer";
+
+ String ENCODING = "UTF-8";
+ String MD5 = "MD5";
+ String SHA1 = "SHA-1";
+
+ // List of known plugins, possibly not active
+ List PLUGINS = Arrays.asList();
+}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/DeviceInfo.java b/Adjust/adjust/src/main/java/com/adjust/sdk/DeviceInfo.java
new file mode 100644
index 000000000..5cccb77f4
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/DeviceInfo.java
@@ -0,0 +1,290 @@
+package com.adjust.sdk;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.util.DisplayMetrics;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.util.Locale;
+import java.util.Map;
+
+import static com.adjust.sdk.Constants.ENCODING;
+import static com.adjust.sdk.Constants.HIGH;
+import static com.adjust.sdk.Constants.LARGE;
+import static com.adjust.sdk.Constants.LONG;
+import static com.adjust.sdk.Constants.LOW;
+import static com.adjust.sdk.Constants.MD5;
+import static com.adjust.sdk.Constants.MEDIUM;
+import static com.adjust.sdk.Constants.NORMAL;
+import static com.adjust.sdk.Constants.SHA1;
+import static com.adjust.sdk.Constants.SMALL;
+import static com.adjust.sdk.Constants.XLARGE;
+
+/**
+ * Created by pfms on 06/11/14.
+ */
+class DeviceInfo {
+ String macSha1;
+ String macShortMd5;
+ String androidId;
+ String fbAttributionId;
+ String clientSdk;
+ String packageName;
+ String appVersion;
+ String deviceType;
+ String deviceName;
+ String deviceManufacturer;
+ String osName;
+ String osVersion;
+ String language;
+ String country;
+ String screenSize;
+ String screenFormat;
+ String screenDensity;
+ String displayWidth;
+ String displayHeight;
+ Map pluginKeys;
+
+ DeviceInfo(Context context, String sdkPrefix) {
+ Resources resources = context.getResources();
+ DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+ Configuration configuration = resources.getConfiguration();
+ Locale locale = configuration.locale;
+ int screenLayout = configuration.screenLayout;
+ boolean isGooglePlayServicesAvailable = Reflection.isGooglePlayServicesAvailable(context);
+ String macAddress = getMacAddress(context, isGooglePlayServicesAvailable);
+
+ packageName = getPackageName(context);
+ appVersion = getAppVersion(context);
+ deviceType = getDeviceType(screenLayout);
+ deviceName = getDeviceName();
+ deviceManufacturer = getDeviceManufacturer();
+ osName = getOsName();
+ osVersion = getOsVersion();
+ language = getLanguage(locale);
+ country = getCountry(locale);
+ screenSize = getScreenSize(screenLayout);
+ screenFormat = getScreenFormat(screenLayout);
+ screenDensity = getScreenDensity(displayMetrics);
+ displayWidth = getDisplayWidth(displayMetrics);
+ displayHeight = getDisplayHeight(displayMetrics);
+ clientSdk = getClientSdk(sdkPrefix);
+ androidId = getAndroidId(context, isGooglePlayServicesAvailable);
+ fbAttributionId = getFacebookAttributionId(context);
+ pluginKeys = Reflection.getPluginKeys(context);
+ macSha1 = getMacSha1(macAddress);
+ macShortMd5 = getMacShortMd5(macAddress);
+ }
+
+ private String getMacAddress(Context context, boolean isGooglePlayServicesAvailable) {
+ if (!isGooglePlayServicesAvailable) {
+ if (!!Util.checkPermission(context, android.Manifest.permission.ACCESS_WIFI_STATE)) {
+ AdjustFactory.getLogger().warn("Missing permission: ACCESS_WIFI_STATE");
+ }
+ return Reflection.getMacAddress(context);
+ } else {
+ return null;
+ }
+ }
+
+ private String getPackageName(Context context) {
+ return context.getPackageName();
+ }
+
+ private String getAppVersion(Context context) {
+ try {
+ PackageManager packageManager = context.getPackageManager();
+ String name = context.getPackageName();
+ PackageInfo info = packageManager.getPackageInfo(name, 0);
+ return info.versionName;
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
+
+ private String getDeviceType(int screenLayout) {
+ int screenSize = screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
+
+ switch (screenSize) {
+ case Configuration.SCREENLAYOUT_SIZE_SMALL:
+ case Configuration.SCREENLAYOUT_SIZE_NORMAL:
+ return "phone";
+ case Configuration.SCREENLAYOUT_SIZE_LARGE:
+ case 4:
+ return "tablet";
+ default:
+ return null;
+ }
+ }
+
+ private String getDeviceName() {
+ return Build.MODEL;
+ }
+
+ private String getDeviceManufacturer() {
+ return Build.MANUFACTURER;
+ }
+
+ private String getOsName() {
+ return "android";
+ }
+
+ private String getOsVersion() {
+ return osVersion = "" + Build.VERSION.SDK_INT;
+ }
+
+ private String getLanguage(Locale locale) {
+ return locale.getLanguage();
+ }
+
+ private String getCountry(Locale locale) {
+ return locale.getCountry();
+ }
+
+ private String getScreenSize(int screenLayout) {
+ int screenSize = screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
+
+ switch (screenSize) {
+ case Configuration.SCREENLAYOUT_SIZE_SMALL:
+ return SMALL;
+ case Configuration.SCREENLAYOUT_SIZE_NORMAL:
+ return NORMAL;
+ case Configuration.SCREENLAYOUT_SIZE_LARGE:
+ return LARGE;
+ case 4:
+ return XLARGE;
+ default:
+ return null;
+ }
+ }
+
+ private String getScreenFormat(int screenLayout) {
+ int screenFormat = screenLayout & Configuration.SCREENLAYOUT_LONG_MASK;
+
+ switch (screenFormat) {
+ case Configuration.SCREENLAYOUT_LONG_YES:
+ return LONG;
+ case Configuration.SCREENLAYOUT_LONG_NO:
+ return NORMAL;
+ default:
+ return null;
+ }
+ }
+
+ private String getScreenDensity(DisplayMetrics displayMetrics) {
+ int density = displayMetrics.densityDpi;
+ int low = (DisplayMetrics.DENSITY_MEDIUM + DisplayMetrics.DENSITY_LOW) / 2;
+ int high = (DisplayMetrics.DENSITY_MEDIUM + DisplayMetrics.DENSITY_HIGH) / 2;
+
+ if (0 == density) {
+ return null;
+ } else if (density < low) {
+ return LOW;
+ } else if (density > high) {
+ return HIGH;
+ }
+ return MEDIUM;
+ }
+
+ private String getDisplayWidth(DisplayMetrics displayMetrics) {
+ return String.valueOf(displayMetrics.widthPixels);
+ }
+
+ private String getDisplayHeight(DisplayMetrics displayMetrics) {
+ return String.valueOf(displayMetrics.heightPixels);
+ }
+
+ private String getClientSdk(String sdkPrefix) {
+ if (sdkPrefix == null) {
+ return Constants.CLIENT_SDK;
+ } else {
+ return String.format("%s@%s", sdkPrefix, Constants.CLIENT_SDK);
+ }
+ }
+
+ private String getMacSha1(String macAddress) {
+ if (macAddress == null) {
+ return null;
+ }
+ String macSha1 = sha1(macAddress);
+
+ return macSha1;
+ }
+
+ private String getMacShortMd5(String macAddress) {
+ if (macAddress == null) {
+ return null;
+ }
+ String macShort = macAddress.replaceAll(":", "");
+ String macShortMd5 = md5(macShort);
+
+ return macShortMd5;
+ }
+
+ private String getAndroidId(Context context, boolean isGooglePlayServicesAvailable) {
+ if (!isGooglePlayServicesAvailable) {
+ return Reflection.getAndroidId(context);
+ } else {
+ return null;
+ }
+ }
+
+ private String sha1(final String text) {
+ return hash(text, SHA1);
+ }
+
+ private String md5(final String text) {
+ return hash(text, MD5);
+ }
+
+ private String hash(final String text, final String method) {
+ String hashString = null;
+ try {
+ final byte[] bytes = text.getBytes(ENCODING);
+ final MessageDigest mesd = MessageDigest.getInstance(method);
+ mesd.update(bytes, 0, bytes.length);
+ final byte[] hash = mesd.digest();
+ hashString = convertToHex(hash);
+ } catch (Exception e) {
+ }
+ return hashString;
+ }
+
+ private static String convertToHex(final byte[] bytes) {
+ final BigInteger bigInt = new BigInteger(1, bytes);
+ final String formatString = "%0" + (bytes.length << 1) + "x";
+ return String.format(formatString, bigInt);
+ }
+
+ private String getFacebookAttributionId(final Context context) {
+ try {
+ final ContentResolver contentResolver = context.getContentResolver();
+ final Uri uri = Uri.parse("content://com.facebook.katana.provider.AttributionIdProvider");
+ final String columnName = "aid";
+ final String[] projection = {columnName};
+ final Cursor cursor = contentResolver.query(uri, projection, null, null, null);
+
+ if (null == cursor) {
+ return null;
+ }
+ if (!cursor.moveToFirst()) {
+ cursor.close();
+ return null;
+ }
+
+ final String attributionId = cursor.getString(cursor.getColumnIndex(columnName));
+ cursor.close();
+ return attributionId;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/IActivityHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/IActivityHandler.java
new file mode 100644
index 000000000..10b92205d
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/IActivityHandler.java
@@ -0,0 +1,36 @@
+package com.adjust.sdk;
+
+import android.net.Uri;
+
+import org.json.JSONObject;
+
+/**
+ * Created by pfms on 15/12/14.
+ */
+public interface IActivityHandler {
+ public void init(AdjustConfig config);
+
+ public void trackSubsessionStart();
+
+ public void trackSubsessionEnd();
+
+ public void trackEvent(AdjustEvent event);
+
+ public void finishedTrackingActivity(JSONObject jsonResponse);
+
+ public void setEnabled(boolean enabled);
+
+ public boolean isEnabled();
+
+ public void readOpenUrl(Uri url, long clickTime);
+
+ public boolean tryUpdateAttribution(AdjustAttribution attribution);
+
+ public void sendReferrer(String referrer, long clickTime);
+
+ public void setOfflineMode(boolean enabled);
+
+ public void setAskingAttribution(boolean askingAttribution);
+
+ public ActivityPackage getAttributionPackage();
+}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/IAttributionHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/IAttributionHandler.java
new file mode 100644
index 000000000..d4e701f75
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/IAttributionHandler.java
@@ -0,0 +1,20 @@
+package com.adjust.sdk;
+
+import org.json.JSONObject;
+
+/**
+ * Created by pfms on 15/12/14.
+ */
+public interface IAttributionHandler {
+ public void init(IActivityHandler activityHandler,
+ ActivityPackage attributionPackage,
+ boolean startPaused);
+
+ public void getAttribution();
+
+ public void checkAttribution(JSONObject jsonResponse);
+
+ public void pauseSending();
+
+ public void resumeSending();
+}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/ILogger.java b/Adjust/adjust/src/main/java/com/adjust/sdk/ILogger.java
new file mode 100644
index 000000000..28f92af4b
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/ILogger.java
@@ -0,0 +1,20 @@
+package com.adjust.sdk;
+
+public interface ILogger {
+ public void setLogLevel(LogLevel logLevel);
+
+ public void setLogLevelString(String logLevelString);
+
+ public void verbose(String message, Object... parameters);
+
+ public void debug(String message, Object... parameters);
+
+ public void info(String message, Object... parameters);
+
+ public void warn(String message, Object... parameters);
+
+ public void error(String message, Object... parameters);
+
+ public void Assert(String message, Object... parameters);
+
+}
diff --git a/Adjust/src/com/adjust/sdk/IPackageHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/IPackageHandler.java
similarity index 58%
rename from Adjust/src/com/adjust/sdk/IPackageHandler.java
rename to Adjust/adjust/src/main/java/com/adjust/sdk/IPackageHandler.java
index 0334aaa50..99c300364 100644
--- a/Adjust/src/com/adjust/sdk/IPackageHandler.java
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/IPackageHandler.java
@@ -1,8 +1,12 @@
package com.adjust.sdk;
+import android.content.Context;
+
import org.json.JSONObject;
public interface IPackageHandler {
+ public void init(IActivityHandler activityHandler, Context context, boolean startPaused);
+
public void addPackage(ActivityPackage pack);
public void sendFirstPackage();
@@ -17,7 +21,7 @@ public interface IPackageHandler {
public String getFailureMessage();
- public boolean dropsOfflineActivities();
+ public void finishedTrackingActivity(JSONObject jsonResponse);
- public void finishedTrackingActivity(ActivityPackage activityPackage, ResponseData responseData, JSONObject jsonResponse);
+ public void sendClickPackage(ActivityPackage clickPackage);
}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/IRequestHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/IRequestHandler.java
new file mode 100644
index 000000000..5b18e2ee9
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/IRequestHandler.java
@@ -0,0 +1,9 @@
+package com.adjust.sdk;
+
+public interface IRequestHandler {
+ public void init(IPackageHandler packageHandler);
+
+ public void sendPackage(ActivityPackage pack);
+
+ public void sendClickPackage(ActivityPackage clickPackage);
+}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/LogLevel.java b/Adjust/adjust/src/main/java/com/adjust/sdk/LogLevel.java
new file mode 100644
index 000000000..5c0b410c2
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/LogLevel.java
@@ -0,0 +1,19 @@
+package com.adjust.sdk;
+
+import android.util.Log;
+
+/**
+ * Created by pfms on 11/03/15.
+ */
+public enum LogLevel {
+ VERBOSE(Log.VERBOSE), DEBUG(Log.DEBUG), INFO(Log.INFO), WARN(Log.WARN), ERROR(Log.ERROR), ASSERT(Log.ASSERT);
+ final int androidLogLevel;
+
+ LogLevel(final int androidLogLevel) {
+ this.androidLogLevel = androidLogLevel;
+ }
+
+ public int getAndroidLogLevel() {
+ return androidLogLevel;
+ }
+}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/Logger.java b/Adjust/adjust/src/main/java/com/adjust/sdk/Logger.java
new file mode 100644
index 000000000..86a644d4a
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/Logger.java
@@ -0,0 +1,107 @@
+//
+// Logger.java
+// Adjust
+//
+// Created by Christian Wellenbrock on 2013-04-18.
+// Copyright (c) 2013 adjust GmbH. All rights reserved.
+// See the file MIT-LICENSE for copying permission.
+//
+
+package com.adjust.sdk;
+
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.Locale;
+
+import static com.adjust.sdk.Constants.LOGTAG;
+
+public class Logger implements ILogger {
+
+ private LogLevel logLevel;
+ private static String formatErrorMessage = "Error formating log message: %s, with params: %s";
+
+ public Logger() {
+ setLogLevel(LogLevel.INFO);
+ }
+
+ @Override
+ public void setLogLevel(LogLevel logLevel) {
+ this.logLevel = logLevel;
+ }
+
+ @Override
+ public void setLogLevelString(String logLevelString) {
+ if (null != logLevelString) {
+ try {
+ setLogLevel(LogLevel.valueOf(logLevelString.toUpperCase(Locale.US)));
+ } catch (IllegalArgumentException iae) {
+ error("Malformed logLevel '%s', falling back to 'info'", logLevelString);
+ }
+ }
+ }
+
+ @Override
+ public void verbose(String message, Object... parameters) {
+ if (logLevel.androidLogLevel <= Log.VERBOSE) {
+ try {
+ Log.v(LOGTAG, String.format(message, parameters));
+ } catch (Exception e) {
+ Log.e(LOGTAG, String.format(formatErrorMessage, message, Arrays.toString(parameters)));
+ }
+ }
+ }
+
+ @Override
+ public void debug(String message, Object... parameters) {
+ if (logLevel.androidLogLevel <= Log.DEBUG) {
+ try {
+ Log.d(LOGTAG, String.format(message, parameters));
+ } catch (Exception e) {
+ Log.e(LOGTAG, String.format(formatErrorMessage, message, Arrays.toString(parameters)));
+ }
+ }
+ }
+
+ @Override
+ public void info(String message, Object... parameters) {
+ if (logLevel.androidLogLevel <= Log.INFO) {
+ try {
+ Log.i(LOGTAG, String.format(message, parameters));
+ } catch (Exception e) {
+ Log.e(LOGTAG, String.format(formatErrorMessage, message, Arrays.toString(parameters)));
+ }
+ }
+ }
+
+ @Override
+ public void warn(String message, Object... parameters) {
+ if (logLevel.androidLogLevel <= Log.WARN) {
+ try {
+ Log.w(LOGTAG, String.format(message, parameters));
+ } catch (Exception e) {
+ Log.e(LOGTAG, String.format(formatErrorMessage, message, Arrays.toString(parameters)));
+ }
+ }
+ }
+
+ @Override
+ public void error(String message, Object... parameters) {
+ if (logLevel.androidLogLevel <= Log.ERROR) {
+ try {
+ Log.e(LOGTAG, String.format(message, parameters));
+ } catch (Exception e) {
+ Log.e(LOGTAG, String.format(formatErrorMessage, message, Arrays.toString(parameters)));
+ }
+ }
+ }
+
+ @Override
+ public void Assert(String message, Object... parameters) {
+ try {
+ Log.println(Log.ASSERT, LOGTAG, String.format(message, parameters));
+ } catch (Exception e) {
+ Log.e(LOGTAG, String.format(formatErrorMessage, message, Arrays.toString(parameters)));
+ }
+ }
+}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/OnAttributionChangedListener.java b/Adjust/adjust/src/main/java/com/adjust/sdk/OnAttributionChangedListener.java
new file mode 100644
index 000000000..137d50d4d
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/OnAttributionChangedListener.java
@@ -0,0 +1,5 @@
+package com.adjust.sdk;
+
+public interface OnAttributionChangedListener {
+ public void onAttributionChanged(AdjustAttribution attribution);
+}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/PackageBuilder.java b/Adjust/adjust/src/main/java/com/adjust/sdk/PackageBuilder.java
new file mode 100644
index 000000000..3a43045fd
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/PackageBuilder.java
@@ -0,0 +1,291 @@
+//
+// PackageBuilder.java
+// Adjust
+//
+// Created by Christian Wellenbrock on 2013-06-25.
+// Copyright (c) 2013 adjust GmbH. All rights reserved.
+// See the file MIT-LICENSE for copying permission.
+//
+
+package com.adjust.sdk;
+
+import android.text.TextUtils;
+
+import org.json.JSONObject;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+class PackageBuilder {
+ private AdjustConfig adjustConfig;
+ private DeviceInfo deviceInfo;
+ private ActivityState activityState;
+ private long createdAt;
+
+ // reattributions
+ Map extraParameters;
+ AdjustAttribution attribution;
+ String reftag;
+
+ private static ILogger logger = AdjustFactory.getLogger();
+
+ public PackageBuilder(AdjustConfig adjustConfig,
+ DeviceInfo deviceInfo,
+ ActivityState activityState,
+ long createdAt) {
+ this.adjustConfig = adjustConfig;
+ this.deviceInfo = deviceInfo;
+ this.activityState = activityState.clone();
+ this.createdAt = createdAt;
+ }
+
+ public ActivityPackage buildSessionPackage() {
+ Map parameters = getDefaultParameters();
+ addDuration(parameters, "last_interval", activityState.lastInterval);
+ addString(parameters, "default_tracker", adjustConfig.defaultTracker);
+
+ ActivityPackage sessionPackage = getDefaultActivityPackage();
+ sessionPackage.setPath("/session");
+ sessionPackage.setActivityKind(ActivityKind.SESSION);
+ sessionPackage.setSuffix("");
+ sessionPackage.setParameters(parameters);
+
+ return sessionPackage;
+ }
+
+ public ActivityPackage buildEventPackage(AdjustEvent event) {
+ Map parameters = getDefaultParameters();
+ addInt(parameters, "event_count", activityState.eventCount);
+ addString(parameters, "event_token", event.eventToken);
+ addDouble(parameters, "revenue", event.revenue);
+ addString(parameters, "currency", event.currency);
+ addMapJson(parameters, "callback_params", event.callbackParameters);
+ addMapJson(parameters, "partner_params", event.partnerParameters);
+
+ ActivityPackage eventPackage = getDefaultActivityPackage();
+ eventPackage.setPath("/event");
+ eventPackage.setActivityKind(ActivityKind.EVENT);
+ eventPackage.setSuffix(getEventSuffix(event));
+ eventPackage.setParameters(parameters);
+
+ return eventPackage;
+ }
+
+ public ActivityPackage buildClickPackage(String source, long clickTime) {
+ Map parameters = getDefaultParameters();
+
+ addString(parameters, "source", source);
+ addDate(parameters, "click_time", clickTime);
+ addString(parameters, "reftag", reftag);
+ addMapJson(parameters, "params", extraParameters);
+ injectAttribution(parameters);
+
+ ActivityPackage clickPackage = getDefaultActivityPackage();
+ clickPackage.setPath("/sdk_click");
+ clickPackage.setActivityKind(ActivityKind.CLICK);
+ clickPackage.setSuffix("");
+ clickPackage.setParameters(parameters);
+
+ return clickPackage;
+ }
+
+ public ActivityPackage buildAttributionPackage() {
+ Map parameters = getIdsParameters();
+
+ ActivityPackage attributionPackage = getDefaultActivityPackage();
+ attributionPackage.setPath("attribution"); // does not contain '/' because of Uri.Builder.appendPath
+ attributionPackage.setActivityKind(ActivityKind.ATTRIBUTION);
+ attributionPackage.setSuffix("");
+ attributionPackage.setParameters(parameters);
+
+ return attributionPackage;
+ }
+
+ private ActivityPackage getDefaultActivityPackage() {
+ ActivityPackage activityPackage = new ActivityPackage();
+ activityPackage.setClientSdk(deviceInfo.clientSdk);
+ return activityPackage;
+ }
+
+ private Map getDefaultParameters() {
+ Map parameters = new HashMap();
+
+ injectDeviceInfo(parameters);
+ injectConfig(parameters);
+ injectActivityState(parameters);
+ addDate(parameters, "created_at", createdAt);
+
+ // general
+ checkDeviceIds(parameters);
+
+ return parameters;
+ }
+
+ private Map getIdsParameters() {
+ Map parameters = new HashMap();
+
+ injectDeviceInfoIds(parameters);
+ injectConfig(parameters);
+ injectActivityStateIds(parameters);
+
+ checkDeviceIds(parameters);
+
+ return parameters;
+ }
+
+ private void injectDeviceInfo(Map parameters) {
+ injectDeviceInfoIds(parameters);
+ addString(parameters, "fb_id", deviceInfo.fbAttributionId);
+ addString(parameters, "package_name", deviceInfo.packageName);
+ addString(parameters, "app_version", deviceInfo.appVersion);
+ addString(parameters, "device_type", deviceInfo.deviceType);
+ addString(parameters, "device_name", deviceInfo.deviceName);
+ addString(parameters, "device_manufacturer", deviceInfo.deviceManufacturer);
+ addString(parameters, "os_name", deviceInfo.osName);
+ addString(parameters, "os_version", deviceInfo.osVersion);
+ addString(parameters, "language", deviceInfo.language);
+ addString(parameters, "country", deviceInfo.country);
+ addString(parameters, "screen_size", deviceInfo.screenSize);
+ addString(parameters, "screen_format", deviceInfo.screenFormat);
+ addString(parameters, "screen_density", deviceInfo.screenDensity);
+ addString(parameters, "display_width", deviceInfo.displayWidth);
+ addString(parameters, "display_height", deviceInfo.displayHeight);
+ fillPluginKeys(parameters);
+ }
+
+ private void injectDeviceInfoIds(Map parameters) {
+ addString(parameters, "mac_sha1", deviceInfo.macSha1);
+ addString(parameters, "mac_md5", deviceInfo.macShortMd5);
+ addString(parameters, "android_id", deviceInfo.androidId);
+ }
+
+ private void injectConfig(Map parameters) {
+ addString(parameters, "app_token", adjustConfig.appToken);
+ addString(parameters, "environment", adjustConfig.environment);
+ addBoolean(parameters, "device_known", adjustConfig.knownDevice);
+ addBoolean(parameters, "needs_attribution_data", adjustConfig.hasListener());
+
+ String playAdId = Util.getPlayAdId(adjustConfig.context);
+ addString(parameters, "gps_adid", playAdId);
+ Boolean isTrackingEnabled = Util.isPlayTrackingEnabled(adjustConfig.context);
+ addBoolean(parameters, "tracking_enabled", isTrackingEnabled);
+ }
+
+ private void injectActivityState(Map parameters) {
+ injectActivityStateIds(parameters);
+ addInt(parameters, "session_count", activityState.sessionCount);
+ addInt(parameters, "subsession_count", activityState.subsessionCount);
+ addDuration(parameters, "session_length", activityState.sessionLength);
+ addDuration(parameters, "time_spent", activityState.timeSpent);
+ }
+
+ private void injectActivityStateIds(Map parameters) {
+ addString(parameters, "android_uuid", activityState.uuid);
+ }
+
+ private void injectAttribution(Map parameters) {
+ if (attribution == null) {
+ return;
+ }
+ addString(parameters, "tracker", attribution.trackerName);
+ addString(parameters, "campaign", attribution.campaign);
+ addString(parameters, "adgroup", attribution.adgroup);
+ addString(parameters, "creative", attribution.creative);
+ }
+
+ private void checkDeviceIds(Map parameters) {
+ if (!parameters.containsKey("mac_sha1")
+ && !parameters.containsKey("mac_md5")
+ && !parameters.containsKey("android_id")
+ && !parameters.containsKey("gps_adid")) {
+ logger.error("Missing device id's. Please check if Proguard is correctly set with Adjust SDK");
+ }
+ }
+
+ private void fillPluginKeys(Map parameters) {
+ if (deviceInfo.pluginKeys == null) {
+ return;
+ }
+
+ for (Map.Entry entry : deviceInfo.pluginKeys.entrySet()) {
+ addString(parameters, entry.getKey(), entry.getValue());
+ }
+ }
+
+ private String getEventSuffix(AdjustEvent event) {
+ if (event.revenue == null) {
+ return String.format(" '%s'", event.eventToken);
+ } else {
+ return String.format(Locale.US, " (%.4f %s, '%s')", event.revenue, event.currency, event.eventToken);
+ }
+ }
+
+ private void addString(Map parameters, String key, String value) {
+ if (TextUtils.isEmpty(value)) {
+ return;
+ }
+
+ parameters.put(key, value);
+ }
+
+ private void addInt(Map parameters, String key, long value) {
+ if (value < 0) {
+ return;
+ }
+
+ String valueString = Long.toString(value);
+ addString(parameters, key, valueString);
+ }
+
+ private void addDate(Map parameters, String key, long value) {
+ if (value < 0) {
+ return;
+ }
+
+ String dateString = Util.dateFormat(value);
+ addString(parameters, key, dateString);
+ }
+
+ private void addDuration(Map parameters, String key, long durationInMilliSeconds) {
+ if (durationInMilliSeconds < 0) {
+ return;
+ }
+
+ long durationInSeconds = (durationInMilliSeconds + 500) / 1000;
+ addInt(parameters, key, durationInSeconds);
+ }
+
+ private void addMapJson(Map parameters, String key, Map map) {
+ if (map == null) {
+ return;
+ }
+
+ if (map.size() == 0) {
+ return;
+ }
+
+ JSONObject jsonObject = new JSONObject(map);
+ String jsonString = jsonObject.toString();
+
+ addString(parameters, key, jsonString);
+ }
+
+ private void addBoolean(Map parameters, String key, Boolean value) {
+ if (value == null) {
+ return;
+ }
+
+ int intValue = value ? 1 : 0;
+
+ addInt(parameters, key, intValue);
+ }
+
+ private void addDouble(Map parameters, String key, Double value) {
+ if (value == null) return;
+
+ String doubleString = String.format("%.5f", value);
+
+ addString(parameters, key, doubleString);
+ }
+}
diff --git a/Adjust/src/com/adjust/sdk/PackageHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/PackageHandler.java
similarity index 79%
rename from Adjust/src/com/adjust/sdk/PackageHandler.java
rename to Adjust/adjust/src/main/java/com/adjust/sdk/PackageHandler.java
index 2feb5a7f6..d0a84ccd1 100644
--- a/Adjust/src/com/adjust/sdk/PackageHandler.java
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/PackageHandler.java
@@ -9,6 +9,14 @@
package com.adjust.sdk;
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+
+import org.json.JSONObject;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
@@ -24,44 +32,42 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
-import org.json.JSONObject;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-
// persistent
public class PackageHandler extends HandlerThread implements IPackageHandler {
private static final String PACKAGE_QUEUE_FILENAME = "AdjustIoPackageQueue";
- private final InternalHandler internalHandler;
- private IRequestHandler requestHandler;
- private ActivityHandler activityHandler;
- private List packageQueue;
- private AtomicBoolean isSending;
- private boolean paused;
- private Context context;
- private boolean dropOfflineActivities;
- private Logger logger;
-
- public PackageHandler(ActivityHandler activityHandler, Context context, boolean dropOfflineActivities) {
+ private final InternalHandler internalHandler;
+ private IRequestHandler requestHandler;
+ private IActivityHandler activityHandler;
+ private List packageQueue;
+ private AtomicBoolean isSending;
+ private boolean paused;
+ private Context context;
+ private ILogger logger;
+
+ public PackageHandler(IActivityHandler activityHandler,
+ Context context,
+ boolean startPaused) {
super(Constants.LOGTAG, MIN_PRIORITY);
setDaemon(true);
start();
this.internalHandler = new InternalHandler(getLooper(), this);
this.logger = AdjustFactory.getLogger();
- this.activityHandler = activityHandler;
- this.context = context;
- this.dropOfflineActivities = dropOfflineActivities;
+ init(activityHandler, context, startPaused);
Message message = Message.obtain();
message.arg1 = InternalHandler.INIT;
internalHandler.sendMessage(message);
}
+ @Override
+ public void init(IActivityHandler activityHandler, Context context, boolean startPaused) {
+ this.activityHandler = activityHandler;
+ this.context = context;
+ this.paused = startPaused;
+ }
+
// add a package to the queue
@Override
public void addPackage(ActivityPackage pack) {
@@ -91,11 +97,7 @@ public void sendNextPackage() {
// close the package to retry in the future (after temporary failure)
@Override
public void closeFirstPackage() {
- if (dropOfflineActivities) {
- sendNextPackage();
- } else {
- isSending.set(false);
- }
+ isSending.set(false);
}
// interrupt the sending loop after the current request has finished
@@ -113,35 +115,25 @@ public void resumeSending() {
// short info about how failing packages are handled
@Override
public String getFailureMessage() {
- if (dropOfflineActivities) {
- return "Dropping offline activity.";
- } else {
- return "Will retry later.";
- }
+ return "Will retry later.";
}
@Override
- public boolean dropsOfflineActivities() {
- return dropOfflineActivities;
+ public void finishedTrackingActivity(JSONObject jsonResponse) {
+ activityHandler.finishedTrackingActivity(jsonResponse);
}
@Override
- public void finishedTrackingActivity(ActivityPackage activityPackage, ResponseData responseData, JSONObject jsonResponse) {
- responseData.setActivityKind(activityPackage.getActivityKind());
-
- String deepLink = null;
-
- if (jsonResponse != null) {
- deepLink = jsonResponse.optString("deeplink", null);
- }
-
- activityHandler.finishedTrackingActivity(responseData, deepLink);
+ public void sendClickPackage(ActivityPackage clickPackage) {
+ logger.debug("Sending click package (%s)", clickPackage);
+ logger.verbose("%s", clickPackage.getExtendedString());
+ requestHandler.sendClickPackage(clickPackage);
}
private static final class InternalHandler extends Handler {
- private static final int INIT = 1;
- private static final int ADD = 2;
- private static final int SEND_NEXT = 3;
+ private static final int INIT = 1;
+ private static final int ADD = 2;
+ private static final int SEND_NEXT = 3;
private static final int SEND_FIRST = 4;
private final WeakReference packageHandlerReference;
@@ -191,7 +183,7 @@ private void initInternal() {
private void addInternal(ActivityPackage newPackage) {
packageQueue.add(newPackage);
logger.debug("Added package %d (%s)", packageQueue.size(), newPackage);
- logger.verbose(newPackage.getExtendedString());
+ logger.verbose("%s", newPackage.getExtendedString());
writePackageQueue();
}
@@ -222,11 +214,6 @@ private void sendNextInternal() {
}
private void readPackageQueue() {
- if (dropOfflineActivities) {
- packageQueue = new ArrayList();
- return; // don't read old packages when offline tracking is disabled
- }
-
try {
FileInputStream inputStream = context.openFileInput(PACKAGE_QUEUE_FILENAME);
BufferedInputStream bufferedStream = new BufferedInputStream(inputStream);
@@ -266,10 +253,6 @@ public static Boolean deletePackageQueue(Context context) {
private void writePackageQueue() {
- if (dropOfflineActivities) {
- return; // don't write packages when offline tracking is disabled
- }
-
try {
FileOutputStream outputStream = context.openFileOutput(PACKAGE_QUEUE_FILENAME, Context.MODE_PRIVATE);
BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream);
diff --git a/Adjust/src/com/adjust/sdk/Reflection.java b/Adjust/adjust/src/main/java/com/adjust/sdk/Reflection.java
similarity index 69%
rename from Adjust/src/com/adjust/sdk/Reflection.java
rename to Adjust/adjust/src/main/java/com/adjust/sdk/Reflection.java
index 4e4faf0a9..d9d9a9dbc 100644
--- a/Adjust/src/com/adjust/sdk/Reflection.java
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/Reflection.java
@@ -1,10 +1,18 @@
package com.adjust.sdk;
+import android.content.Context;
+
+import com.adjust.sdk.plugin.Plugin;
+
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
-import android.content.Context;
+import static com.adjust.sdk.Constants.PLUGINS;
public class Reflection {
@@ -15,8 +23,7 @@ public static String getPlayAdId(Context context) {
String playAdid = (String) invokeInstanceMethod(AdvertisingInfoObject, "getId", null);
return playAdid;
- }
- catch (Throwable t) {
+ } catch (Throwable t) {
return null;
}
}
@@ -28,8 +35,7 @@ public static Boolean isPlayTrackingEnabled(Context context) {
Boolean isLimitedTrackingEnabled = (Boolean) invokeInstanceMethod(AdvertisingInfoObject, "isLimitAdTrackingEnabled", null);
return !isLimitedTrackingEnabled;
- }
- catch (Throwable t) {
+ } catch (Throwable t) {
return null;
}
}
@@ -39,14 +45,13 @@ public static boolean isGooglePlayServicesAvailable(Context context) {
Integer isGooglePlayServicesAvailableStatusCode = (Integer) invokeStaticMethod(
"com.google.android.gms.common.GooglePlayServicesUtil",
"isGooglePlayServicesAvailable",
- new Class[] {Context.class}, context
+ new Class[]{Context.class}, context
);
boolean isGooglePlayServicesAvailable = (Boolean) isConnectionResultSuccess(isGooglePlayServicesAvailableStatusCode);
return isGooglePlayServicesAvailable;
- }
- catch (Throwable t) {
+ } catch (Throwable t) {
return false;
}
}
@@ -56,12 +61,11 @@ public static String getMacAddress(Context context) {
String macSha1 = (String) invokeStaticMethod(
"com.adjust.sdk.plugin.MacAddressUtil",
"getMacAddress",
- new Class[] {Context.class}, context
+ new Class[]{Context.class}, context
);
return macSha1;
- }
- catch (Throwable t) {
+ } catch (Throwable t) {
return null;
}
}
@@ -69,11 +73,21 @@ public static String getMacAddress(Context context) {
public static String getAndroidId(Context context) {
try {
String androidId = (String) invokeStaticMethod("com.adjust.sdk.plugin.AndroidIdUtil", "getAndroidId"
- ,new Class[] {Context.class}, context);
+ , new Class[]{Context.class}, context);
return androidId;
+ } catch (Throwable t) {
+ return null;
}
- catch (Throwable t) {
+ }
+
+ public static String getSha1EmailAddress(Context context, String key) {
+ try {
+ String sha1EmailAddress = (String) invokeStaticMethod("com.adjust.sdk.plugin.EmailUtil", "getSha1EmailAddress"
+ , new Class[]{Context.class, String.class}, context, key);
+
+ return sha1EmailAddress;
+ } catch (Throwable t) {
return null;
}
}
@@ -82,7 +96,7 @@ private static Object getAdvertisingInfoObject(Context context)
throws Exception {
return invokeStaticMethod("com.google.android.gms.ads.identifier.AdvertisingIdClient",
"getAdvertisingIdInfo",
- new Class[] {Context.class} , context
+ new Class[]{Context.class}, context
);
}
@@ -99,8 +113,7 @@ private static boolean isConnectionResultSuccess(Integer statusCode) {
int successStatusCode = SuccessField.getInt(null);
return successStatusCode == statusCode;
- }
- catch (Throwable t) {
+ } catch (Throwable t) {
return false;
}
}
@@ -124,7 +137,7 @@ public static Object createDefaultInstance(Class classObject) {
try {
Object instance = classObject.newInstance();
return instance;
- }catch (Throwable t) {
+ } catch (Throwable t) {
return null;
}
}
@@ -132,10 +145,11 @@ public static Object createDefaultInstance(Class classObject) {
public static Object createInstance(String className, Class[] cArgs, Object... args) {
try {
Class classObject = Class.forName(className);
+ @SuppressWarnings("unchecked")
Constructor constructor = classObject.getConstructor(cArgs);
Object instance = constructor.newInstance(args);
return instance;
- }catch (Throwable t) {
+ } catch (Throwable t) {
return null;
}
}
@@ -156,10 +170,41 @@ public static Object invokeInstanceMethod(Object instance, String methodName, Cl
public static Object invokeMethod(Class classObject, String methodName, Object instance, Class[] cArgs, Object... args)
throws Exception {
+ @SuppressWarnings("unchecked")
Method methodObject = classObject.getMethod(methodName, cArgs);
Object resultObject = methodObject.invoke(instance, args);
return resultObject;
}
+
+ public static Map getPluginKeys(Context context) {
+ Map pluginKeys = new HashMap();
+
+ for (Plugin plugin : getPlugins()) {
+ Map.Entry pluginEntry = plugin.getParameter(context);
+ if (pluginEntry != null) {
+ pluginKeys.put(pluginEntry.getKey(), pluginEntry.getValue());
+ }
+ }
+
+ if (pluginKeys.size() == 0) {
+ return null;
+ } else {
+ return pluginKeys;
+ }
+ }
+
+ private static List getPlugins() {
+ List plugins = new ArrayList(PLUGINS.size());
+
+ for (String pluginName : PLUGINS) {
+ Object pluginObject = Reflection.createDefaultInstance(pluginName);
+ if (pluginObject != null && pluginObject instanceof Plugin) {
+ plugins.add((Plugin) pluginObject);
+ }
+ }
+
+ return plugins;
+ }
}
diff --git a/Adjust/src/com/adjust/sdk/RequestHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/RequestHandler.java
similarity index 64%
rename from Adjust/src/com/adjust/sdk/RequestHandler.java
rename to Adjust/adjust/src/main/java/com/adjust/sdk/RequestHandler.java
index 412009b14..49a46c8de 100644
--- a/Adjust/src/com/adjust/sdk/RequestHandler.java
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/RequestHandler.java
@@ -9,18 +9,12 @@
package com.adjust.sdk;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.lang.ref.WeakReference;
-import java.net.SocketTimeoutException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
@@ -29,24 +23,22 @@
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
import org.json.JSONObject;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.lang.ref.WeakReference;
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
public class RequestHandler extends HandlerThread implements IRequestHandler {
- private static final int CONNECTION_TIMEOUT = Constants.ONE_MINUTE;
- private static final int SOCKET_TIMEOUT = Constants.ONE_MINUTE;
-
private InternalHandler internalHandler;
private IPackageHandler packageHandler;
- private HttpClient httpClient;
- private Logger logger;
+ private HttpClient httpClient;
+ private ILogger logger;
public RequestHandler(IPackageHandler packageHandler) {
super(Constants.LOGTAG, MIN_PRIORITY);
@@ -55,13 +47,18 @@ public RequestHandler(IPackageHandler packageHandler) {
this.logger = AdjustFactory.getLogger();
this.internalHandler = new InternalHandler(getLooper(), this);
- this.packageHandler = packageHandler;
+ init(packageHandler);
Message message = Message.obtain();
message.arg1 = InternalHandler.INIT;
internalHandler.sendMessage(message);
}
+ @Override
+ public void init(IPackageHandler packageHandler) {
+ this.packageHandler = packageHandler;
+ }
+
@Override
public void sendPackage(ActivityPackage pack) {
Message message = Message.obtain();
@@ -70,9 +67,19 @@ public void sendPackage(ActivityPackage pack) {
internalHandler.sendMessage(message);
}
+ @Override
+ public void sendClickPackage(ActivityPackage clickPackage) {
+ Message message = Message.obtain();
+ message.arg1 = InternalHandler.SEND_CLICK;
+ message.obj = clickPackage;
+ internalHandler.sendMessage(message);
+
+ }
+
private static final class InternalHandler extends Handler {
private static final int INIT = 72401;
private static final int SEND = 72400;
+ private static final int SEND_CLICK = 72402;
private final WeakReference requestHandlerReference;
@@ -96,90 +103,75 @@ public void handleMessage(Message message) {
break;
case SEND:
ActivityPackage activityPackage = (ActivityPackage) message.obj;
- requestHandler.sendInternal(activityPackage);
+ requestHandler.sendInternal(activityPackage, true);
+ break;
+ case SEND_CLICK:
+ ActivityPackage clickPackage = (ActivityPackage) message.obj;
+ requestHandler.sendInternal(clickPackage, false);
break;
}
}
}
private void initInternal() {
- HttpParams httpParams = new BasicHttpParams();
- HttpConnectionParams.setConnectionTimeout(httpParams, CONNECTION_TIMEOUT);
- HttpConnectionParams.setSoTimeout(httpParams, SOCKET_TIMEOUT);
- httpClient = AdjustFactory.getHttpClient(httpParams);
+ httpClient = Util.getHttpClient();
}
- private void sendInternal(ActivityPackage activityPackage) {
+ private void sendInternal(ActivityPackage activityPackage, boolean sendToPackageHandler) {
try {
HttpUriRequest request = getRequest(activityPackage);
HttpResponse response = httpClient.execute(request);
- requestFinished(response, activityPackage);
+ requestFinished(response, sendToPackageHandler);
} catch (UnsupportedEncodingException e) {
- sendNextPackage(activityPackage, "Failed to encode parameters", e);
+ sendNextPackage(activityPackage, "Failed to encode parameters", e, sendToPackageHandler);
} catch (ClientProtocolException e) {
- closePackage(activityPackage, "Client protocol error", e);
+ closePackage(activityPackage, "Client protocol error", e, sendToPackageHandler);
} catch (SocketTimeoutException e) {
- closePackage(activityPackage, "Request timed out", e);
+ closePackage(activityPackage, "Request timed out", e, sendToPackageHandler);
} catch (IOException e) {
- closePackage(activityPackage, "Request failed", e);
+ closePackage(activityPackage, "Request failed", e, sendToPackageHandler);
} catch (Throwable e) {
- sendNextPackage(activityPackage, "Runtime exception", e);
+ sendNextPackage(activityPackage, "Runtime exception", e, sendToPackageHandler);
}
}
- private void requestFinished(HttpResponse response, ActivityPackage activityPackage) {
- int statusCode = response.getStatusLine().getStatusCode();
- String responseString = parseResponse(response);
- JSONObject jsonResponse = Util.buildJsonObject(responseString);
- ResponseData responseData = ResponseData.fromJson(jsonResponse, responseString);
+ private void requestFinished(HttpResponse response, boolean sendToPackageHandler) {
+ JSONObject jsonResponse = Util.parseJsonResponse(response, logger);
- if (HttpStatus.SC_OK == statusCode) {
- // success
- responseData.setWasSuccess(true);
- logger.info(activityPackage.getSuccessMessage());
- } else {
- // wrong status code
- logger.error("%s. (%s)", activityPackage.getFailureMessage(), responseData.getError());
+ if (jsonResponse == null) {
+ if (sendToPackageHandler) {
+ packageHandler.closeFirstPackage();
+ }
+ return;
}
- packageHandler.finishedTrackingActivity(activityPackage, responseData, jsonResponse);
- packageHandler.sendNextPackage();
- }
-
- private String parseResponse(HttpResponse response) {
- try {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- response.getEntity().writeTo(out);
- out.close();
- return out.toString().trim();
- } catch (Exception e) {
- logger.error("Failed to parse response (%s)", e);
- return "Failed to parse response";
+ packageHandler.finishedTrackingActivity(jsonResponse);
+ if (sendToPackageHandler) {
+ packageHandler.sendNextPackage();
}
}
// close current package because it failed
- private void closePackage(ActivityPackage activityPackage, String message, Throwable throwable) {
+ private void closePackage(ActivityPackage activityPackage, String message, Throwable throwable, boolean sendToPackageHandler) {
final String packageMessage = activityPackage.getFailureMessage();
final String handlerMessage = packageHandler.getFailureMessage();
final String reasonString = getReasonString(message, throwable);
logger.error("%s. (%s) %s", packageMessage, reasonString, handlerMessage);
- ResponseData responseData = ResponseData.fromError(reasonString);
- responseData.setWillRetry(!packageHandler.dropsOfflineActivities());
- packageHandler.finishedTrackingActivity(activityPackage, responseData, null);
- packageHandler.closeFirstPackage();
+ if (sendToPackageHandler) {
+ packageHandler.closeFirstPackage();
+ }
}
// send next package because the current package failed
- private void sendNextPackage(ActivityPackage activityPackage, String message, Throwable throwable) {
+ private void sendNextPackage(ActivityPackage activityPackage, String message, Throwable throwable, boolean sendToPackageHandler) {
final String failureMessage = activityPackage.getFailureMessage();
final String reasonString = getReasonString(message, throwable);
logger.error("%s. (%s)", failureMessage, reasonString);
- ResponseData responseData = ResponseData.fromError(reasonString);
- packageHandler.finishedTrackingActivity(activityPackage, responseData, null);
- packageHandler.sendNextPackage();
+ if (sendToPackageHandler) {
+ packageHandler.sendNextPackage();
+ }
}
private String getReasonString(String message, Throwable throwable) {
@@ -195,13 +187,12 @@ private HttpUriRequest getRequest(ActivityPackage activityPackage) throws Unsupp
HttpPost request = new HttpPost(url);
String language = Locale.getDefault().getLanguage();
- request.addHeader("User-Agent", activityPackage.getUserAgent());
request.addHeader("Client-SDK", activityPackage.getClientSdk());
request.addHeader("Accept-Language", language);
List pairs = new ArrayList();
- for (Map.Entry entity : activityPackage.getParameters().entrySet()) {
- NameValuePair pair = new BasicNameValuePair(entity.getKey(), entity.getValue());
+ for (Map.Entry entry : activityPackage.getParameters().entrySet()) {
+ NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue());
pairs.add(pair);
}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/UnitTestActivity.java b/Adjust/adjust/src/main/java/com/adjust/sdk/UnitTestActivity.java
new file mode 100644
index 000000000..799fb8982
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/UnitTestActivity.java
@@ -0,0 +1,38 @@
+package com.adjust.sdk;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+
+public class UnitTestActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ //setContentView(com.adjust.sdk.test.R.layout.activity_unit_test);
+ }
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ //getMenuInflater().inflate(com.adjust.sdk.test.R.menu.menu_unit_test, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+/* int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == com.adjust.sdk.test.R.id.action_settings) {
+ return true;
+ }
+*/
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/Util.java b/Adjust/adjust/src/main/java/com/adjust/sdk/Util.java
new file mode 100644
index 000000000..a893d09f9
--- /dev/null
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/Util.java
@@ -0,0 +1,202 @@
+//
+// Util.java
+// Adjust
+//
+// Created by Christian Wellenbrock on 2012-10-11.
+// Copyright (c) 2012-2014 adjust GmbH. All rights reserved.
+// See the file MIT-LICENSE for copying permission.
+//
+
+package com.adjust.sdk;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OptionalDataException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Collects utility functions used by Adjust.
+ */
+public class Util {
+
+ private static SimpleDateFormat dateFormat;
+ private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'Z";
+
+ protected static String createUuid() {
+ return UUID.randomUUID().toString();
+ }
+
+ public static String quote(String string) {
+ if (string == null) {
+ return null;
+ }
+
+ Pattern pattern = Pattern.compile("\\s");
+ Matcher matcher = pattern.matcher(string);
+ if (!matcher.find()) {
+ return string;
+ }
+
+ return String.format("'%s'", string);
+ }
+
+ public static String dateFormat(long date) {
+ if (null == dateFormat) {
+ dateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.US);
+ }
+ return dateFormat.format(date);
+ }
+
+ public static String getPlayAdId(Context context) {
+ return Reflection.getPlayAdId(context);
+ }
+
+ public static Boolean isPlayTrackingEnabled(Context context) {
+ return Reflection.isPlayTrackingEnabled(context);
+ }
+
+ public static T readObject(Context context, String filename, String objectName) {
+ ILogger logger = AdjustFactory.getLogger();
+ try {
+ FileInputStream inputStream = context.openFileInput(filename);
+ BufferedInputStream bufferedStream = new BufferedInputStream(inputStream);
+ ObjectInputStream objectStream = new ObjectInputStream(bufferedStream);
+
+ try {
+ @SuppressWarnings("unchecked")
+ T t = (T) objectStream.readObject();
+ logger.debug("Read %s: %s", objectName, t);
+ return t;
+ } catch (ClassNotFoundException e) {
+ logger.error("Failed to find %s class", objectName);
+ } catch (OptionalDataException e) {
+ /* no-op */
+ } catch (IOException e) {
+ logger.error("Failed to read %s object", objectName);
+ } catch (ClassCastException e) {
+ logger.error("Failed to cast %s object", objectName);
+ } finally {
+ objectStream.close();
+ }
+
+ } catch (FileNotFoundException e) {
+ logger.verbose("%s file not found", objectName);
+ } catch (Exception e) {
+ logger.error("Failed to open %s file for reading (%s)", objectName, e);
+ }
+
+ return null;
+ }
+
+ public static void writeObject(T object, Context context, String filename, String objectName) {
+ ILogger logger = AdjustFactory.getLogger();
+ try {
+ FileOutputStream outputStream = context.openFileOutput(filename, Context.MODE_PRIVATE);
+ BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream);
+ ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream);
+
+ try {
+ objectStream.writeObject(object);
+ logger.debug("Wrote %s: %s", objectName, object);
+ } catch (NotSerializableException e) {
+ logger.error("Failed to serialize %s", objectName);
+ } finally {
+ objectStream.close();
+ }
+
+ } catch (Exception e) {
+ logger.error("Failed to open %s for writing (%s)", objectName, e);
+ }
+ }
+
+ public static String parseResponse(HttpResponse httpResponse, ILogger logger) {
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ httpResponse.getEntity().writeTo(out);
+ out.close();
+ String response = out.toString().trim();
+ logger.verbose("Response: %s", response);
+ return response;
+ } catch (Exception e) {
+ logger.error("Failed to parse response (%s)", e);
+ return null;
+ }
+ }
+
+ public static JSONObject parseJsonResponse(HttpResponse httpResponse, ILogger logger) {
+ if (httpResponse == null) {
+ return null;
+ }
+ String stringResponse = null;
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ httpResponse.getEntity().writeTo(out);
+ out.close();
+ stringResponse = out.toString().trim();
+ } catch (Exception e) {
+ logger.error("Failed to parse response (%s)", e.getMessage());
+ }
+
+ logger.verbose("Response: %s", stringResponse);
+ if (stringResponse == null) return null;
+
+ JSONObject jsonResponse = null;
+ try {
+ jsonResponse = new JSONObject(stringResponse);
+ } catch (JSONException e) {
+ logger.error("Failed to parse json response: %s (%s)", stringResponse, e.getMessage());
+ }
+
+ if (jsonResponse == null) return null;
+
+ String message = jsonResponse.optString("message", null);
+
+ if (message == null) {
+ message = "No message found";
+ }
+
+ if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+ logger.info("%s", message);
+ } else {
+ logger.error("%s", message);
+ }
+
+ return jsonResponse;
+ }
+
+ public static HttpClient getHttpClient() {
+ HttpParams httpParams = new BasicHttpParams();
+ HttpConnectionParams.setConnectionTimeout(httpParams, Constants.CONNECTION_TIMEOUT);
+ HttpConnectionParams.setSoTimeout(httpParams, Constants.SOCKET_TIMEOUT);
+ return AdjustFactory.getHttpClient(httpParams);
+ }
+
+ public static boolean checkPermission(Context context, String permission) {
+ int result = context.checkCallingOrSelfPermission(permission);
+ return result == PackageManager.PERMISSION_GRANTED;
+ }
+}
diff --git a/Adjust/src/com/adjust/sdk/plugin/AndroidIdUtil.java b/Adjust/adjust/src/main/java/com/adjust/sdk/plugin/AndroidIdUtil.java
similarity index 100%
rename from Adjust/src/com/adjust/sdk/plugin/AndroidIdUtil.java
rename to Adjust/adjust/src/main/java/com/adjust/sdk/plugin/AndroidIdUtil.java
diff --git a/Adjust/src/com/adjust/sdk/plugin/MacAddressUtil.java b/Adjust/adjust/src/main/java/com/adjust/sdk/plugin/MacAddressUtil.java
similarity index 100%
rename from Adjust/src/com/adjust/sdk/plugin/MacAddressUtil.java
rename to Adjust/adjust/src/main/java/com/adjust/sdk/plugin/MacAddressUtil.java
index 13b24901a..c8bdbadd7 100644
--- a/Adjust/src/com/adjust/sdk/plugin/MacAddressUtil.java
+++ b/Adjust/adjust/src/main/java/com/adjust/sdk/plugin/MacAddressUtil.java
@@ -1,14 +1,14 @@
package com.adjust.sdk.plugin;
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.text.TextUtils;
+
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Locale;
-import android.content.Context;
-import android.net.wifi.WifiManager;
-import android.text.TextUtils;
-
public class MacAddressUtil {
public static String getMacAddress(Context context) {
final String rawAddress = getRawMacAddress(context);
diff --git a/Adjust/src/com/adjust/sdk/plugin/Plugin.java b/Adjust/adjust/src/main/java/com/adjust/sdk/plugin/Plugin.java
similarity index 100%
rename from Adjust/src/com/adjust/sdk/plugin/Plugin.java
rename to Adjust/adjust/src/main/java/com/adjust/sdk/plugin/Plugin.java
diff --git a/Adjust/adjust/src/main/res/values/strings.xml b/Adjust/adjust/src/main/res/values/strings.xml
new file mode 100644
index 000000000..5cf65ffdb
--- /dev/null
+++ b/Adjust/adjust/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ adjust
+
diff --git a/Adjust/build.gradle b/Adjust/build.gradle
index 657d85399..741f70e77 100644
--- a/Adjust/build.gradle
+++ b/Adjust/build.gradle
@@ -1,48 +1,19 @@
-apply plugin: 'android-library'
-buildscript {
- repositories {
- mavenCentral()
- mavenLocal()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:+'
- }
-}
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
-repositories {
- mavenCentral()
- mavenLocal()
-}
-
-dependencies {
- compile 'com.android.support:support-v4:19.1.+'
-}
-
-android {
- buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
- compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)
- defaultConfig {
- versionCode 11
- versionName '3.6.2'
- minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION)
- targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION)
- }
- sourceSets {
- main {
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['src']
- resources.srcDirs = ['src']
- res.srcDirs = ['res']
- assets.srcDirs = ['assets']
+buildscript {
+ repositories {
+ jcenter()
}
- }
-}
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.0.1'
-task jar(type: Jar) {
- from android.sourceSets.main.java
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
}
-task wrapper(type: Wrapper) {
- gradleVersion = '1.8'
+allprojects {
+ repositories {
+ jcenter()
+ }
}
diff --git a/Adjust/build.xml b/Adjust/build.xml
deleted file mode 100644
index 507923cb3..000000000
--- a/Adjust/build.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Adjust/example/.gitignore b/Adjust/example/.gitignore
new file mode 100644
index 000000000..796b96d1c
--- /dev/null
+++ b/Adjust/example/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Adjust/example/build.gradle b/Adjust/example/build.gradle
new file mode 100644
index 000000000..e59d6f944
--- /dev/null
+++ b/Adjust/example/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion "21.1.2"
+
+ defaultConfig {
+ applicationId "com.adjust.example"
+ minSdkVersion 9
+ targetSdkVersion 21
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile 'com.android.support:appcompat-v7:21.0.3'
+ compile 'com.google.android.gms:play-services-ads:6.5.87'
+ // imported module
+ compile project(":adjust")
+ // running mvn package
+ //compile fileTree(dir: '../target', include: ['*.jar'])
+ // using maven repository
+ //compile 'com.adjust.sdk:adjust-android:4.0.0'
+}
diff --git a/Adjust/test/proguard-project.txt b/Adjust/example/proguard-rules.pro
similarity index 62%
rename from Adjust/test/proguard-project.txt
rename to Adjust/example/proguard-rules.pro
index f2fe1559a..b88e5ae52 100644
--- a/Adjust/test/proguard-project.txt
+++ b/Adjust/example/proguard-rules.pro
@@ -1,11 +1,8 @@
-# To enable ProGuard in your project, edit project.properties
-# to define the proguard.config property as described in that file.
-#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
-# in ${sdk.dir}/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the ProGuard
-# include property in project.properties.
+# in /Users/pfms/Development/Android_SDK/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
diff --git a/Adjust/example/src/androidTest/java/com/adjust/example/ApplicationTest.java b/Adjust/example/src/androidTest/java/com/adjust/example/ApplicationTest.java
new file mode 100644
index 000000000..5d4139ba0
--- /dev/null
+++ b/Adjust/example/src/androidTest/java/com/adjust/example/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.adjust.example;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/Adjust/example/src/main/AndroidManifest.xml b/Adjust/example/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..9b12f001c
--- /dev/null
+++ b/Adjust/example/src/main/AndroidManifest.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Adjust/example/src/main/java/com/adjust/example/GlobalApplication.java b/Adjust/example/src/main/java/com/adjust/example/GlobalApplication.java
new file mode 100644
index 000000000..2c83a2480
--- /dev/null
+++ b/Adjust/example/src/main/java/com/adjust/example/GlobalApplication.java
@@ -0,0 +1,96 @@
+package com.adjust.example;
+
+import android.app.Application;
+import android.util.Log;
+
+import com.adjust.sdk.Adjust;
+import com.adjust.sdk.AdjustAttribution;
+import com.adjust.sdk.AdjustConfig;
+import com.adjust.sdk.LogLevel;
+import com.adjust.sdk.OnAttributionChangedListener;
+
+/**
+ * Created by pfms on 17/12/14.
+ */
+public class GlobalApplication extends Application {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ // configure Adjust
+ String appToken = "{yourAppToken}";
+ String environment = AdjustConfig.ENVIRONMENT_SANDBOX;
+ AdjustConfig config = new AdjustConfig(this, appToken, environment);
+
+ // change the log level
+ config.setLogLevel(LogLevel.VERBOSE);
+
+ // enable event buffering
+ //config.setEventBufferingEnabled(true);
+
+ // set default tracker
+ //config.setDefaultTracker("{YourDefaultTracker}");
+
+ // set attribution delegate
+ config.setOnAttributionChangedListener(new OnAttributionChangedListener() {
+ @Override
+ public void onAttributionChanged(AdjustAttribution attribution) {
+ Log.d("example", "attribution: " + attribution.toString());
+ }
+ });
+
+ Adjust.onCreate(config);
+
+
+ // register onResume and onPause events of all activities
+ // for applications with minimum support of Android v4 or greater
+ //registerActivityLifecycleCallbacks(new AdjustLifecycleCallbacks());
+
+ // put the SDK in offline mode
+ //Adjust.setOfflineMode(true);
+
+ // disable the SDK
+ //Adjust.setEnabled(false);
+ }
+
+ // you can use this class if your app is for Android 4.0 or higher
+ /*
+ private static final class AdjustLifecycleCallbacks implements ActivityLifecycleCallbacks {
+ @Override
+ public void onActivityResumed(Activity activity) {
+ Adjust.onResume();
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ Adjust.onPause();
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+
+ }
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+
+ }
+ }
+ */
+
+
+}
diff --git a/Adjust/example/src/main/java/com/adjust/example/MainActivity.java b/Adjust/example/src/main/java/com/adjust/example/MainActivity.java
new file mode 100644
index 000000000..02c32c2e8
--- /dev/null
+++ b/Adjust/example/src/main/java/com/adjust/example/MainActivity.java
@@ -0,0 +1,93 @@
+package com.adjust.example;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.adjust.sdk.Adjust;
+import com.adjust.sdk.AdjustEvent;
+
+public class MainActivity extends ActionBarActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ Intent intent = getIntent();
+ Uri data = intent.getData();
+ Adjust.appWillOpenUrl(data);
+ }
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.action_settings) {
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ Adjust.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ Adjust.onPause();
+ }
+
+ public void onTrackSimpleEventClick(View v) {
+ AdjustEvent event = new AdjustEvent("{eventToken}");
+
+ Adjust.trackEvent(event);
+ }
+
+ public void onTrackRevenueEventClick(View v) {
+ AdjustEvent event = new AdjustEvent("{eventToken}");
+
+ // add revenue 1 cent of an euro
+ event.setRevenue(0.01, "EUR");
+
+ Adjust.trackEvent(event);
+ }
+
+ public void onTrackEventWithCallbackClick(View v) {
+ AdjustEvent event = new AdjustEvent("{eventToken}");
+
+ // add callback parameters to this parameter
+ event.addCallbackParameter("key", "value");
+
+ Adjust.trackEvent(event);
+ }
+
+ public void onTrackEventWithPartnerClick(View v) {
+ AdjustEvent event = new AdjustEvent("{eventToken}");
+
+ // add partner parameters to this parameter
+ event.addPartnerParameter("foo", "bar");
+
+ Adjust.trackEvent(event);
+ }
+}
diff --git a/Adjust/example/src/main/res/layout/activity_main.xml b/Adjust/example/src/main/res/layout/activity_main.xml
new file mode 100644
index 000000000..ecab379a0
--- /dev/null
+++ b/Adjust/example/src/main/res/layout/activity_main.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Adjust/example/src/main/res/menu/menu_main.xml b/Adjust/example/src/main/res/menu/menu_main.xml
new file mode 100644
index 000000000..36428ecce
--- /dev/null
+++ b/Adjust/example/src/main/res/menu/menu_main.xml
@@ -0,0 +1,10 @@
+
diff --git a/Adjust/example/src/main/res/mipmap-hdpi/ic_launcher.png b/Adjust/example/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..cde69bccc
Binary files /dev/null and b/Adjust/example/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Adjust/example/src/main/res/mipmap-mdpi/ic_launcher.png b/Adjust/example/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..c133a0cbd
Binary files /dev/null and b/Adjust/example/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Adjust/example/src/main/res/mipmap-xhdpi/ic_launcher.png b/Adjust/example/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..bfa42f0e7
Binary files /dev/null and b/Adjust/example/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Adjust/example/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Adjust/example/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..324e72cdd
Binary files /dev/null and b/Adjust/example/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Adjust/example/src/main/res/values-w820dp/dimens.xml b/Adjust/example/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 000000000..63fc81644
--- /dev/null
+++ b/Adjust/example/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Adjust/test/res/values/dimens.xml b/Adjust/example/src/main/res/values/dimens.xml
similarity index 99%
rename from Adjust/test/res/values/dimens.xml
rename to Adjust/example/src/main/res/values/dimens.xml
index 55c1e5908..47c822467 100644
--- a/Adjust/test/res/values/dimens.xml
+++ b/Adjust/example/src/main/res/values/dimens.xml
@@ -1,7 +1,5 @@
-
16dp
16dp
-
diff --git a/Adjust/example/src/main/res/values/strings.xml b/Adjust/example/src/main/res/values/strings.xml
new file mode 100644
index 000000000..9466ba4ce
--- /dev/null
+++ b/Adjust/example/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ example
+
+ Settings
+
diff --git a/Adjust/example/src/main/res/values/styles.xml b/Adjust/example/src/main/res/values/styles.xml
new file mode 100644
index 000000000..766ab9930
--- /dev/null
+++ b/Adjust/example/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/Adjust/gradle.properties b/Adjust/gradle.properties
index 2f5d3629e..1d3591c8a 100644
--- a/Adjust/gradle.properties
+++ b/Adjust/gradle.properties
@@ -1,4 +1,18 @@
-ANDROID_BUILD_MIN_SDK_VERSION=8
-ANDROID_BUILD_TARGET_SDK_VERSION=21
-ANDROID_BUILD_TOOLS_VERSION=21.1.1
-ANDROID_BUILD_SDK_VERSION=21
\ No newline at end of file
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/Adjust/plugin/AdjustCriteo.java b/Adjust/plugin/AdjustCriteo.java
new file mode 100644
index 000000000..4fdb00130
--- /dev/null
+++ b/Adjust/plugin/AdjustCriteo.java
@@ -0,0 +1,126 @@
+package com.adjust.sdk.plugin;
+
+import com.adjust.sdk.AdjustEvent;
+import com.adjust.sdk.AdjustFactory;
+import com.adjust.sdk.ILogger;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.List;
+
+/**
+ * Created by pfms on 24/02/15.
+ */
+public class AdjustCriteo {
+ private static ILogger logger = AdjustFactory.getLogger();
+ private static int MAX_VIEW_LISTING_PRODUCTS = 3;
+
+ public static void injectViewSearchIntoEvent(AdjustEvent event, String checkInDate, String checkOutDate) {
+ event.addPartnerParameter("din", checkInDate);
+ event.addPartnerParameter("dout", checkOutDate);
+ }
+
+ public static void injectViewListingIntoEvent(AdjustEvent event, List products, String customerId) {
+ String jsonProducts = createCriteoVLFromProducts(products);
+
+ if (jsonProducts == null) {
+ logger.error("Missing products from Criteo View Listing");
+ return;
+ }
+
+ event.addPartnerParameter("customer_id", customerId);
+ event.addPartnerParameter("criteo_p", jsonProducts);
+ }
+
+ public static void injectViewProductIntoEvent(AdjustEvent event, String productId, String customerId) {
+ event.addPartnerParameter("customer_id", customerId);
+ event.addPartnerParameter("criteo_p", productId);
+ }
+
+ public static void injectCartIntoEvent(AdjustEvent event, List products, String customerId) {
+ String jsonProducts = createCriteoVBFromProducts(products);
+ if (jsonProducts == null) {
+ logger.error("Missing products from Criteo Cart");
+ return;
+ }
+
+ event.addPartnerParameter("customer_id", customerId);
+ event.addPartnerParameter("criteo_p", jsonProducts);
+ }
+
+ public static void injectTransactionConfirmedIntoEvent(AdjustEvent event, List products, String customerId) {
+ String jsonProducts = createCriteoVBFromProducts(products);
+ if (jsonProducts == null) {
+ logger.error("Missing products from Criteo Transaction Confirmed");
+ return;
+ }
+
+ event.addPartnerParameter("customer_id", customerId);
+ event.addPartnerParameter("criteo_p", jsonProducts);
+ }
+
+ private static String createCriteoVLFromProducts(List products) {
+ if (products == null) {
+ return null;
+ }
+ StringBuffer criteoVBValue = new StringBuffer("[");
+ int productsSize = products.size();
+
+ if (productsSize > MAX_VIEW_LISTING_PRODUCTS) {
+ logger.warn("View Listing events should only have at most 3 objects, discarding the rest");
+ }
+ for (int i = 0; i < productsSize; ) {
+ CriteoProduct criteoProduct = products.get(i);
+ String productString = String.format("\"%s\"", criteoProduct.productID);
+ criteoVBValue.append(productString);
+
+ i++;
+
+ if (i == productsSize || i >= MAX_VIEW_LISTING_PRODUCTS) {
+ break;
+ }
+
+ criteoVBValue.append(",");
+ }
+ criteoVBValue.append("]");
+ String result = null;
+ try {
+ result = URLEncoder.encode(criteoVBValue.toString(),"UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ logger.error("error converting criteo products (%s)", e.getMessage());
+ }
+ return result;
+ }
+
+ private static String createCriteoVBFromProducts(List products) {
+ if (logger == null) {
+ return null;
+ }
+ StringBuffer criteoVBValue = new StringBuffer("[");
+ int productsSize = products.size();
+ for (int i = 0; i < productsSize; ) {
+ CriteoProduct criteoProduct = products.get(i);
+ String productString = String.format("{\"i\":\"%s,\"pr\":%f,\"q\":%d}",
+ criteoProduct.productID,
+ criteoProduct.price,
+ criteoProduct.quantity);
+ criteoVBValue.append(productString);
+
+ i++;
+
+ if (i == productsSize) {
+ break;
+ }
+
+ criteoVBValue.append(",");
+ }
+ criteoVBValue.append("]");
+ String result = null;
+ try {
+ result = URLEncoder.encode(criteoVBValue.toString(),"UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ logger.error("error converting criteo products (%s)", e.getMessage());
+ }
+ return result;
+ }
+}
diff --git a/Adjust/plugin/CriteoProduct.java b/Adjust/plugin/CriteoProduct.java
new file mode 100644
index 000000000..d85746c03
--- /dev/null
+++ b/Adjust/plugin/CriteoProduct.java
@@ -0,0 +1,16 @@
+package com.adjust.sdk.plugin;
+
+/**
+ * Created by pfms on 24/02/15.
+ */
+public class CriteoProduct {
+ float price;
+ int quantity;
+ String productID;
+
+ public CriteoProduct(float price, int quantity, String productID) {
+ this.price = price;
+ this.quantity = quantity;
+ this.productID = productID;
+ }
+}
diff --git a/Adjust/pom.xml b/Adjust/pom.xml
index 23f66e441..1638485f4 100644
--- a/Adjust/pom.xml
+++ b/Adjust/pom.xml
@@ -1,144 +1,144 @@
- 4.0.0
- adjust-android
- com.adjust.sdk
- 3.6.2
- jar
- Adjust Android SDK
- https://github.com/adjust/android_sdk
- The Adjust SDK for Android
-
-
- MIT License
- http://www.opensource.org/licenses/mit-license.php
-
-
-
-
- Pedro Silva
- pedro@adjust.com
- adjust GmbH
- http://www.adjust.com
-
-
-
- scm:git:git@github.com:adjust/android_sdk.git
- scm:git:git@github.com:adjust/android_sdk.git
- git@github.com:adjust/android_sdk.git
-
-
- UTF-8
- 4.1.1.4
- 16
-
-
-
- android
- ${android.version}
- com.google.android
- provided
-
-
-
-
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
-
- src
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 2.5.1
-
- 1.6
- 1.6
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
- 2.2.1
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 2.9.1
-
-
- attach-javadocs
-
- jar
-
-
-
-
-
- com.jayway.maven.plugins.android.generation2
- android-maven-plugin
- 3.5.3
- true
-
-
- ${android.version.platform}
-
-
-
-
- maven-deploy-plugin
- 2.8.1
-
-
- default-deploy
- deploy
-
- deploy
-
-
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- 1.5
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
-
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.2
- true
-
- ossrh
- https://oss.sonatype.org/
- false
-
-
-
-
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ 4.0.0
+ adjust-android
+ com.adjust.sdk
+ 4.0.0
+ jar
+ Adjust Android SDK
+ https://github.com/adjust/android_sdk
+ The Adjust SDK for Android
+
+
+ MIT License
+ http://www.opensource.org/licenses/mit-license.php
+
+
+
+
+ Pedro Silva
+ pedro@adjust.com
+ adjust GmbH
+ http://www.adjust.com
+
+
+
+ scm:git:git@github.com:adjust/android_sdk.git
+ scm:git:git@github.com:adjust/android_sdk.git
+ git@github.com:adjust/android_sdk.git
+
+
+ UTF-8
+ 4.1.1.4
+ 16
+
+
+
+ android
+ ${android.version}
+ com.google.android
+ provided
+
+
+
+
+ ossrh
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+ ossrh
+ https://oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
+ adjust/src/main/java
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.5.1
+
+ 1.6
+ 1.6
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 2.2.1
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.9.1
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ com.jayway.maven.plugins.android.generation2
+ android-maven-plugin
+ 3.5.3
+ true
+
+
+ ${android.version.platform}
+
+
+
+
+ maven-deploy-plugin
+ 2.8.1
+
+
+ default-deploy
+ deploy
+
+ deploy
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.5
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.2
+ true
+
+ ossrh
+ https://oss.sonatype.org/
+ false
+
+
+
+
diff --git a/Adjust/project.properties b/Adjust/project.properties
deleted file mode 100644
index db721fd89..000000000
--- a/Adjust/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-8
-android.library=true
diff --git a/Adjust/settings.gradle b/Adjust/settings.gradle
new file mode 100644
index 000000000..ab8c3e3e4
--- /dev/null
+++ b/Adjust/settings.gradle
@@ -0,0 +1 @@
+include ':adjust', ':test', ':example'
diff --git a/Adjust/src/com/adjust/sdk/ActivityHandler.java b/Adjust/src/com/adjust/sdk/ActivityHandler.java
deleted file mode 100644
index 9904d6b0b..000000000
--- a/Adjust/src/com/adjust/sdk/ActivityHandler.java
+++ /dev/null
@@ -1,786 +0,0 @@
-//
-// ActivityHandler.java
-// Adjust
-//
-// Created by Christian Wellenbrock on 2013-06-25.
-// Copyright (c) 2013 adjust GmbH. All rights reserved.
-// See the file MIT-LICENSE for copying permission.
-//
-
-package com.adjust.sdk;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.preference.PreferenceManager;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.NotSerializableException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.OptionalDataException;
-import java.lang.ref.WeakReference;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import static com.adjust.sdk.Constants.LOGTAG;
-import static com.adjust.sdk.Constants.SESSION_STATE_FILENAME;
-import static com.adjust.sdk.Constants.UNKNOWN;
-
-public class ActivityHandler extends HandlerThread {
-
- private static long TIMER_INTERVAL;
- private static long SESSION_INTERVAL;
- private static long SUBSESSION_INTERVAL;
- private static final String TIME_TRAVEL = "Time travel!";
- private static final String ADJUST_PREFIX = "adjust_";
-
- private SessionHandler sessionHandler;
- private IPackageHandler packageHandler;
- private OnFinishedListener onFinishedListener;
- private ActivityState activityState;
- private Logger logger;
- private static ScheduledExecutorService timer;
- private Context context;
- private String environment;
- private String defaultTracker;
- private boolean eventBuffering;
- private boolean dropOfflineActivities;
- private boolean enabled;
-
- private String appToken;
- private String macSha1;
- private String macShortMd5;
- private String androidId; // everything else here could be persisted
- private String fbAttributionId;
- private String userAgent; // changes, should be updated periodically
- private String clientSdk;
- private Map pluginKeys;
-
- public ActivityHandler(Context context) {
- super(LOGTAG, MIN_PRIORITY);
-
- initActivityHandler(context);
-
- Message message = Message.obtain();
- message.arg1 = SessionHandler.INIT_BUNDLE;
- sessionHandler.sendMessage(message);
- }
-
- public ActivityHandler(Context context, String appToken,
- String environment, String logLevel, boolean eventBuffering) {
- super(LOGTAG, MIN_PRIORITY);
-
- initActivityHandler(context);
-
- this.environment = environment;
- this.eventBuffering = eventBuffering;
- logger.setLogLevelString(logLevel);
-
- Message message = Message.obtain();
- message.arg1 = SessionHandler.INIT_PRESET;
- message.obj = appToken;
- sessionHandler.sendMessage(message);
- }
-
- private void initActivityHandler(Context context) {
- setDaemon(true);
- start();
-
- TIMER_INTERVAL = AdjustFactory.getTimerInterval();
- SESSION_INTERVAL = AdjustFactory.getSessionInterval();
- SUBSESSION_INTERVAL = AdjustFactory.getSubsessionInterval();
- sessionHandler = new SessionHandler(getLooper(), this);
- this.context = context.getApplicationContext();
- clientSdk = Constants.CLIENT_SDK;
- pluginKeys = Util.getPluginKeys(this.context);
- enabled = true;
-
- logger = AdjustFactory.getLogger();
- }
-
- public void setSdkPrefix(String sdkPrefx) {
- clientSdk = String.format("%s@%s", sdkPrefx, clientSdk);
- }
-
- public void setOnFinishedListener(OnFinishedListener listener) {
- onFinishedListener = listener;
- }
-
- public void trackSubsessionStart() {
- Message message = Message.obtain();
- message.arg1 = SessionHandler.START;
- sessionHandler.sendMessage(message);
- }
-
- public void trackSubsessionEnd() {
- Message message = Message.obtain();
- message.arg1 = SessionHandler.END;
- sessionHandler.sendMessage(message);
- }
-
- public void trackEvent(String eventToken, Map parameters) {
- PackageBuilder builder = new PackageBuilder(context);
- builder.setEventToken(eventToken);
- builder.setCallbackParameters(parameters);
-
- Message message = Message.obtain();
- message.arg1 = SessionHandler.EVENT;
- message.obj = builder;
- sessionHandler.sendMessage(message);
- }
-
- public void trackRevenue(double amountInCents, String eventToken, Map parameters) {
- PackageBuilder builder = new PackageBuilder(context);
- builder.setAmountInCents(amountInCents);
- builder.setEventToken(eventToken);
- builder.setCallbackParameters(parameters);
-
- Message message = Message.obtain();
- message.arg1 = SessionHandler.REVENUE;
- message.obj = builder;
- sessionHandler.sendMessage(message);
- }
-
- public void finishedTrackingActivity(final ResponseData responseData, final String deepLink) {
- if (onFinishedListener == null && deepLink == null) {
- return;
- }
-
- Handler handler = new Handler(context.getMainLooper());
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- try {
- runDelegateMain(responseData);
- launchDeepLinkMain(deepLink);
- } catch (NullPointerException e) {
- }
- }
- };
- handler.post(runnable);
- }
-
- public void setEnabled(Boolean enabled) {
- this.enabled = enabled;
- if (checkActivityState(activityState))
- activityState.enabled = enabled;
- if (enabled) {
- this.trackSubsessionStart();
- } else {
- this.trackSubsessionEnd();
- }
- }
-
- public Boolean isEnabled() {
- if (checkActivityState(activityState)) {
- return activityState.enabled;
- } else {
- return this.enabled;
- }
- }
-
- public void readOpenUrl(Uri url) {
- Message message = Message.obtain();
- message.arg1 = SessionHandler.DEEP_LINK;
- message.obj = url;
- sessionHandler.sendMessage(message);
- }
-
- private static final class SessionHandler extends Handler {
- private static final int INIT_BUNDLE = 72630;
- private static final int INIT_PRESET = 72633;
- private static final int START = 72640;
- private static final int END = 72650;
- private static final int EVENT = 72660;
- private static final int REVENUE = 72670;
- private static final int DEEP_LINK = 72680;
-
-
- private final WeakReference sessionHandlerReference;
-
- protected SessionHandler(Looper looper, ActivityHandler sessionHandler) {
- super(looper);
- this.sessionHandlerReference = new WeakReference(sessionHandler);
- }
-
- @Override
- public void handleMessage(Message message) {
- super.handleMessage(message);
-
- ActivityHandler sessionHandler = sessionHandlerReference.get();
- if (sessionHandler == null) {
- return;
- }
-
- switch (message.arg1) {
- case INIT_BUNDLE:
- sessionHandler.initInternal(true, null);
- break;
- case INIT_PRESET:
- String appToken = (String) message.obj;
- sessionHandler.initInternal(false, appToken);
- break;
- case START:
- sessionHandler.startInternal();
- break;
- case END:
- sessionHandler.endInternal();
- break;
- case EVENT:
- PackageBuilder eventBuilder = (PackageBuilder) message.obj;
- sessionHandler.trackEventInternal(eventBuilder);
- break;
- case REVENUE:
- PackageBuilder revenueBuilder = (PackageBuilder) message.obj;
- sessionHandler.trackRevenueInternal(revenueBuilder);
- break;
- case DEEP_LINK:
- Uri url = (Uri) message.obj;
- sessionHandler.readOpenUrlInternal(url);
- break;
- }
- }
- }
-
- private void initInternal(boolean fromBundle, String appToken) {
- if (fromBundle) {
- appToken = processApplicationBundle();
- } else {
- setEnvironment(environment);
- setEventBuffering(eventBuffering);
- }
-
- if (!canInit(appToken)) {
- return;
- }
-
- this.appToken = appToken;
- androidId = Util.getAndroidId(context);
- fbAttributionId = Util.getAttributionId(context);
- userAgent = Util.getUserAgent(context);
-
- String playAdId = Util.getPlayAdId(context);
- if (playAdId == null) {
- logger.info("Unable to get Google Play Services Advertising ID at start time");
- }
-
- if (!Util.isGooglePlayServicesAvailable(context)) {
- String macAddress = Util.getMacAddress(context);
- macSha1 = Util.getMacSha1(macAddress);
- macShortMd5 = Util.getMacShortMd5(macAddress);
- }
-
- packageHandler = AdjustFactory.getPackageHandler(this, context, dropOfflineActivities);
-
- readActivityState();
- }
-
- private boolean canInit(String appToken) {
- return checkAppTokenNotNull(appToken)
- && checkAppTokenLength(appToken)
- && checkContext(context)
- && checkPermissions(context);
- }
-
- private void startInternal() {
- if (!checkAppTokenNotNull(appToken)) {
- return;
- }
-
- if (activityState != null
- && !activityState.enabled) {
- return;
- }
-
- packageHandler.resumeSending();
- startTimer();
-
- long now = System.currentTimeMillis();
-
- // very first session
- if (null == activityState) {
- activityState = new ActivityState();
- activityState.sessionCount = 1; // this is the first session
- activityState.createdAt = now; // starting now
-
- transferSessionPackage();
- activityState.resetSessionAttributes(now);
- activityState.enabled = this.enabled;
- writeActivityState();
- logger.info("First session");
- return;
- }
-
- long lastInterval = now - activityState.lastActivity;
-
- if (lastInterval < 0) {
- logger.error(TIME_TRAVEL);
- activityState.lastActivity = now;
- writeActivityState();
- return;
- }
-
- // new session
- if (lastInterval > SESSION_INTERVAL) {
- activityState.sessionCount++;
- activityState.createdAt = now;
- activityState.lastInterval = lastInterval;
-
- transferSessionPackage();
- activityState.resetSessionAttributes(now);
- writeActivityState();
- logger.debug("Session %d", activityState.sessionCount);
- return;
- }
-
- // new subsession
- if (lastInterval > SUBSESSION_INTERVAL) {
- activityState.subsessionCount++;
- logger.info("Started subsession %d of session %d",
- activityState.subsessionCount,
- activityState.sessionCount);
- }
- activityState.sessionLength += lastInterval;
- activityState.lastActivity = now;
- writeActivityState();
- }
-
- private void endInternal() {
- if (!checkAppTokenNotNull(appToken)) {
- return;
- }
-
- packageHandler.pauseSending();
- stopTimer();
- updateActivityState(System.currentTimeMillis());
- writeActivityState();
- }
-
- private void trackEventInternal(PackageBuilder eventBuilder) {
- if (!canTrackEvent(eventBuilder)) {
- return;
- }
-
- if (!activityState.enabled) {
- return;
- }
-
- long now = System.currentTimeMillis();
- activityState.createdAt = now;
- activityState.eventCount++;
- updateActivityState(now);
-
- injectGeneralAttributes(eventBuilder);
- activityState.injectEventAttributes(eventBuilder);
- ActivityPackage eventPackage = eventBuilder.buildEventPackage();
- packageHandler.addPackage(eventPackage);
-
- if (eventBuffering) {
- logger.info("Buffered event %s", eventPackage.getSuffix());
- } else {
- packageHandler.sendFirstPackage();
- }
-
- writeActivityState();
- logger.debug("Event %d", activityState.eventCount);
- }
-
- private void trackRevenueInternal(PackageBuilder revenueBuilder) {
- if (!canTrackRevenue(revenueBuilder)) {
- return;
- }
-
- if (!activityState.enabled) {
- return;
- }
-
- long now = System.currentTimeMillis();
-
- activityState.createdAt = now;
- activityState.eventCount++;
- updateActivityState(now);
-
- injectGeneralAttributes(revenueBuilder);
- activityState.injectEventAttributes(revenueBuilder);
- ActivityPackage eventPackage = revenueBuilder.buildRevenuePackage();
- packageHandler.addPackage(eventPackage);
-
- if (eventBuffering) {
- logger.info("Buffered revenue %s", eventPackage.getSuffix());
- } else {
- packageHandler.sendFirstPackage();
- }
-
- writeActivityState();
- logger.debug("Event %d (revenue)", activityState.eventCount);
- }
-
- private void readOpenUrlInternal(Uri url) {
- if (url == null) {
- return;
- }
-
- String queryString = url.getQuery();
- if (queryString == null) {
- return;
- }
-
- Map adjustDeepLinks = new HashMap();
-
- String[] queryPairs = queryString.split("&");
- for (String pair : queryPairs) {
- String[] pairComponents = pair.split("=");
- if (pairComponents.length != 2) continue;
-
- String key = pairComponents[0];
- if (!key.startsWith(ADJUST_PREFIX)) continue;
-
- String value = pairComponents[1];
- if (value.length() == 0) continue;
-
- String keyWOutPrefix = key.substring(ADJUST_PREFIX.length());
- if (keyWOutPrefix.length() == 0) continue;
-
- adjustDeepLinks.put(keyWOutPrefix, value);
- }
-
- if (adjustDeepLinks.size() == 0) {
- return;
- }
-
- PackageBuilder builder = new PackageBuilder(context);
- builder.setDeepLinkParameters(adjustDeepLinks);
- injectGeneralAttributes(builder);
- ActivityPackage reattributionPackage = builder.buildReattributionPackage();
- packageHandler.addPackage(reattributionPackage);
- packageHandler.sendFirstPackage();
-
- logger.debug("Reattribution %s", adjustDeepLinks.toString());
- }
-
- private void runDelegateMain(ResponseData responseData) {
- if (onFinishedListener == null) return;
- if (responseData == null) return;
- onFinishedListener.onFinishedTracking(responseData);
- }
-
- private void launchDeepLinkMain(String deepLink) {
- if (deepLink == null) return;
-
- Uri location = Uri.parse(deepLink);
- Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
- mapIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- // Verify it resolves
- PackageManager packageManager = context.getPackageManager();
- List activities = packageManager.queryIntentActivities(mapIntent, 0);
- boolean isIntentSafe = activities.size() > 0;
-
- // Start an activity if it's safe
- if (!isIntentSafe) {
- logger.error("Unable to open deep link (%s)", deepLink);
- return;
- }
-
- logger.info("Open deep link (%s)", deepLink);
- context.startActivity(mapIntent);
- }
-
- private boolean canTrackEvent(PackageBuilder revenueBuilder) {
- return checkAppTokenNotNull(appToken)
- && checkActivityState(activityState)
- && revenueBuilder.isValidForEvent();
- }
-
- private boolean canTrackRevenue(PackageBuilder revenueBuilder) {
- return checkAppTokenNotNull(appToken)
- && checkActivityState(activityState)
- && revenueBuilder.isValidForRevenue();
- }
-
- private void updateActivityState(long now) {
- if (!checkActivityState(activityState)) {
- return;
- }
-
- long lastInterval = now - activityState.lastActivity;
- if (lastInterval < 0) {
- logger.error(TIME_TRAVEL);
- activityState.lastActivity = now;
- return;
- }
-
- // ignore late updates
- if (lastInterval > SESSION_INTERVAL) {
- return;
- }
-
- activityState.sessionLength += lastInterval;
- activityState.timeSpent += lastInterval;
- activityState.lastActivity = now;
- }
-
- private void readActivityState() {
- try {
- FileInputStream inputStream = context.openFileInput(SESSION_STATE_FILENAME);
- BufferedInputStream bufferedStream = new BufferedInputStream(inputStream);
- ObjectInputStream objectStream = new ObjectInputStream(bufferedStream);
-
- try {
- activityState = (ActivityState) objectStream.readObject();
- logger.debug("Read activity state: %s uuid:%s", activityState, activityState.uuid);
- return;
- } catch (ClassNotFoundException e) {
- logger.error("Failed to find activity state class");
- } catch (OptionalDataException e) {
- /* no-op */
- } catch (IOException e) {
- logger.error("Failed to read activity states object");
- } catch (ClassCastException e) {
- logger.error("Failed to cast activity state object");
- } finally {
- objectStream.close();
- }
-
- } catch (FileNotFoundException e) {
- logger.verbose("Activity state file not found");
- } catch (Exception e) {
- logger.error("Failed to open activity state file for reading (%s)", e);
- }
-
- // start with a fresh activity state in case of any exception
- activityState = null;
- }
-
- private void writeActivityState() {
- try {
- FileOutputStream outputStream = context.openFileOutput(SESSION_STATE_FILENAME, Context.MODE_PRIVATE);
- BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream);
- ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream);
-
- try {
- objectStream.writeObject(activityState);
- logger.debug("Wrote activity state: %s", activityState);
- } catch (NotSerializableException e) {
- logger.error("Failed to serialize activity state");
- } finally {
- objectStream.close();
- }
-
- } catch (Exception e) {
- logger.error("Failed to open activity state for writing (%s)", e);
- }
- }
-
- public static Boolean deleteActivityState(Context context) {
- return context.deleteFile(SESSION_STATE_FILENAME);
- }
-
- private void transferSessionPackage() {
- PackageBuilder builder = new PackageBuilder(context);
- injectGeneralAttributes(builder);
- injectReferrer(builder);
- activityState.injectSessionAttributes(builder);
- ActivityPackage sessionPackage = builder.buildSessionPackage();
- packageHandler.addPackage(sessionPackage);
- packageHandler.sendFirstPackage();
- }
-
- private void injectGeneralAttributes(PackageBuilder builder) {
- builder.setAppToken(appToken);
- builder.setMacShortMd5(macShortMd5);
- builder.setMacSha1(macSha1);
- builder.setAndroidId(androidId);
- builder.setFbAttributionId(fbAttributionId);
- builder.setUserAgent(userAgent);
- builder.setClientSdk(clientSdk);
- builder.setEnvironment(environment);
- builder.setDefaultTracker(defaultTracker);
- builder.setPluginKeys(pluginKeys);
- }
-
- private void injectReferrer(PackageBuilder builder) {
- try {
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
- builder.setReferrer(preferences.getString(ReferrerReceiver.REFERRER_KEY, null));
- }
- catch (Exception e) {
- logger.error("Failed to inject referrer (%s)", e);
- }
- }
-
- private void startTimer() {
- if (timer != null) {
- stopTimer();
- }
- timer = Executors.newSingleThreadScheduledExecutor();
- timer.scheduleWithFixedDelay(new Runnable() {
- @Override
- public void run() {
- timerFired();
- }
- }, 1000, TIMER_INTERVAL, TimeUnit.MILLISECONDS);
- }
-
- private void stopTimer() {
- try {
- timer.shutdown();
- } catch (NullPointerException e) {
- logger.error("No timer found");
- }
- }
-
- private void timerFired() {
- if (null != activityState
- && !activityState.enabled) {
- return;
- }
-
- packageHandler.sendFirstPackage();
-
- updateActivityState(System.currentTimeMillis());
- writeActivityState();
- }
-
- private boolean checkPermissions(Context context) {
- boolean result = true;
-
- if (!checkPermission(context, android.Manifest.permission.INTERNET)) {
- logger.error("Missing permission: INTERNET");
- result = false;
- }
- if (!checkPermission(context, android.Manifest.permission.ACCESS_WIFI_STATE)) {
- logger.warn("Missing permission: ACCESS_WIFI_STATE");
- }
-
- return result;
- }
-
- private String processApplicationBundle() {
- Bundle bundle = getApplicationBundle();
- if (null == bundle) {
- return null;
- }
-
- String appToken = bundle.getString("AdjustAppToken");
- setEnvironment(bundle.getString("AdjustEnvironment"));
- setDefaultTracker(bundle.getString("AdjustDefaultTracker"));
- setEventBuffering(bundle.getBoolean("AdjustEventBuffering"));
- logger.setLogLevelString(bundle.getString("AdjustLogLevel"));
- setDropOfflineActivities(bundle.getBoolean("AdjustDropOfflineActivities"));
-
- return appToken;
- }
-
- private void setEnvironment(String env) {
- environment = env;
- if (null == environment) {
- logger.Assert("Missing environment");
- logger.setLogLevel(Logger.LogLevel.ASSERT);
- environment = UNKNOWN;
- } else if ("sandbox".equalsIgnoreCase(environment)) {
- logger.Assert(
- "SANDBOX: Adjust is running in Sandbox mode. Use this setting for testing. Don't forget to set the environment to `production` before publishing!");
- } else if ("production".equalsIgnoreCase(environment)) {
- logger.Assert(
- "PRODUCTION: Adjust is running in Production mode. Use this setting only for the build that you want to publish. Set the environment to `sandbox` if you want to test your app!");
- logger.setLogLevel(Logger.LogLevel.ASSERT);
- } else {
- logger.Assert("Malformed environment '%s'", environment);
- logger.setLogLevel(Logger.LogLevel.ASSERT);
- environment = Constants.MALFORMED;
- }
- }
-
- private void setEventBuffering(boolean buffering) {
- eventBuffering = buffering;
- if (eventBuffering) {
- logger.info("Event buffering is enabled");
- }
- }
-
- private void setDefaultTracker(String tracker) {
- defaultTracker = tracker;
- if (defaultTracker != null) {
- logger.info("Default tracker: '%s'", defaultTracker);
- }
- }
-
- private void setDropOfflineActivities(boolean drop) {
- dropOfflineActivities = drop;
- if (dropOfflineActivities) {
- logger.info("Offline activities will get dropped");
- }
- }
-
- private Bundle getApplicationBundle() {
- final ApplicationInfo applicationInfo;
- try {
- String packageName = context.getPackageName();
- applicationInfo = context.getPackageManager().getApplicationInfo(packageName, PackageManager.GET_META_DATA);
- return applicationInfo.metaData;
- } catch (PackageManager.NameNotFoundException e) {
- logger.error("ApplicationInfo not found");
- } catch (Exception e) {
- logger.error("Failed to get ApplicationBundle (%s)", e);
- }
- return null;
- }
-
- private boolean checkContext(Context context) {
- if (null == context) {
- logger.error("Missing context");
- return false;
- }
- return true;
- }
-
- private static boolean checkPermission(Context context, String permission) {
- int result = context.checkCallingOrSelfPermission(permission);
- return result == PackageManager.PERMISSION_GRANTED;
- }
-
- private boolean checkActivityState(ActivityState activityState) {
- if (null == activityState) {
- logger.error("Missing activity state.");
- return false;
- }
- return true;
- }
-
- private boolean checkAppTokenNotNull(String appToken) {
- if (null == appToken) {
- logger.error("Missing App Token.");
- return false;
- }
- return true;
- }
-
- private boolean checkAppTokenLength(String appToken) {
- if (12 != appToken.length()) {
- logger.error("Malformed App Token '%s'", appToken);
- return false;
- }
- return true;
- }
-}
diff --git a/Adjust/src/com/adjust/sdk/ActivityKind.java b/Adjust/src/com/adjust/sdk/ActivityKind.java
deleted file mode 100644
index 32942b616..000000000
--- a/Adjust/src/com/adjust/sdk/ActivityKind.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.adjust.sdk;
-
-public enum ActivityKind {
- UNKNOWN, SESSION, EVENT, REVENUE, REATTRIBUTION;
-
- public static ActivityKind fromString(String string) {
- if ("session".equals(string)) {
- return SESSION;
- } else if ("event".equals(string)) {
- return EVENT;
- } else if ("revenue".equals(string)) {
- return REVENUE;
- } else if ("reattribution".equals(string)) {
- return REATTRIBUTION;
- } else {
- return UNKNOWN;
- }
- }
-
- @Override
- public String toString() {
- switch(this) {
- case SESSION: return "session";
- case EVENT: return "event";
- case REVENUE: return "revenue";
- case REATTRIBUTION: return "reattribution";
- default: return "unknown";
- }
- }
-}
diff --git a/Adjust/src/com/adjust/sdk/ActivityState.java b/Adjust/src/com/adjust/sdk/ActivityState.java
deleted file mode 100644
index 9bf936d50..000000000
--- a/Adjust/src/com/adjust/sdk/ActivityState.java
+++ /dev/null
@@ -1,135 +0,0 @@
-//
-// ActivityState.java
-// Adjust
-//
-// Created by Christian Wellenbrock on 2013-06-25.
-// Copyright (c) 2013 adjust GmbH. All rights reserved.
-// See the file MIT-LICENSE for copying permission.
-//
-
-package com.adjust.sdk;
-
-import java.io.IOException;
-import java.io.NotActiveException;
-import java.io.ObjectInputStream;
-import java.io.ObjectInputStream.GetField;
-import java.io.Serializable;
-import java.util.Date;
-import java.util.Locale;
-
-import android.util.Log;
-
-public class ActivityState implements Serializable {
- private static final long serialVersionUID = 9039439291143138148L;
-
- // persistent data
- protected String uuid;
- protected Boolean enabled;
-
- // global counters
- protected int eventCount;
- protected int sessionCount;
-
- // session attributes
- protected int subsessionCount;
- protected long sessionLength; // all durations in milliseconds
- protected long timeSpent;
- protected long lastActivity; // all times in milliseconds since 1970
-
- protected long createdAt;
- protected long lastInterval;
-
- protected ActivityState() {
- // create UUID for new devices
- uuid = Util.createUuid();
- enabled = true;
-
- eventCount = 0; // no events yet
- sessionCount = 0; // the first session just started
- subsessionCount = -1; // we don't know how many subsessions this first session will have
- sessionLength = -1; // same for session length and time spent
- timeSpent = -1; // this information will be collected and attached to the next session
- lastActivity = -1;
- createdAt = -1;
- lastInterval = -1;
- }
-
- protected void resetSessionAttributes(long now) {
- subsessionCount = 1; // first subsession
- sessionLength = 0; // no session length yet
- timeSpent = 0; // no time spent yet
- lastActivity = now;
- createdAt = -1;
- lastInterval = -1;
- }
-
- protected void injectSessionAttributes(PackageBuilder builder) {
- injectGeneralAttributes(builder);
- builder.setLastInterval(lastInterval);
- }
-
- protected void injectEventAttributes(PackageBuilder builder) {
- injectGeneralAttributes(builder);
- builder.setEventCount(eventCount);
- }
-
- @Override
- public String toString() {
- return String.format(Locale.US,
- "ec:%d sc:%d ssc:%d sl:%.1f ts:%.1f la:%s",
- eventCount, sessionCount, subsessionCount,
- sessionLength / 1000.0, timeSpent / 1000.0,
- stamp(lastActivity));
- }
-
- private void readObject(ObjectInputStream stream) throws NotActiveException, IOException, ClassNotFoundException {
- GetField fields = stream.readFields();
-
- eventCount = fields.get("eventCount", 0);
- sessionCount = fields.get("sessionCount", 0);
- subsessionCount = fields.get("subsessionCount", -1);
- sessionLength = fields.get("sessionLength", -1l);
- timeSpent = fields.get("timeSpent", -1l);
- lastActivity = fields.get("lastActivity", -1l);
- createdAt = fields.get("createdAt", -1l);
- lastInterval = fields.get("lastInterval", -1l);
-
- // default values for migrating devices
- uuid = null;
- enabled = true;
- // try to read in order of less recent new fields
- try {
- uuid = (String)fields.get("uuid", null);
- enabled = fields.get("enabled", true);
- // add new fields here
- } catch (Exception e) {
- Logger logger = AdjustFactory.getLogger();
- logger.debug("Unable to read new field in migration device with error (%s)",
- e.getMessage());
- }
-
- // create UUID for migrating devices
- if (uuid == null) {
- uuid = Util.createUuid();
- Log.d("XXX", "migrate " + uuid);
- }
- }
-
- private static String stamp(long dateMillis) {
- Date date = new Date(dateMillis);
- return String.format(Locale.US,
- "%02d:%02d:%02d",
- date.getHours(),
- date.getMinutes(),
- date.getSeconds());
- }
-
- private void injectGeneralAttributes(PackageBuilder builder) {
- builder.setSessionCount(sessionCount);
- builder.setSubsessionCount(subsessionCount);
- builder.setSessionLength(sessionLength);
- builder.setTimeSpent(timeSpent);
- builder.setCreatedAt(createdAt);
- builder.setUuid(uuid);
- }
-}
diff --git a/Adjust/src/com/adjust/sdk/Adjust.java b/Adjust/src/com/adjust/sdk/Adjust.java
deleted file mode 100644
index b306dfb66..000000000
--- a/Adjust/src/com/adjust/sdk/Adjust.java
+++ /dev/null
@@ -1,172 +0,0 @@
-//
-// Adjust.java
-// Adjust
-//
-// Created by Christian Wellenbrock on 2012-10-11.
-// Copyright (c) 2012-2014 adjust GmbH. All rights reserved.
-// See the file MIT-LICENSE for copying permission.
-//
-
-package com.adjust.sdk;
-
-import android.content.Context;
-import android.net.Uri;
-
-import java.util.Map;
-
-import static com.adjust.sdk.Constants.NO_ACTIVITY_HANDLER_FOUND;
-
-/**
- * The main interface to Adjust.
- *
- * Use the methods of this class to tell Adjust about the usage of your app.
- * See the README for details.
- */
-public class Adjust {
-
- /**
- * Tell Adjust that an activity did resume.
- *
- * This is used to initialize Adjust and keep track of the current session state.
- * Call this in the onResume method of every activity of your app.
- *
- * @param context The context of the activity that has just resumed.
- */
- public static void onResume(Context context) {
- if (null == activityHandler) {
- activityHandler = new ActivityHandler(context);
- }
- activityHandler.trackSubsessionStart();
- }
-
- /**
- * Tell Adjust that an activity will pause.
- *
- * This is used to calculate session attributes like session length and subsession count.
- * Call this in the onPause method of every activity of your app.
- */
- public static void onPause() {
- try {
- getLogger().debug("onPause");
- activityHandler.trackSubsessionEnd();
- } catch (NullPointerException e) {
- getLogger().error(NO_ACTIVITY_HANDLER_FOUND);
- }
- }
-
- public static void setOnFinishedListener(OnFinishedListener listener) {
- try {
- activityHandler.setOnFinishedListener(listener);
- } catch (NullPointerException e) {
- getLogger().error(NO_ACTIVITY_HANDLER_FOUND);
- }
- }
-
- /**
- * Tell Adjust that a particular event has happened.
- *
- * In your dashboard at http://adjust.com you can assign a callback URL to each
- * event type. That URL will get called every time the event is triggered. On
- * top of that you can pass a set of parameters to the following method that
- * will be forwarded to these callbacks.
- *
- * @param eventToken The Event Token for this kind of event. They are created
- * in the dashboard at http://adjust.com and should be six characters long.
- * @param parameters An optional dictionary containing the callback parameters.
- * Provide key-value-pairs to be forwarded to your callbacks.
- */
- public static void trackEvent(String eventToken) {
- trackEvent(eventToken, null);
- }
-
- public static void trackEvent(String eventToken, Map parameters) {
- try {
- activityHandler.trackEvent(eventToken, parameters);
- } catch (NullPointerException e) {
- getLogger().error(NO_ACTIVITY_HANDLER_FOUND);
- }
- }
-
-
- /**
- * Tell Adjust that a user generated some revenue.
- *
- * The amount is measured in cents and rounded to on digit after the
- * decimal point. If you want to differentiate between several revenue
- * types, you can do so by using different event tokens. If your revenue
- * events have callbacks, you can also pass in parameters that will be
- * forwarded to your end point.
- *
- * @param amountInCents The amount in cents (example: 1.5 means one and a half cents)
- * @param eventToken The token for this revenue event (optional, see above)
- * @param parameters Parameters for this revenue event (optional, see above)
- */
- public static void trackRevenue(double amountInCents) {
- Adjust.trackRevenue(amountInCents, null);
- }
-
- public static void trackRevenue(double amountInCents, String eventToken) {
- Adjust.trackRevenue(amountInCents, eventToken, null);
- }
-
- public static void trackRevenue(double amountInCents, String eventToken, Map parameters) {
- try {
- activityHandler.trackRevenue(amountInCents, eventToken, parameters);
- } catch (NullPointerException e) {
- getLogger().error(NO_ACTIVITY_HANDLER_FOUND);
- }
- }
-
- /**
- * Enable or disable the adjust SDK
- *
- * @param enabled The flag to enable or disable the adjust SDK
- */
- public static void setEnabled(Boolean enabled) {
- try {
- activityHandler.setEnabled(enabled);
- } catch (NullPointerException e) {
- getLogger().error(NO_ACTIVITY_HANDLER_FOUND);
- }
- }
-
- /**
- * Check if the SDK is enabled or disabled
- */
- public static Boolean isEnabled() {
- try {
- return activityHandler.isEnabled();
- } catch (NullPointerException e) {
- getLogger().error(NO_ACTIVITY_HANDLER_FOUND);
- }
- return false;
- }
-
- public static void appWillOpenUrl(Uri url) {
- try {
- activityHandler.readOpenUrl(url);
- } catch (NullPointerException e) {
- getLogger().error(NO_ACTIVITY_HANDLER_FOUND);
- }
-
- }
-
-
- // Special appDidLaunch method used by SDK wrappers such as our Adobe Air SDK.
- public static void appDidLaunch(Context context, String appToken, String environment, String logLevel, boolean eventBuffering) {
- activityHandler = new ActivityHandler(context, appToken, environment, logLevel, eventBuffering);
- }
-
- // Special method used by SDK wrappers such as our Adobe Air SDK.
- public static void setSdkPrefix(String sdkPrefix) {
- activityHandler.setSdkPrefix(sdkPrefix);
- }
-
- /**
- * Every activity will get forwarded to this handler to be processed in the background.
- */
- private static ActivityHandler activityHandler;
- private static Logger getLogger() {
- return AdjustFactory.getLogger();
- };
-}
diff --git a/Adjust/src/com/adjust/sdk/Constants.java b/Adjust/src/com/adjust/sdk/Constants.java
deleted file mode 100644
index 3d5e497c5..000000000
--- a/Adjust/src/com/adjust/sdk/Constants.java
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-// Constants.java
-// Adjust
-//
-// Created by keyboardsurfer on 2013-11-08.
-// Copyright (c) 2012-2014 adjust GmbH. All rights reserved.
-// See the file MIT-LICENSE for copying permission.
-//
-
-package com.adjust.sdk;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * @author keyboardsurfer
- * @since 8.11.13
- */
-public interface Constants {
- int ONE_SECOND = 1000;
- int ONE_MINUTE = 60 * ONE_SECOND;
- int THIRTY_MINUTES = 30 * ONE_MINUTE;
-
- String BASE_URL = "https://app.adjust.io";
- String CLIENT_SDK = "android3.6.2";
- String LOGTAG = "Adjust";
-
- String SESSION_STATE_FILENAME = "AdjustIoActivityState";
- String NO_ACTIVITY_HANDLER_FOUND = "No activity handler found";
-
- String UNKNOWN = "unknown";
- String MALFORMED = "malformed";
- String SMALL = "small";
- String NORMAL = "normal";
- String LONG = "long";
- String LARGE = "large";
- String XLARGE = "xlarge";
- String LOW = "low";
- String MEDIUM = "medium";
- String HIGH = "high";
- String REFERRER = "referrer";
-
- String ENCODING = "UTF-8";
- String MD5 = "MD5";
- String SHA1 = "SHA-1";
-
- // List of known plugins, possibly not active
- List PLUGINS = Arrays.asList();
-}
diff --git a/Adjust/src/com/adjust/sdk/IRequestHandler.java b/Adjust/src/com/adjust/sdk/IRequestHandler.java
deleted file mode 100644
index f9fd17f79..000000000
--- a/Adjust/src/com/adjust/sdk/IRequestHandler.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.adjust.sdk;
-
-public interface IRequestHandler {
- public void sendPackage(ActivityPackage pack);
-}
diff --git a/Adjust/src/com/adjust/sdk/LogCatLogger.java b/Adjust/src/com/adjust/sdk/LogCatLogger.java
deleted file mode 100644
index e3169721c..000000000
--- a/Adjust/src/com/adjust/sdk/LogCatLogger.java
+++ /dev/null
@@ -1,81 +0,0 @@
-//
-// Logger.java
-// Adjust
-//
-// Created by Christian Wellenbrock on 2013-04-18.
-// Copyright (c) 2013 adjust GmbH. All rights reserved.
-// See the file MIT-LICENSE for copying permission.
-//
-
-package com.adjust.sdk;
-
-import static com.adjust.sdk.Constants.LOGTAG;
-
-import java.util.Locale;
-
-import android.util.Log;
-
-public class LogCatLogger implements Logger {
-
- private LogLevel logLevel;
-
- public LogCatLogger() {
- setLogLevel(LogLevel.INFO);
- }
-
- @Override
- public void setLogLevel(LogLevel logLevel) {
- this.logLevel = logLevel;
- }
-
- @Override
- public void setLogLevelString(String logLevelString) {
- if (null != logLevelString) {
- try {
- setLogLevel(LogLevel.valueOf(logLevelString.toUpperCase(Locale.US)));
- } catch (IllegalArgumentException iae) {
- error("Malformed logLevel '%s', falling back to 'info'", logLevelString);
- }
- }
- }
-
- @Override
- public void verbose(String message, Object ...parameters) {
- if (logLevel.androidLogLevel <= Log.VERBOSE) {
- Log.v(LOGTAG, String.format(message, parameters));
- }
- }
-
- @Override
- public void debug(String message, Object ...parameters) {
- if (logLevel.androidLogLevel <= Log.DEBUG) {
- Log.d(LOGTAG, String.format(message, parameters));
- }
- }
-
- @Override
- public void info(String message, Object ...parameters) {
- if (logLevel.androidLogLevel <= Log.INFO) {
- Log.i(LOGTAG, String.format(message, parameters));
- }
- }
-
- @Override
- public void warn(String message, Object ...parameters) {
- if (logLevel.androidLogLevel <= Log.WARN) {
- Log.w(LOGTAG, String.format(message, parameters));
- }
- }
-
- @Override
- public void error(String message, Object ...parameters) {
- if (logLevel.androidLogLevel <= Log.ERROR) {
- Log.e(LOGTAG, String.format(message, parameters));
- }
- }
-
- @Override
- public void Assert(String message, Object ...parameters) {
- Log.println(Log.ASSERT, LOGTAG, String.format(message, parameters));
- }
-}
diff --git a/Adjust/src/com/adjust/sdk/Logger.java b/Adjust/src/com/adjust/sdk/Logger.java
deleted file mode 100644
index 4662cd8fb..000000000
--- a/Adjust/src/com/adjust/sdk/Logger.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.adjust.sdk;
-
-import android.util.Log;
-
-public interface Logger {
-
- public enum LogLevel {
- VERBOSE(Log.VERBOSE), DEBUG(Log.DEBUG), INFO(Log.INFO), WARN(Log.WARN), ERROR(Log.ERROR), ASSERT(Log.ASSERT);
- final int androidLogLevel;
-
- LogLevel(final int androidLogLevel) {
- this.androidLogLevel = androidLogLevel;
- }
-
- public int getAndroidLogLevel() {
- return androidLogLevel;
- }
- }
-
- public void setLogLevel(LogLevel logLevel);
-
- public void setLogLevelString(String logLevelString);
-
- public void verbose(String message, Object ...parameters);
-
- public void debug(String message, Object ...parameters);
-
- public void info(String message, Object ...parameters);
-
- public void warn(String message, Object ...parameters);
-
- public void error(String message, Object ...parameters);
-
- public void Assert(String message, Object ...parameters);
-
-}
diff --git a/Adjust/src/com/adjust/sdk/OnFinishedListener.java b/Adjust/src/com/adjust/sdk/OnFinishedListener.java
deleted file mode 100644
index 5d62605e7..000000000
--- a/Adjust/src/com/adjust/sdk/OnFinishedListener.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.adjust.sdk;
-
-public interface OnFinishedListener {
- public void onFinishedTracking(ResponseData responseData);
-}
diff --git a/Adjust/src/com/adjust/sdk/PackageBuilder.java b/Adjust/src/com/adjust/sdk/PackageBuilder.java
deleted file mode 100644
index 7f0b7eb2d..000000000
--- a/Adjust/src/com/adjust/sdk/PackageBuilder.java
+++ /dev/null
@@ -1,394 +0,0 @@
-//
-// PackageBuilder.java
-// Adjust
-//
-// Created by Christian Wellenbrock on 2013-06-25.
-// Copyright (c) 2013 adjust GmbH. All rights reserved.
-// See the file MIT-LICENSE for copying permission.
-//
-
-package com.adjust.sdk;
-
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-import org.json.JSONObject;
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.Base64;
-
-public class PackageBuilder {
-
- private Context context;
-
- // general
- private String appToken;
- private String macSha1;
- private String macShortMd5;
- private String androidId;
- private String fbAttributionId;
- private String userAgent;
- private String clientSdk;
- private String uuid;
- private String environment;
- private Map pluginsKeys;
-
- // sessions
- private int sessionCount;
- private int subsessionCount;
- private long createdAt;
- private long sessionLength;
- private long timeSpent;
- private long lastInterval;
- private String defaultTracker;
- private String referrer;
-
- // events
- private int eventCount;
- private String eventToken;
- private double amountInCents;
- private Map callbackParameters;
-
- // reattributions
- private Map deepLinkParameters;
-
- public PackageBuilder(Context context)
- {
- this.context = context;
- }
-
- public void setAppToken(String appToken) {
- this.appToken = appToken;
- }
-
- public void setMacSha1(String macSha1) {
- this.macSha1 = macSha1;
- }
-
- public void setMacShortMd5(String macShortMd5) {
- this.macShortMd5 = macShortMd5;
- }
-
- public void setAndroidId(String androidId) {
- this.androidId = androidId;
- }
-
- public void setUuid(String uuid) {
- this.uuid = uuid;
- }
-
- public void setFbAttributionId(String fbAttributionId) {
- this.fbAttributionId = fbAttributionId;
- }
-
- public void setUserAgent(String userAgent) {
- this.userAgent = userAgent;
- }
-
- public void setClientSdk(String clientSdk) {
- this.clientSdk = clientSdk;
- }
-
- public void setEnvironment(String environment) {
- this.environment = environment;
- }
-
- public void setSessionCount(int sessionCount) {
- this.sessionCount = sessionCount;
- }
-
- public void setSubsessionCount(int subsessionCount) {
- this.subsessionCount = subsessionCount;
- }
-
- public void setCreatedAt(long createdAt) {
- this.createdAt = createdAt;
- }
-
- public void setSessionLength(long sessionLength) {
- this.sessionLength = sessionLength;
- }
-
- public void setTimeSpent(long timeSpent) {
- this.timeSpent = timeSpent;
- }
-
- public void setLastInterval(long lastInterval) {
- this.lastInterval = lastInterval;
- }
-
- public void setDefaultTracker(String defaultTracker) {
- this.defaultTracker = defaultTracker;
- }
-
- public void setReferrer(String referrer) {
- this.referrer = referrer;
- }
-
- public void setEventCount(int eventCount) {
- this.eventCount = eventCount;
- }
-
- public String getEventToken() {
- return eventToken;
- }
-
- public void setEventToken(String eventToken) {
- this.eventToken = eventToken;
- }
-
- public double getAmountInCents() {
- return amountInCents;
- }
-
- public void setAmountInCents(double amountInCents) {
- this.amountInCents = amountInCents;
- }
-
- public void setCallbackParameters(Map callbackParameters) {
- this.callbackParameters = callbackParameters;
- }
-
- public void setDeepLinkParameters(Map deepLinkParameters) {
- this.deepLinkParameters = deepLinkParameters;
- }
-
- public void setPluginKeys(Map pluginKeys) {
- this.pluginsKeys = pluginKeys;
- }
-
- public boolean isValidForEvent() {
- if (null == eventToken) {
- Logger logger = AdjustFactory.getLogger();
- logger.error("Missing Event Token");
- return false; // non revenue events need event tokens
- }
- return isEventTokenValid(); // and they must be valid
- }
-
- public boolean isValidForRevenue() {
- if (amountInCents < 0.0) {
- Logger logger = AdjustFactory.getLogger();
- logger.error("Invalid amount %f", amountInCents);
- return false;
- }
- if (eventToken == null) {
- return true; // revenue events don't need event tokens
- }
- return isEventTokenValid(); // but if they have one, it must be valid
- }
-
- public ActivityPackage buildSessionPackage() {
- Map parameters = getDefaultParameters();
- addDuration(parameters, "last_interval", lastInterval);
- addString(parameters, "default_tracker", defaultTracker);
- addString(parameters, Constants.REFERRER, referrer);
-
- ActivityPackage sessionPackage = getDefaultActivityPackage();
- sessionPackage.setPath("/startup");
- sessionPackage.setActivityKind(ActivityKind.SESSION);
- sessionPackage.setSuffix("");
- sessionPackage.setParameters(parameters);
-
- return sessionPackage;
- }
-
- public ActivityPackage buildEventPackage() {
- Map parameters = getDefaultParameters();
- injectEventParameters(parameters);
-
- ActivityPackage eventPackage = getDefaultActivityPackage();
- eventPackage.setPath("/event");
- eventPackage.setActivityKind(ActivityKind.EVENT);
- eventPackage.setSuffix(getEventSuffix());
- eventPackage.setParameters(parameters);
-
- return eventPackage;
- }
-
- public ActivityPackage buildRevenuePackage() {
- Map parameters = getDefaultParameters();
- injectEventParameters(parameters);
- addString(parameters, "amount", getAmountString());
-
- ActivityPackage revenuePackage = getDefaultActivityPackage();
- revenuePackage.setPath("/revenue");
- revenuePackage.setActivityKind(ActivityKind.REVENUE);
- revenuePackage.setSuffix(getRevenueSuffix());
- revenuePackage.setParameters(parameters);
-
- return revenuePackage;
- }
-
- public ActivityPackage buildReattributionPackage() {
- Map parameters = getDefaultParameters();
- addMapJson(parameters, "deeplink_parameters", deepLinkParameters);
-
- ActivityPackage reattributionPackage = getDefaultActivityPackage();
- reattributionPackage.setPath("/reattribute");
- reattributionPackage.setActivityKind(ActivityKind.REATTRIBUTION);
- reattributionPackage.setSuffix("");
- reattributionPackage.setParameters(parameters);
-
- return reattributionPackage;
- }
-
- private boolean isEventTokenValid() {
- if (6 != eventToken.length()) {
- Logger logger = AdjustFactory.getLogger();
- logger.error("Malformed Event Token '%s'", eventToken);
- return false;
- }
- return true;
- }
-
- private ActivityPackage getDefaultActivityPackage() {
- ActivityPackage activityPackage = new ActivityPackage();
- activityPackage.setUserAgent(userAgent);
- activityPackage.setClientSdk(clientSdk);
- return activityPackage;
- }
-
- private Map getDefaultParameters() {
- Map parameters = new HashMap();
-
- // general
- addDate(parameters, "created_at", createdAt);
- addString(parameters, "app_token", appToken);
- addString(parameters, "mac_sha1", macSha1);
- addString(parameters, "mac_md5", macShortMd5);
- addString(parameters, "android_id", androidId);
- addString(parameters, "android_uuid", uuid);
- addString(parameters, "fb_id", fbAttributionId);
- addString(parameters, "environment", environment);
- String playAdId = Util.getPlayAdId(context);
- addString(parameters, "gps_adid", playAdId);
- Boolean isTrackingEnabled = Util.isPlayTrackingEnabled(context);
- addBoolean(parameters, "tracking_enabled", isTrackingEnabled);
- fillPluginKeys(parameters);
- checkDeviceIds(parameters);
-
- // session related (used for events as well)
- addInt(parameters, "session_count", sessionCount);
- addInt(parameters, "subsession_count", subsessionCount);
- addDuration(parameters, "session_length", sessionLength);
- addDuration(parameters, "time_spent", timeSpent);
-
- return parameters;
- }
-
- private void checkDeviceIds(Map parameters) {
- if (!parameters.containsKey("mac_sha1")
- && !parameters.containsKey("mac_md5")
- && !parameters.containsKey("android_id")
- && !parameters.containsKey("gps_adid"))
- {
- Logger logger = AdjustFactory.getLogger();
- logger.error("Missing device id's. Please check if Proguard is correctly set with Adjust SDK");
- }
- }
-
- private void fillPluginKeys(Map parameters) {
- if (pluginsKeys == null) {
- return;
- }
-
- for (Map.Entry pluginEntry : pluginsKeys.entrySet()) {
- addString(parameters, pluginEntry.getKey(), pluginEntry.getValue());
- }
- }
-
- private void injectEventParameters(Map parameters) {
- addInt(parameters, "event_count", eventCount);
- addString(parameters, "event_token", eventToken);
- addMapBase64(parameters, "params", callbackParameters);
- }
-
- private String getAmountString() {
- long amountInMillis = Math.round(10 * amountInCents);
- amountInCents = amountInMillis / 10.0; // now rounded to one decimal point
- return Long.toString(amountInMillis);
- }
-
- private String getEventSuffix() {
- return String.format(" '%s'", eventToken);
- }
-
- private String getRevenueSuffix() {
- if (eventToken != null) {
- return String.format(Locale.US, " (%.1f cent, '%s')", amountInCents, eventToken);
- } else {
- return String.format(Locale.US, " (%.1f cent)", amountInCents);
- }
- }
-
- private void addString(Map parameters, String key, String value) {
- if (TextUtils.isEmpty(value)) {
- return;
- }
-
- parameters.put(key, value);
- }
-
- private void addInt(Map parameters, String key, long value) {
- if (value < 0) {
- return;
- }
-
- String valueString = Long.toString(value);
- addString(parameters, key, valueString);
- }
-
- private void addDate(Map parameters, String key, long value) {
- if (value < 0) {
- return;
- }
-
- String dateString = Util.dateFormat(value);
- addString(parameters, key, dateString);
- }
-
- private void addDuration(Map parameters, String key, long durationInMilliSeconds) {
- if (durationInMilliSeconds < 0) {
- return;
- }
-
- long durationInSeconds = (durationInMilliSeconds + 500) / 1000;
- addInt(parameters, key, durationInSeconds);
- }
-
- private void addMapBase64(Map parameters, String key, Map map) {
- if (null == map) {
- return;
- }
-
- JSONObject jsonObject = new JSONObject(map);
- byte[] jsonBytes = jsonObject.toString().getBytes();
- String encodedMap = Base64.encodeToString(jsonBytes, Base64.NO_WRAP);
-
- addString(parameters, key, encodedMap);
- }
-
- private void addMapJson(Map parameters, String key, Map map) {
- if (null == map) {
- return;
- }
-
- JSONObject jsonObject = new JSONObject(map);
- String jsonString = jsonObject.toString();
-
- addString(parameters, key, jsonString);
- }
-
- private void addBoolean(Map parameters, String key, Boolean value) {
- if (value == null) {
- return;
- }
-
- int intValue = value? 1 : 0;
-
- addInt(parameters, key, intValue);
- }
-}
diff --git a/Adjust/src/com/adjust/sdk/ResponseData.java b/Adjust/src/com/adjust/sdk/ResponseData.java
deleted file mode 100644
index 079fe8dee..000000000
--- a/Adjust/src/com/adjust/sdk/ResponseData.java
+++ /dev/null
@@ -1,188 +0,0 @@
-package com.adjust.sdk;
-
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-import org.json.JSONObject;
-
-import android.text.TextUtils;
-
-/*
- * Information about the result of a tracking attempt
- *
- * Will be passed to the delegate function TODO: update
- */
-public class ResponseData {
- // set by SDK
-
- // the kind of activity (ActivityKind.SESSION etc.)
- // see the ActivityKind definition
- public ActivityKind getActivityKind() {
- return activityKind;
- }
-
- // returns human readable version of activityKind
- // (session, event, revenue), see above
- public String getActivityKindString() {
- return activityKind.toString();
- }
-
- // true when the activity was tracked successfully
- // might be true even if response could not be parsed
- public boolean wasSuccess() {
- return success;
- }
-
- // true if the server was not reachable and the request will be tried again
- public boolean willRetry() {
- return willRetry;
- }
-
- // set by SDK or server
-
- // null if activity was tracked successfully and response could be parsed
- // might be not null even when activity was tracked successfully
- public String getError() {
- return error;
- }
-
- // returned by server
-
- // tracker token of current device
- public String getTrackerToken() {
- return trackerToken;
- }
-
- // tracker name of current device
- public String getTrackerName() {
- return trackerName;
- }
-
- // network of the tracker
- public String getNetwork() {
- return network;
- }
-
- // campaign of the tracker
- public String getCampaign() {
- return campaign;
- }
-
- // adgroup of the tracker
- public String getAdgroup() {
- return adgroup;
- }
-
- // creative of the tracker
- public String getCreative() {
- return creative;
- }
-
- // internals
-
- private ActivityKind activityKind = ActivityKind.UNKNOWN;
- private boolean success;
- private boolean willRetry;
- private String error;
- private String trackerToken;
- private String trackerName;
- private String network;
- private String campaign;
- private String adgroup;
- private String creative;
-
- public static ResponseData fromJson(JSONObject jsonObject, String jsonString) {
-
- if (jsonObject == null) {
- String error = String.format("Failed to parse json response: %s", jsonString.trim());
- return ResponseData.fromError(error);
- }
-
- ResponseData data = new ResponseData();
-
- data.error = jsonObject.optString("error", null);
- data.trackerToken = jsonObject.optString("tracker_token", null);
- data.trackerName = jsonObject.optString("tracker_name", null);
- data.network = jsonObject.optString("network", null);
- data.campaign = jsonObject.optString("campaign", null);
- data.adgroup = jsonObject.optString("adgroup", null);
- data.creative = jsonObject.optString("creative", null);
-
- return data;
- }
-
- public static ResponseData fromError(String error) {
- ResponseData data = new ResponseData();
- data.error = error;
- return data;
- }
-
- @Override
- public String toString() {
- return String.format(Locale.US,
- "[kind:%s success:%b willRetry:%b "
- + "error:%s trackerToken:%s trackerName:%s "
- + "network:%s campaign:%s adgroup:%s creative:%s]",
- getActivityKindString(),
- success,
- willRetry,
- Util.quote(error),
- trackerToken,
- Util.quote(trackerName),
- Util.quote(network),
- Util.quote(campaign),
- Util.quote(adgroup),
- Util.quote(creative));
- }
-
- public void setActivityKind(ActivityKind activityKind) {
- this.activityKind = activityKind;
- }
-
- public void setWasSuccess(boolean success) {
- this.success = success;
- }
-
- public void setWillRetry(boolean willRetry) {
- this.willRetry = willRetry;
- }
-
- public Map toDic() {
- Map responseDataDic = new HashMap();
-
- responseDataDic.put("activityKind", activityKind.toString());
- responseDataDic.put("success", success ? "true" : "false");
- responseDataDic.put("willRetry", willRetry ? "true" : "false");
-
- if (!TextUtils.isEmpty(error)) {
- responseDataDic.put("error", error);
- }
-
- if (!TextUtils.isEmpty(trackerToken)) {
- responseDataDic.put("trackerToken", trackerToken);
- }
-
- if (!TextUtils.isEmpty(trackerName)) {
- responseDataDic.put("trackerName", trackerName);
- }
-
- if (!TextUtils.isEmpty(network)) {
- responseDataDic.put("network", network);
- }
-
- if (!TextUtils.isEmpty(campaign)) {
- responseDataDic.put("campaign", campaign);
- }
-
- if (!TextUtils.isEmpty(adgroup)) {
- responseDataDic.put("adgroup", adgroup);
- }
-
- if (!TextUtils.isEmpty(creative)) {
- responseDataDic.put("creative", creative);
- }
-
- return responseDataDic;
- }
-}
diff --git a/Adjust/src/com/adjust/sdk/Util.java b/Adjust/src/com/adjust/sdk/Util.java
deleted file mode 100644
index 4c004544f..000000000
--- a/Adjust/src/com/adjust/sdk/Util.java
+++ /dev/null
@@ -1,380 +0,0 @@
-//
-// Util.java
-// Adjust
-//
-// Created by Christian Wellenbrock on 2012-10-11.
-// Copyright (c) 2012-2014 adjust GmbH. All rights reserved.
-// See the file MIT-LICENSE for copying permission.
-//
-
-package com.adjust.sdk;
-
-import static com.adjust.sdk.Constants.ENCODING;
-import static com.adjust.sdk.Constants.HIGH;
-import static com.adjust.sdk.Constants.LARGE;
-import static com.adjust.sdk.Constants.LONG;
-import static com.adjust.sdk.Constants.LOW;
-import static com.adjust.sdk.Constants.MD5;
-import static com.adjust.sdk.Constants.MEDIUM;
-import static com.adjust.sdk.Constants.NORMAL;
-import static com.adjust.sdk.Constants.PLUGINS;
-import static com.adjust.sdk.Constants.SHA1;
-import static com.adjust.sdk.Constants.SMALL;
-import static com.adjust.sdk.Constants.UNKNOWN;
-import static com.adjust.sdk.Constants.XLARGE;
-
-import java.math.BigInteger;
-import java.security.MessageDigest;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.UUID;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.DisplayMetrics;
-
-import com.adjust.sdk.plugin.Plugin;
-
-/**
- * Collects utility functions used by Adjust.
- */
-public class Util {
-
- private static SimpleDateFormat dateFormat;
- private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'Z";
-
- protected static String getUserAgent(final Context context) {
- final Resources resources = context.getResources();
- final DisplayMetrics displayMetrics = resources.getDisplayMetrics();
- final Configuration configuration = resources.getConfiguration();
- final Locale locale = configuration.locale;
- final int screenLayout = configuration.screenLayout;
-
- final String[] parts = {
- getPackageName(context),
- getAppVersion(context),
- getDeviceType(screenLayout),
- getDeviceName(),
- getOsName(),
- getOsVersion(),
- getLanguage(locale),
- getCountry(locale),
- getScreenSize(screenLayout),
- getScreenFormat(screenLayout),
- getScreenDensity(displayMetrics),
- getDisplayWidth(displayMetrics),
- getDisplayHeight(displayMetrics)
- };
- return TextUtils.join(" ", parts);
- }
-
- private static String getPackageName(final Context context) {
- final String packageName = context.getPackageName();
- return sanitizeString(packageName);
- }
-
- private static String getAppVersion(final Context context) {
- try {
- final PackageManager packageManager = context.getPackageManager();
- final String name = context.getPackageName();
- final PackageInfo info = packageManager.getPackageInfo(name, 0);
- final String versionName = info.versionName;
- return sanitizeString(versionName);
- } catch (NameNotFoundException e) {
- return UNKNOWN;
- }
- }
-
- private static String getDeviceType(final int screenLayout) {
- int screenSize = screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
-
- switch (screenSize) {
- case Configuration.SCREENLAYOUT_SIZE_SMALL:
- case Configuration.SCREENLAYOUT_SIZE_NORMAL:
- return "phone";
- case Configuration.SCREENLAYOUT_SIZE_LARGE:
- case 4:
- return "tablet";
- default:
- return UNKNOWN;
- }
- }
-
- private static String getDeviceName() {
- final String deviceName = Build.MODEL;
- return sanitizeString(deviceName);
- }
-
- private static String getOsName() {
- return "android";
- }
-
- private static String getOsVersion() {
- final String osVersion = "" + Build.VERSION.SDK_INT;
- return sanitizeString(osVersion);
- }
-
- private static String getLanguage(final Locale locale) {
- final String language = locale.getLanguage();
- return sanitizeStringShort(language);
- }
-
- private static String getCountry(final Locale locale) {
- final String country = locale.getCountry();
- return sanitizeStringShort(country);
- }
-
- private static String getScreenSize(final int screenLayout) {
- final int screenSize = screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
-
- switch (screenSize) {
- case Configuration.SCREENLAYOUT_SIZE_SMALL:
- return SMALL;
- case Configuration.SCREENLAYOUT_SIZE_NORMAL:
- return NORMAL;
- case Configuration.SCREENLAYOUT_SIZE_LARGE:
- return LARGE;
- case 4:
- return XLARGE;
- default:
- return UNKNOWN;
- }
- }
-
- private static String getScreenFormat(final int screenLayout) {
- final int screenFormat = screenLayout & Configuration.SCREENLAYOUT_LONG_MASK;
-
- switch (screenFormat) {
- case Configuration.SCREENLAYOUT_LONG_YES:
- return LONG;
- case Configuration.SCREENLAYOUT_LONG_NO:
- return NORMAL;
- default:
- return UNKNOWN;
- }
- }
-
- private static String getScreenDensity(final DisplayMetrics displayMetrics) {
- final int density = displayMetrics.densityDpi;
- final int low = (DisplayMetrics.DENSITY_MEDIUM + DisplayMetrics.DENSITY_LOW) / 2;
- final int high = (DisplayMetrics.DENSITY_MEDIUM + DisplayMetrics.DENSITY_HIGH) / 2;
-
- if (0 == density) {
- return UNKNOWN;
- } else if (density < low) {
- return LOW;
- } else if (density > high) {
- return HIGH;
- }
- return MEDIUM;
- }
-
- private static String getDisplayWidth(DisplayMetrics displayMetrics) {
- final String displayWidth = String.valueOf(displayMetrics.widthPixels);
- return sanitizeString(displayWidth);
- }
-
- private static String getDisplayHeight(DisplayMetrics displayMetrics) {
- final String displayHeight = String.valueOf(displayMetrics.heightPixels);
- return sanitizeString(displayHeight);
- }
-
- protected static String createUuid() {
- return UUID.randomUUID().toString();
- }
-
- // removes spaces and replaces empty string with "unknown"
- private static String sanitizeString(final String string) {
- return sanitizeString(string, UNKNOWN);
- }
-
- private static String sanitizeStringShort(final String string) {
- return sanitizeString(string, "zz");
- }
-
- private static String sanitizeString(final String string, final String defaultString) {
- String result = string;
- if (TextUtils.isEmpty(result)) {
- result = defaultString;
- }
-
- result = result.replaceAll("\\s", "");
- if (TextUtils.isEmpty(result)) {
- result = defaultString;
- }
-
- return result;
- }
-
- protected static String getAttributionId(final Context context) {
- try {
- final ContentResolver contentResolver = context.getContentResolver();
- final Uri uri = Uri.parse("content://com.facebook.katana.provider.AttributionIdProvider");
- final String columnName = "aid";
- final String[] projection = {columnName};
- final Cursor cursor = contentResolver.query(uri, projection, null, null, null);
-
- if (null == cursor) {
- return null;
- }
- if (!cursor.moveToFirst()) {
- cursor.close();
- return null;
- }
-
- final String attributionId = cursor.getString(cursor.getColumnIndex(columnName));
- cursor.close();
- return attributionId;
- } catch (Exception e) {
- return null;
- }
- }
-
- public static String quote(String string) {
- if (string == null) {
- return null;
- }
-
- Pattern pattern = Pattern.compile("\\s");
- Matcher matcher = pattern.matcher(string);
- if (!matcher.find()) {
- return string;
- }
-
- return String.format("'%s'", string);
- }
-
- public static String dateFormat(long date) {
- if (null == dateFormat) {
- dateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.US);
- }
- return dateFormat.format(date);
- }
-
-
- public static JSONObject buildJsonObject(String jsonString) {
- JSONObject jsonObject = null;
-
- try {
- jsonObject = new JSONObject(jsonString);
- } catch (JSONException e){
- }
-
- return jsonObject;
- }
-
- public static String getPlayAdId(Context context) {
- return Reflection.getPlayAdId(context);
- }
-
- public static Boolean isPlayTrackingEnabled(Context context) {
- return Reflection.isPlayTrackingEnabled(context);
- }
-
- public static boolean isGooglePlayServicesAvailable(Context context) {
- return Reflection.isGooglePlayServicesAvailable(context);
- }
-
- public static String getMacAddress(Context context) {
- return Reflection.getMacAddress(context);
- }
-
- public static String getMacSha1(String macAddress) {
- if (macAddress == null) {
- return null;
- }
- String macSha1 = sha1(macAddress);
-
- return macSha1;
- }
-
- public static String getMacShortMd5(String macAddress) {
- if (macAddress == null) {
- return null;
- }
- String macShort = macAddress.replaceAll(":", "");
- String macShortMd5 = md5(macShort);
-
- return macShortMd5;
- }
-
- public static String getAndroidId(Context context) {
- return Reflection.getAndroidId(context);
- }
-
- private static String sha1(final String text) {
- return hash(text, SHA1);
- }
-
- private static String md5(final String text) {
- return hash(text, MD5);
- }
-
- private static String hash(final String text, final String method) {
- try {
- final byte[] bytes = text.getBytes(ENCODING);
- final MessageDigest mesd = MessageDigest.getInstance(method);
- mesd.update(bytes, 0, bytes.length);
- final byte[] hash = mesd.digest();
- return convertToHex(hash);
- } catch (Exception e) {
- return null;
- }
- }
-
- private static String convertToHex(final byte[] bytes) {
- final BigInteger bigInt = new BigInteger(1, bytes);
- final String formatString = "%0" + (bytes.length << 1) + "x";
- return String.format(formatString, bigInt);
- }
-
- public static Map getPluginKeys(Context context) {
- Map pluginKeys = new HashMap();
-
- for (Plugin plugin : getPlugins()) {
- Map.Entry pluginEntry = plugin.getParameter(context);
- if (pluginEntry != null) {
- pluginKeys.put(pluginEntry.getKey(), pluginEntry.getValue());
- }
- }
-
- if (pluginKeys.size() == 0) {
- return null;
- } else {
- return pluginKeys;
- }
- }
-
- private static List getPlugins() {
- List plugins = new ArrayList(PLUGINS.size());
-
- for (String pluginName : PLUGINS) {
- Object pluginObject = Reflection.createDefaultInstance(pluginName);
- if (pluginObject != null && pluginObject instanceof Plugin) {
- plugins.add((Plugin) pluginObject);
- }
- }
-
- return plugins;
- }
-}
diff --git a/Adjust/src/com/adjust/sdk/plugin/MapEntry.java b/Adjust/src/com/adjust/sdk/plugin/MapEntry.java
deleted file mode 100644
index 3e29ab16e..000000000
--- a/Adjust/src/com/adjust/sdk/plugin/MapEntry.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.adjust.sdk.plugin;
-
-import java.util.Map;
-
-/**
- * Created by pfms on 18/09/14.
- */
-public class MapEntry implements Map.Entry{
-
- private K key;
- private V value;
-
- public MapEntry(K key, V value) {
- this.key = key;
- this.value = value;
- }
-
- @Override
- public K getKey() {
- return key;
- }
-
- @Override
- public V getValue() {
- return value;
- }
-
- @Override
- public V setValue(V v) {
- this.value = v;
- return this.value;
- }
-}
diff --git a/Adjust/test/.gitignore b/Adjust/test/.gitignore
new file mode 100644
index 000000000..796b96d1c
--- /dev/null
+++ b/Adjust/test/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Adjust/test/AndroidManifest.xml b/Adjust/test/AndroidManifest.xml
deleted file mode 100644
index d30f4dba0..000000000
--- a/Adjust/test/AndroidManifest.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Adjust/test/build.gradle b/Adjust/test/build.gradle
new file mode 100644
index 000000000..f7db4463f
--- /dev/null
+++ b/Adjust/test/build.gradle
@@ -0,0 +1,16 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion "21.1.2"
+
+ defaultConfig {
+ minSdkVersion 9
+ targetSdkVersion 21
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile project(':adjust')
+}
diff --git a/Adjust/test/project.properties b/Adjust/test/project.properties
deleted file mode 100644
index f34195638..000000000
--- a/Adjust/test/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-19
-android.library.reference.1=..
diff --git a/Adjust/test/res/drawable-ldpi/ic_launcher.png b/Adjust/test/res/drawable-ldpi/ic_launcher.png
deleted file mode 100644
index 99238729d..000000000
Binary files a/Adjust/test/res/drawable-ldpi/ic_launcher.png and /dev/null differ
diff --git a/Adjust/test/res/menu/main.xml b/Adjust/test/res/menu/main.xml
deleted file mode 100644
index c00202823..000000000
--- a/Adjust/test/res/menu/main.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
diff --git a/Adjust/test/res/values-sw600dp/dimens.xml b/Adjust/test/res/values-sw600dp/dimens.xml
deleted file mode 100644
index 44f01db75..000000000
--- a/Adjust/test/res/values-sw600dp/dimens.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
diff --git a/Adjust/test/res/values-sw720dp-land/dimens.xml b/Adjust/test/res/values-sw720dp-land/dimens.xml
deleted file mode 100644
index 61e3fa8fb..000000000
--- a/Adjust/test/res/values-sw720dp-land/dimens.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
- 128dp
-
-
diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/ApplicationTest.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/ApplicationTest.java
new file mode 100644
index 000000000..790870026
--- /dev/null
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.adjust.sdk.test;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/AssertUtil.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/AssertUtil.java
new file mode 100644
index 000000000..899e8df49
--- /dev/null
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/AssertUtil.java
@@ -0,0 +1,116 @@
+package com.adjust.sdk.test;
+
+import junit.framework.Assert;
+
+import static com.adjust.sdk.LogLevel.ASSERT;
+import static com.adjust.sdk.LogLevel.DEBUG;
+import static com.adjust.sdk.LogLevel.ERROR;
+import static com.adjust.sdk.LogLevel.INFO;
+import static com.adjust.sdk.LogLevel.VERBOSE;
+import static com.adjust.sdk.LogLevel.WARN;
+
+/**
+ * Created by pfms on 09/01/15.
+ */
+public class AssertUtil {
+ private MockLogger mockLogger;
+
+ public AssertUtil(MockLogger mockLogger) {
+ this.mockLogger = mockLogger;
+ }
+
+ public void test(String message) {
+ Assert.assertTrue(mockLogger.toString(),
+ mockLogger.containsTestMessage(message));
+ }
+
+ public void verbose(String message) {
+ Assert.assertTrue(mockLogger.toString(),
+ mockLogger.containsMessage(VERBOSE, message));
+ }
+
+ public void debug(String message) {
+ Assert.assertTrue(mockLogger.toString(),
+ mockLogger.containsMessage(DEBUG, message));
+ }
+
+ public void info(String message) {
+ Assert.assertTrue(mockLogger.toString(),
+ mockLogger.containsMessage(INFO, message));
+ }
+
+ public void warn(String message) {
+ Assert.assertTrue(mockLogger.toString(),
+ mockLogger.containsMessage(WARN, message));
+ }
+
+ public void error(String message) {
+ Assert.assertTrue(mockLogger.toString(),
+ mockLogger.containsMessage(ERROR, message));
+ }
+
+ public void Assert(String message) {
+ Assert.assertTrue(mockLogger.toString(),
+ mockLogger.containsMessage(ASSERT, message));
+ }
+
+ public void notInTest(String message) {
+ Assert.assertFalse(mockLogger.toString(),
+ mockLogger.containsTestMessage(message));
+ }
+
+ public void notInVerbose(String message) {
+ Assert.assertFalse(mockLogger.toString(),
+ mockLogger.containsMessage(VERBOSE, message));
+ }
+
+ public void notInDebug(String message) {
+ Assert.assertFalse(mockLogger.toString(),
+ mockLogger.containsMessage(DEBUG, message));
+ }
+
+ public void notInInfo(String message) {
+ Assert.assertFalse(mockLogger.toString(),
+ mockLogger.containsMessage(INFO, message));
+ }
+
+ public void notInWarn(String message) {
+ Assert.assertFalse(mockLogger.toString(),
+ mockLogger.containsMessage(WARN, message));
+ }
+
+ public void notInError(String message) {
+ Assert.assertFalse(mockLogger.toString(),
+ mockLogger.containsMessage(ERROR, message));
+ }
+
+ public void notInAssert(String message) {
+ Assert.assertFalse(mockLogger.toString(),
+ mockLogger.containsMessage(ASSERT, message));
+ }
+
+ public void isNull(Object object) {
+ Assert.assertNull(mockLogger.toString(),
+ object);
+ }
+
+ public void isNotNull(Object object) {
+ Assert.assertNotNull(mockLogger.toString(),
+ object);
+ }
+
+ public void isTrue(boolean value) {
+ Assert.assertTrue(mockLogger.toString(),
+ value);
+ }
+
+ public void isFalse(boolean value) {
+ Assert.assertFalse(mockLogger.toString(),
+ value);
+ }
+
+ public void isEqual(String expected, String actual) {
+ Assert.assertEquals(mockLogger.toString(),
+ expected, actual);
+ }
+}
diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockActivityHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockActivityHandler.java
new file mode 100644
index 000000000..9a297d4af
--- /dev/null
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockActivityHandler.java
@@ -0,0 +1,96 @@
+package com.adjust.sdk.test;
+
+import android.net.Uri;
+
+import com.adjust.sdk.ActivityPackage;
+import com.adjust.sdk.AdjustAttribution;
+import com.adjust.sdk.AdjustConfig;
+import com.adjust.sdk.AdjustEvent;
+import com.adjust.sdk.IActivityHandler;
+
+import org.json.JSONObject;
+
+/**
+ * Created by pfms on 09/01/15.
+ */
+public class MockActivityHandler implements IActivityHandler {
+ private MockLogger testLogger;
+ private String prefix = "ActivityHandler ";
+ boolean updated;
+ AdjustConfig config;
+
+
+ public MockActivityHandler(MockLogger testLogger) {
+ this.testLogger = testLogger;
+ }
+
+ @Override
+ public void init(AdjustConfig config) {
+ testLogger.test(prefix + "init");
+ this.config = config;
+ }
+
+ @Override
+ public void trackSubsessionStart() {
+ testLogger.test(prefix + "trackSubsessionStart");
+ }
+
+ @Override
+ public void trackSubsessionEnd() {
+ testLogger.test(prefix + "trackSubsessionEnd");
+ }
+
+ @Override
+ public void trackEvent(AdjustEvent event) {
+ testLogger.test(prefix + "trackEvent, " + event);
+ }
+
+ @Override
+ public void finishedTrackingActivity(JSONObject jsonResponse) {
+ testLogger.test(prefix + "finishedTrackingActivity, " + jsonResponse);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ testLogger.test(prefix + "setEnabled, " + enabled);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ testLogger.test(prefix + "isEnabled");
+ return false;
+ }
+
+ @Override
+ public void readOpenUrl(Uri url, long clickTime) {
+ testLogger.test(prefix + "readOpenUrl, " + url + ". ClickTime, " + clickTime);
+ }
+
+ @Override
+ public boolean tryUpdateAttribution(AdjustAttribution attribution) {
+ testLogger.test(prefix + "tryUpdateAttribution, " + attribution);
+ return false;
+ }
+
+ @Override
+ public void sendReferrer(String referrer, long clickTime) {
+ testLogger.test(prefix + "sendReferrer, " + referrer + ". ClickTime, " + clickTime);
+ }
+
+ @Override
+ public void setOfflineMode(boolean enabled) {
+ testLogger.test(prefix + "setOfflineMode, " + enabled);
+ }
+
+ @Override
+ public void setAskingAttribution(boolean askingAttribution) {
+ testLogger.test(prefix + "setAskingAttribution, " + askingAttribution);
+ }
+
+ @Override
+ public ActivityPackage getAttributionPackage() {
+ testLogger.test(prefix + "getAttributionPackage");
+ return null;
+ }
+
+}
diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockAttributionHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockAttributionHandler.java
new file mode 100644
index 000000000..5b231cf8e
--- /dev/null
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockAttributionHandler.java
@@ -0,0 +1,51 @@
+package com.adjust.sdk.test;
+
+import com.adjust.sdk.ActivityPackage;
+import com.adjust.sdk.IActivityHandler;
+import com.adjust.sdk.IAttributionHandler;
+
+import org.json.JSONObject;
+
+/**
+ * Created by pfms on 09/01/15.
+ */
+public class MockAttributionHandler implements IAttributionHandler {
+ private MockLogger testLogger;
+ private String prefix = "AttributionHandler ";
+ IActivityHandler activityHandler;
+ JSONObject lastJsonResponse;
+ ActivityPackage attributionPackage;
+
+ public MockAttributionHandler(MockLogger testLogger) {
+ this.testLogger = testLogger;
+ }
+
+ @Override
+ public void init(IActivityHandler activityHandler, ActivityPackage attributionPackage, boolean startPaused) {
+ testLogger.test(prefix + "init, startPaused: " + startPaused);
+ this.activityHandler = activityHandler;
+ this.attributionPackage = attributionPackage;
+ }
+
+ @Override
+ public void getAttribution() {
+ testLogger.test(prefix + "getAttribution");
+ }
+
+ @Override
+ public void checkAttribution(JSONObject jsonResponse) {
+ testLogger.test(prefix + "checkAttribution");
+
+ this.lastJsonResponse = jsonResponse;
+ }
+
+ @Override
+ public void pauseSending() {
+ testLogger.test(prefix + "pauseSending");
+ }
+
+ @Override
+ public void resumeSending() {
+ testLogger.test(prefix + "resumeSending");
+ }
+}
diff --git a/Adjust/test/src/com/adjust/sdk/test/MockHttpClient.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockHttpClient.java
similarity index 58%
rename from Adjust/test/src/com/adjust/sdk/test/MockHttpClient.java
rename to Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockHttpClient.java
index bbdbb95a3..43b70fa2b 100644
--- a/Adjust/test/src/com/adjust/sdk/test/MockHttpClient.java
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockHttpClient.java
@@ -1,9 +1,5 @@
package com.adjust.sdk.test;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
@@ -21,34 +17,66 @@
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
public class MockHttpClient implements HttpClient {
private MockLogger testLogger;
private String prefix = "HttpClient ";
- private String messageError;
- private String responseError;
+ public ResponseType responseType;
+ public HttpUriRequest lastRequest;
+ public boolean timeout;
public MockHttpClient(MockLogger testLogger) {
this.testLogger = testLogger;
- messageError = null;
}
@Override
public HttpResponse execute(HttpUriRequest request) throws IOException,
ClientProtocolException {
- testLogger.test(prefix + "execute HttpUriRequest request");
+ testLogger.test(prefix + "execute, responseType: " + responseType);
+ lastRequest = request;
+
+ if (timeout) {
+ testLogger.test("timing out");
- if (messageError != null) {
- throw new ClientProtocolException(messageError);
}
- if (responseError != null)
- return getMockResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "{ \"error\": \"" + responseError + "\"}");
- else
- return getMockResponse(HttpStatus.SC_OK, "{ \"tracker_token\": \"token\", \"tracker_name\": \"name\", \"network\": \"network\", \"campaign\": \"campaign\", \"adgroup\": \"adgroup\", \"creative\": \"creative\", \"deeplink\": \"testApp://\"}");
+ if (responseType == ResponseType.CLIENT_PROTOCOL_EXCEPTION) {
+ throw new ClientProtocolException("testResponseError");
+ } else if (responseType == ResponseType.INTERNAL_SERVER_ERROR) {
+ return getMockResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "{ \"message\": \"testResponseError\"}");
+ } else if (responseType == ResponseType.WRONG_JSON) {
+ return getOkResponse("not a json response");
+ } else if (responseType == ResponseType.EMPTY_JSON) {
+ return getOkResponse("{ }");
+ } else if (responseType == ResponseType.MESSAGE) {
+ return getOkResponse("{ \"message\" : \"response OK\"}");
+ } else if (responseType == ResponseType.ATTRIBUTION) {
+ return getOkResponse(
+ "{ \"attribution\" : {" +
+ "\"tracker_token\" : \"ttValue\" , " +
+ "\"tracker_name\" : \"tnValue\" , " +
+ "\"network\" : \"nValue\" , " +
+ "\"campaign\" : \"cpValue\" , " +
+ "\"adgroup\" : \"aValue\" , " +
+ "\"creative\" : \"ctValue\" } }");
+ } else if (responseType == ResponseType.ASK_IN) {
+ return getOkResponse("{ \"ask_in\" : 4000 }");
+ }
+
+ return null;
+ }
+
+ private HttpResponse getOkResponse(String responseData)
+ throws IOException {
+ return getMockResponse(HttpStatus.SC_OK, responseData);
}
- private HttpResponse getMockResponse(int statusCode, String responseData) throws IOException {
+ private HttpResponse getMockResponse(int statusCode, String responseData)
+ throws IOException {
StatusLine statusLine = new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), statusCode, null);
HttpResponse response = new BasicHttpResponse(statusLine);
BasicHttpEntity entity = new BasicHttpEntity();
@@ -59,14 +87,6 @@ private HttpResponse getMockResponse(int statusCode, String responseData) throws
return response;
}
- public void setMessageError(String messageError) {
- this.messageError = messageError;
- }
-
- public void setResponseError(String responseError) {
- this.responseError = responseError;
- }
-
@Override
public HttpResponse execute(HttpUriRequest request, HttpContext context)
throws IOException, ClientProtocolException {
@@ -87,27 +107,27 @@ public T execute(HttpUriRequest arg0, ResponseHandler extends T> arg1)
@Override
public HttpResponse execute(HttpHost target, HttpRequest request,
- HttpContext context) throws IOException, ClientProtocolException {
+ HttpContext context) throws IOException, ClientProtocolException {
return null;
}
@Override
public T execute(HttpUriRequest arg0,
- ResponseHandler extends T> arg1, HttpContext arg2)
+ ResponseHandler extends T> arg1, HttpContext arg2)
throws IOException, ClientProtocolException {
return null;
}
@Override
public T execute(HttpHost arg0, HttpRequest arg1,
- ResponseHandler extends T> arg2) throws IOException,
+ ResponseHandler extends T> arg2) throws IOException,
ClientProtocolException {
return null;
}
@Override
public T execute(HttpHost arg0, HttpRequest arg1,
- ResponseHandler extends T> arg2, HttpContext arg3)
+ ResponseHandler extends T> arg2, HttpContext arg3)
throws IOException, ClientProtocolException {
return null;
}
diff --git a/Adjust/test/src/com/adjust/sdk/test/MockLogger.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockLogger.java
similarity index 59%
rename from Adjust/test/src/com/adjust/sdk/test/MockLogger.java
rename to Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockLogger.java
index cde1c2565..85ba41bd6 100644
--- a/Adjust/test/src/com/adjust/sdk/test/MockLogger.java
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockLogger.java
@@ -1,20 +1,27 @@
package com.adjust.sdk.test;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.adjust.sdk.ILogger;
+import com.adjust.sdk.LogLevel;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.adjust.sdk.Logger;
+import static com.adjust.sdk.Constants.LOGTAG;
-public class MockLogger implements Logger {
+public class MockLogger implements ILogger {
private StringBuffer logBuffer;
private SparseArray> logMap;
public MockLogger() {
+ reset();
+ }
+
+ public void reset() {
logBuffer = new StringBuffer();
logMap = new SparseArray>(7);
logMap.put(LogLevel.ASSERT.getAndroidLogLevel(), new ArrayList());
@@ -25,6 +32,8 @@ public MockLogger() {
logMap.put(LogLevel.WARN.getAndroidLogLevel(), new ArrayList());
// logging test level == 1
logMap.put(1, new ArrayList());
+
+ test("Logger reset");
}
@Override
@@ -36,54 +45,71 @@ public String toString() {
@Override
public void setLogLevel(LogLevel logLevel) {
-
+ test("MockLogger setLogLevel: " + logLevel);
}
@Override
public void setLogLevelString(String logLevelString) {
-
}
- private void logMessage(String message, Integer iLoglevel, String messagePrefix) {
+ private void logMessage(String message, Integer iLoglevel, String messagePrefix, int priority) {
logBuffer.append(messagePrefix + message + System.getProperty("line.separator"));
- Log.d(messagePrefix, message);
+ Log.println(priority, LOGTAG, messagePrefix + message);
List prefixedList = logMap.get(iLoglevel);
prefixedList.add(message);
}
@Override
- public void verbose(String message, Object ...parameters) {
- logMessage(String.format(message, parameters), LogLevel.VERBOSE.getAndroidLogLevel(), "v ");
+ public void verbose(String message, Object... parameters) {
+ logMessage(String.format(message, parameters),
+ LogLevel.VERBOSE.getAndroidLogLevel(),
+ "v ",
+ Log.VERBOSE);
}
@Override
- public void debug(String message, Object ...parameters) {
- logMessage(String.format(message, parameters), LogLevel.DEBUG.getAndroidLogLevel(), "d ");
+ public void debug(String message, Object... parameters) {
+ logMessage(String.format(message, parameters),
+ LogLevel.DEBUG.getAndroidLogLevel(),
+ "d ",
+ Log.DEBUG);
}
@Override
- public void info(String message, Object ...parameters) {
- logMessage(String.format(message, parameters), LogLevel.INFO.getAndroidLogLevel(), "i ");
+ public void info(String message, Object... parameters) {
+ logMessage(String.format(message, parameters),
+ LogLevel.INFO.getAndroidLogLevel(),
+ "i ",
+ Log.INFO);
}
@Override
- public void warn(String message, Object ...parameters) {
- logMessage(String.format(message, parameters), LogLevel.WARN.getAndroidLogLevel(), "w ");
+ public void warn(String message, Object... parameters) {
+ logMessage(String.format(message, parameters),
+ LogLevel.WARN.getAndroidLogLevel(),
+ "w ",
+ Log.WARN);
}
@Override
- public void error(String message, Object ...parameters) {
- logMessage(String.format(message, parameters), LogLevel.ERROR.getAndroidLogLevel(), "e ");
+ public void error(String message, Object... parameters) {
+ logMessage(String.format(message, parameters),
+ LogLevel.ERROR.getAndroidLogLevel(),
+ "e ",
+ Log.ERROR);
}
@Override
- public void Assert(String message, Object ...parameters) {
- logMessage(String.format(message, parameters), LogLevel.ASSERT.getAndroidLogLevel(), "a ");
+ public void Assert(String message, Object... parameters) {
+ logMessage(String.format(message, parameters),
+ LogLevel.ASSERT.getAndroidLogLevel(),
+ "a ",
+ Log.ASSERT);
}
public void test(String message) {
- logMessage(message, 1, "t ");
+ logMessage(message, 1, "t ", Log.VERBOSE);
}
private Boolean mapContainsMessage(int level, String beginsWith) {
diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockPackageHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockPackageHandler.java
new file mode 100644
index 000000000..064c2ec88
--- /dev/null
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockPackageHandler.java
@@ -0,0 +1,87 @@
+package com.adjust.sdk.test;
+
+import android.content.Context;
+
+import com.adjust.sdk.ActivityPackage;
+import com.adjust.sdk.IActivityHandler;
+import com.adjust.sdk.IPackageHandler;
+
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MockPackageHandler implements IPackageHandler {
+ private MockLogger testLogger;
+ private String prefix = "PackageHandler ";
+ IActivityHandler activityHandler;
+ JSONObject jsonResponse;
+ List queue;
+ Context context;
+
+ public MockPackageHandler(MockLogger testLogger) {
+ this.testLogger = testLogger;
+ queue = new ArrayList();
+ }
+
+ @Override
+ public void init(IActivityHandler activityHandler, Context context, boolean startPaused) {
+ testLogger.test(prefix + "init, startPaused: " + startPaused);
+ this.activityHandler = activityHandler;
+ this.context = context;
+ }
+
+ @Override
+ public void addPackage(ActivityPackage pack) {
+ testLogger.test(prefix + "addPackage");
+ queue.add(pack);
+ }
+
+ @Override
+ public void sendFirstPackage() {
+ testLogger.test(prefix + "sendFirstPackage");
+ /*
+ if (activityHandler != null) {
+ activityHandler.finishedTrackingActivity(jsonResponse);
+ }
+ */
+ }
+
+ @Override
+ public void sendNextPackage() {
+ testLogger.test(prefix + "sendNextPackage");
+ }
+
+ @Override
+ public void closeFirstPackage() {
+ testLogger.test(prefix + "closeFirstPackage");
+ }
+
+ @Override
+ public void pauseSending() {
+ testLogger.test(prefix + "pauseSending");
+ }
+
+ @Override
+ public void resumeSending() {
+ testLogger.test(prefix + "resumeSending");
+ }
+
+ @Override
+ public String getFailureMessage() {
+ testLogger.test(prefix + "getFailureMessage");
+ return "Mock Failure Message.";
+ }
+
+ @Override
+ public void finishedTrackingActivity(JSONObject jsonResponse) {
+ testLogger.test(prefix + "finishedTrackingActivity, " + jsonResponse);
+ this.jsonResponse = jsonResponse;
+ }
+
+ @Override
+ public void sendClickPackage(ActivityPackage clickPackage) {
+ testLogger.test(prefix + "sendClickPackage");
+ queue.add(clickPackage);
+ }
+}
diff --git a/Adjust/test/src/com/adjust/sdk/test/MockRequestHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockRequestHandler.java
similarity index 69%
rename from Adjust/test/src/com/adjust/sdk/test/MockRequestHandler.java
rename to Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockRequestHandler.java
index 9deca3b73..418065483 100644
--- a/Adjust/test/src/com/adjust/sdk/test/MockRequestHandler.java
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockRequestHandler.java
@@ -5,21 +5,25 @@
import com.adjust.sdk.IRequestHandler;
public class MockRequestHandler implements IRequestHandler {
-
private MockLogger testLogger;
private String prefix = "RequestHandler ";
- private IPackageHandler packageHandler;
- private Boolean errorNextSend;
+ IPackageHandler packageHandler;
public MockRequestHandler(MockLogger testLogger) {
this.testLogger = testLogger;
- this.errorNextSend = false;
+ }
+
+ @Override
+ public void init(IPackageHandler packageHandler) {
+ testLogger.test(prefix + "init");
+ this.packageHandler = packageHandler;
}
@Override
public void sendPackage(ActivityPackage pack) {
- testLogger.test(prefix + "sendPackage");
+ testLogger.test(prefix + "sendPackage, " + pack);
+ /*
// respond successfully to the package handler
if (packageHandler != null && !errorNextSend) {
packageHandler.sendNextPackage();
@@ -29,14 +33,11 @@ public void sendPackage(ActivityPackage pack) {
testLogger.test(packageHandler.getFailureMessage());
packageHandler.closeFirstPackage();
}
+ */
}
- public void setPackageHandler(IPackageHandler packageHandler) {
- this.packageHandler = packageHandler;
- }
-
- public void setErrorNextSend(Boolean errorNextSend) {
- this.errorNextSend = errorNextSend;
+ @Override
+ public void sendClickPackage(ActivityPackage clickPackage) {
+ testLogger.test(prefix + "sendClickPackage, " + clickPackage);
}
-
}
diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/ResponseType.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/ResponseType.java
new file mode 100644
index 000000000..ed8dc9cce
--- /dev/null
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/ResponseType.java
@@ -0,0 +1,8 @@
+package com.adjust.sdk.test;
+
+/**
+ * Created by pfms on 28/01/15.
+ */
+public enum ResponseType {
+ NULL, CLIENT_PROTOCOL_EXCEPTION, INTERNAL_SERVER_ERROR, WRONG_JSON, EMPTY_JSON, MESSAGE, ATTRIBUTION, ASK_IN;
+}
diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestActivityHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestActivityHandler.java
new file mode 100644
index 000000000..df6a1d3d0
--- /dev/null
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestActivityHandler.java
@@ -0,0 +1,1625 @@
+package com.adjust.sdk.test;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.mock.MockContext;
+
+import com.adjust.sdk.ActivityHandler;
+import com.adjust.sdk.ActivityPackage;
+import com.adjust.sdk.AdjustAttribution;
+import com.adjust.sdk.AdjustConfig;
+import com.adjust.sdk.AdjustEvent;
+import com.adjust.sdk.AdjustFactory;
+import com.adjust.sdk.Constants;
+import com.adjust.sdk.LogLevel;
+import com.adjust.sdk.OnAttributionChangedListener;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+
+public class TestActivityHandler extends ActivityInstrumentationTestCase2 {
+ protected MockLogger mockLogger;
+ protected MockPackageHandler mockPackageHandler;
+ protected MockAttributionHandler mockAttributionHandler;
+ protected UnitTestActivity activity;
+ protected Context context;
+ protected AssertUtil assertUtil;
+
+ public TestActivityHandler() {
+ super(UnitTestActivity.class);
+ }
+
+ public TestActivityHandler(Class mainActivity) {
+ super(mainActivity);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mockLogger = new MockLogger();
+ mockPackageHandler = new MockPackageHandler(mockLogger);
+ mockAttributionHandler = new MockAttributionHandler(mockLogger);
+ assertUtil = new AssertUtil(mockLogger);
+
+ AdjustFactory.setLogger(mockLogger);
+ AdjustFactory.setPackageHandler(mockPackageHandler);
+ AdjustFactory.setAttributionHandler(mockAttributionHandler);
+
+ activity = getActivity();
+ context = activity.getApplicationContext();
+
+ // deleting the activity state file to simulate a first session
+ boolean activityStateDeleted = ActivityHandler.deleteActivityState(context);
+ boolean attributionDeleted = ActivityHandler.deleteAttribution(context);
+
+ mockLogger.test("Was AdjustActivityState deleted? " + activityStateDeleted);
+
+ // deleting the attribution file to simulate a first session
+ mockLogger.test("Was Attribution deleted? " + attributionDeleted);
+
+ // check the server url
+ assertEquals(Constants.BASE_URL, "https://app.adjust.com");
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ AdjustFactory.setPackageHandler(null);
+ AdjustFactory.setAttributionHandler(null);
+ AdjustFactory.setLogger(null);
+ AdjustFactory.setTimerInterval(-1);
+ AdjustFactory.setTimerStart(-1);
+ AdjustFactory.setSessionInterval(-1);
+ AdjustFactory.setSubsessionInterval(-1);
+
+ activity = null;
+ context = null;
+ }
+
+ public void testFirstSession() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestActivityHandler testFirstSession");
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ // start activity handler with config
+ ActivityHandler activityHandler = ActivityHandler.getInstance(config);
+
+ SystemClock.sleep(3000);
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "INFO", false);
+
+ // test first session start
+ firstSessionStartTests(false, false);
+
+ // checking the default values of the first session package
+ // should only have one package
+ assertEquals(1, mockPackageHandler.queue.size());
+
+ ActivityPackage activityPackage = mockPackageHandler.queue.get(0);
+
+ // create activity package test
+ TestActivityPackage testActivityPackage = new TestActivityPackage(activityPackage);
+
+ // set first session
+ testActivityPackage.testSessionPackage(1);
+ }
+
+ public void testEventsBuffered() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestActivityHandler testEventsBuffered");
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ // buffer events
+ config.setEventBufferingEnabled(true);
+
+ // set verbose log level
+ config.setLogLevel(LogLevel.VERBOSE);
+
+ // start activity handler with config
+ ActivityHandler activityHandler = ActivityHandler.getInstance(config);
+
+ SystemClock.sleep(3000);
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "VERBOSE", true);
+
+ // test first session start
+ firstSessionStartTests(false, false);
+
+ // create the first Event
+ AdjustEvent firstEvent = new AdjustEvent("event1");
+
+ // add callback parameters
+ firstEvent.addCallbackParameter("keyCall", "valueCall");
+ firstEvent.addCallbackParameter("keyCall", "valueCall2");
+ firstEvent.addCallbackParameter("fooCall", "barCall");
+
+ // add partner paramters
+ firstEvent.addPartnerParameter("keyPartner", "valuePartner");
+ firstEvent.addPartnerParameter("keyPartner", "valuePartner2");
+ firstEvent.addPartnerParameter("fooPartner", "barPartner");
+
+ // add revenue
+ firstEvent.setRevenue(0.001, "EUR");
+
+ // track event
+ activityHandler.trackEvent(firstEvent);
+
+ // create the second Event
+ AdjustEvent secondEvent = new AdjustEvent("event2");
+
+ // add empty revenue
+ secondEvent.setRevenue(0, "USD");
+
+ // track second event
+ activityHandler.trackEvent(secondEvent);
+
+ // create third Event
+ AdjustEvent thirdEvent = new AdjustEvent("event3");
+
+ // track third event
+ activityHandler.trackEvent(thirdEvent);
+
+ SystemClock.sleep(3000);
+
+ // test first event
+ // check that callback parameter was overwritten
+ assertUtil.warn("key keyCall was overwritten");
+
+ // check that partner parameter was overwritten
+ assertUtil.warn("key keyPartner was overwritten");
+
+ // check that event package was added
+ assertUtil.test("PackageHandler addPackage");
+
+ // check that event was buffered
+ assertUtil.info("Buffered event (0.0010 EUR, 'event1')");
+
+ // and not sent to package handler
+ assertUtil.notInTest("PackageHandler sendFirstPackage");
+
+ // after tracking the event it should write the activity state
+ assertUtil.debug("Wrote Activity state");
+
+ // test second event
+ // check that event package was added
+ assertUtil.test("PackageHandler addPackage");
+
+ // check that event was buffered
+ assertUtil.info("Buffered event (0.0000 USD, 'event2')");
+
+ // and not sent to package handler
+ assertUtil.notInTest("PackageHandler sendFirstPackage");
+
+ // after tracking the event it should write the activity state
+ assertUtil.debug("Wrote Activity state");
+
+ // test third event
+ // check that event package was added
+ assertUtil.test("PackageHandler addPackage");
+
+ // check that event was buffered
+ assertUtil.info("Buffered event 'event3'");
+
+ // and not sent to package handler
+ assertUtil.notInTest("PackageHandler sendFirstPackage");
+
+ // after tracking the event it should write the activity state
+ assertUtil.debug("Wrote Activity state");
+
+ // check the number of activity packages
+ // 1 session + 3 events
+ assertEquals(4, mockPackageHandler.queue.size());
+
+ ActivityPackage firstSessionPackage = mockPackageHandler.queue.get(0);
+
+ // create activity package test
+ TestActivityPackage testFirstSessionPackage = new TestActivityPackage(firstSessionPackage);
+
+ // set first session
+ testFirstSessionPackage.testSessionPackage(1);
+
+ // first event
+ ActivityPackage firstEventPackage = mockPackageHandler.queue.get(1);
+
+ // create event package test
+ TestActivityPackage testFirstEventPackage = new TestActivityPackage(firstEventPackage);
+
+ // set event test parameters
+ testFirstEventPackage.eventCount = "1";
+ testFirstEventPackage.suffix = " (0.0010 EUR, 'event1')";
+ testFirstEventPackage.revenueString = "0.00100";
+ testFirstEventPackage.currency = "EUR";
+ testFirstEventPackage.callbackParams = "{\"keyCall\":\"valueCall2\",\"fooCall\":\"barCall\"}";
+ testFirstEventPackage.partnerParams = "{\"keyPartner\":\"valuePartner2\",\"fooPartner\":\"barPartner\"}";
+
+ // test first event
+ testFirstEventPackage.testEventPackage("event1");
+
+ // second event
+ ActivityPackage secondEventPackage = mockPackageHandler.queue.get(2);
+
+ // create event package test
+ TestActivityPackage testSecondEventPackage = new TestActivityPackage(secondEventPackage);
+
+ // set event test parameters
+ testSecondEventPackage.eventCount = "2";
+ testSecondEventPackage.suffix = " (0.0000 USD, 'event2')";
+ testSecondEventPackage.revenueString = "0.00000";
+ testSecondEventPackage.currency = "USD";
+
+ // test second event
+ testSecondEventPackage.testEventPackage("event2");
+
+ // third event
+ ActivityPackage thirdEventPackage = mockPackageHandler.queue.get(3);
+
+ // create event package test
+ TestActivityPackage testThirdEventPackage = new TestActivityPackage(thirdEventPackage);
+
+ // set event test parameters
+ testThirdEventPackage.eventCount = "3";
+ testThirdEventPackage.suffix = " 'event3'";
+
+ // test third event
+ testThirdEventPackage.testEventPackage("event3");
+ }
+
+ public void testEventsNotBuffered() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestActivityHandler testEventsNotBuffered");
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ // set log level
+ config.setLogLevel(LogLevel.DEBUG);
+
+ // start activity handler with config
+ ActivityHandler activityHandler = ActivityHandler.getInstance(config);
+
+ SystemClock.sleep(3000);
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "DEBUG", false);
+
+ // test first session start
+ firstSessionStartTests(false, false);
+
+ // create the first Event
+ AdjustEvent firstEvent = new AdjustEvent("event1");
+
+ // track event
+ activityHandler.trackEvent(firstEvent);
+
+ SystemClock.sleep(2000);
+
+ // check that event package was added
+ assertUtil.test("PackageHandler addPackage");
+
+ // check that event was sent to package handler
+ assertUtil.test("PackageHandler sendFirstPackage");
+
+ // and not buffered
+ assertUtil.notInInfo("Buffered event");
+
+ // after tracking the event it should write the activity state
+ assertUtil.debug("Wrote Activity state");
+ }
+
+ public void testChecks() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestActivityHandler testChecks");
+
+ // config with null app token
+ AdjustConfig nullAppTokenConfig = new AdjustConfig(context, null, AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ assertUtil.error("Missing App Token.");
+ assertUtil.isFalse(nullAppTokenConfig.isValid());
+
+ // config with wrong size app token
+ AdjustConfig oversizeAppTokenConfig = new AdjustConfig(context, "1234567890123", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ assertUtil.error("Malformed App Token '1234567890123'");
+ assertUtil.isFalse(oversizeAppTokenConfig.isValid());
+
+ // config with null environment
+ AdjustConfig nullEnvironmentConfig = new AdjustConfig(context, "123456789012", null);
+
+ assertUtil.error("Missing environment");
+ assertUtil.isFalse(nullEnvironmentConfig.isValid());
+
+ // config with wrong environment
+ AdjustConfig wrongEnvironmentConfig = new AdjustConfig(context, "123456789012", "Other");
+
+ assertUtil.error("Unknown environment 'Other'");
+ assertUtil.isFalse(wrongEnvironmentConfig.isValid());
+
+ // config with null context
+ AdjustConfig nullContextConfig = new AdjustConfig(null, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ assertUtil.error("Missing context");
+ assertUtil.isFalse(nullContextConfig.isValid());
+
+ // config without internet permission
+ Context mockContext = new MockContext() {
+ @Override
+ public int checkCallingOrSelfPermission(String permission) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ };
+ AdjustConfig mockContextConfig = new AdjustConfig(mockContext, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ assertUtil.error("Missing permission: INTERNET");
+ assertUtil.isFalse(mockContextConfig.isValid());
+
+ // config without access wifi state permission
+ // TODO
+
+ // start with null config
+ ActivityHandler nullConfigactivityHandler = ActivityHandler.getInstance(null);
+
+ assertUtil.error("AdjustConfig missing");
+ assertUtil.isNull(nullConfigactivityHandler);
+
+ ActivityHandler invalidConfigactivityHandler = ActivityHandler.getInstance(nullAppTokenConfig);
+
+ assertUtil.error("AdjustConfig not initialized correctly");
+ assertUtil.isNull(invalidConfigactivityHandler);
+
+ // event with null event token
+ AdjustEvent nullEventToken = new AdjustEvent(null);
+
+ assertUtil.error("Missing Event Token");
+ assertUtil.isFalse(nullEventToken.isValid());
+
+ // event with wrong size
+ AdjustEvent wrongEventTokenSize = new AdjustEvent("eventXX");
+
+ assertUtil.error("Malformed Event Token 'eventXX'");
+ assertUtil.isFalse(wrongEventTokenSize.isValid());
+
+ // event
+ AdjustEvent event = new AdjustEvent("event1");
+
+ // event with negative revenue
+ event.setRevenue(-0.001, "EUR");
+
+ assertUtil.error("Invalid amount -0.001");
+
+ // event with null currency
+ event.setRevenue(0, null);
+
+ assertUtil.error("Currency must be set with revenue");
+
+ // event with empty currency
+ event.setRevenue(0, "");
+
+ assertUtil.error("Currency is empty");
+
+ // callback parameter null key
+ event.addCallbackParameter(null, "callValue");
+
+ assertUtil.error("Callback parameter key is missing");
+
+ // callback parameter empty key
+ event.addCallbackParameter("", "callValue");
+
+ assertUtil.error("Callback parameter key is empty");
+
+ // callback parameter null value
+ event.addCallbackParameter("keyCall", null);
+
+ assertUtil.error("Callback parameter value is missing");
+
+ // callback parameter empty value
+ event.addCallbackParameter("keyCall", "");
+
+ assertUtil.error("Callback parameter value is empty");
+
+ // partner parameter null key
+ event.addPartnerParameter(null, "callValue");
+
+ assertUtil.error("Partner parameter key is missing");
+
+ // partner parameter empty key
+ event.addPartnerParameter("", "callValue");
+
+ assertUtil.error("Partner parameter key is empty");
+
+ // partner parameter null value
+ event.addPartnerParameter("keyCall", null);
+
+ assertUtil.error("Partner parameter value is missing");
+
+ // partner parameter empty value
+ event.addPartnerParameter("keyCall", "");
+
+ assertUtil.error("Partner parameter value is empty");
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ // set the log level
+ config.setLogLevel(LogLevel.WARN);
+
+ // create handler and start the first session
+ ActivityHandler activityHandler = ActivityHandler.getInstance(config);
+
+ SystemClock.sleep(3000);
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "WARN", false);
+
+ // test first session start
+ firstSessionStartTests(false, false);
+
+ // track null event
+ activityHandler.trackEvent(null);
+ SystemClock.sleep(1000);
+
+ assertUtil.error("Event missing");
+
+ activityHandler.trackEvent(nullEventToken);
+ SystemClock.sleep(1000);
+
+ assertUtil.error("Event not initialized correctly");
+ }
+
+ public void testSessions() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestActivityHandler testSessions");
+
+ // adjust the session intervals for testing
+ AdjustFactory.setSessionInterval(4000);
+ AdjustFactory.setSubsessionInterval(1000);
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ // start activity handler with config
+ ActivityHandler activityHandler = ActivityHandler.getInstance(config);
+
+ SystemClock.sleep(3000);
+
+ // trigger a new sub session session
+ activityHandler.trackSubsessionStart();
+
+ SystemClock.sleep(5000);
+
+ // trigger a new session
+ activityHandler.trackSubsessionStart();
+
+ SystemClock.sleep(1000);
+
+ // end the session
+ activityHandler.trackSubsessionEnd();
+
+ SystemClock.sleep(1000);
+
+ // set verbose log level
+ config.setLogLevel(LogLevel.INFO);
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "INFO", false);
+
+ // test first session start
+ firstSessionStartTests(false, false);
+
+ // test the new sub session
+ assertUtil.test("PackageHandler resumeSending");
+
+ // save activity state
+ assertUtil.debug("Wrote Activity state: ec:0 sc:1 ssc:2");
+
+ // test the subsession message
+ assertUtil.info("Started subsession 2 of session 1");
+
+ // test the new timer
+ timerFiredTest();
+
+ // save activity state
+ assertUtil.debug("Wrote Activity state: ec:0 sc:1 ssc:2");
+
+ // new session
+ startSessionTest(false, false);
+
+ // test the new subsession
+ assertUtil.debug("Wrote Activity state: ec:0 sc:2 ssc:1");
+
+ // test the new timer
+ timerFiredTest();
+
+ // save activity state
+ assertUtil.debug("Wrote Activity state: ec:0 sc:2 ssc:1");
+
+ // pause sending
+ assertUtil.test("PackageHandler pauseSending");
+
+ // save activity state
+ assertUtil.debug("Wrote Activity state: ec:0 sc:2 ssc:1");
+
+ // 2 session packages
+ assertEquals(2, mockPackageHandler.queue.size());
+
+ ActivityPackage firstSessionActivityPackage = mockPackageHandler.queue.get(0);
+
+ // create activity package test
+ TestActivityPackage testFirstSessionActivityPackage = new TestActivityPackage(firstSessionActivityPackage);
+
+ // test first session
+ testFirstSessionActivityPackage.testSessionPackage(1);
+
+ // get second session package
+ ActivityPackage secondSessionActivityPackage = mockPackageHandler.queue.get(1);
+
+ // create second session test package
+ TestActivityPackage testSecondSessionActivityPackage = new TestActivityPackage(secondSessionActivityPackage);
+
+ // check if it saved the second subsession in the new package
+ testSecondSessionActivityPackage.subsessionCount = 2;
+
+ // test second session
+ testSecondSessionActivityPackage.testSessionPackage(2);
+ }
+
+ public void testDisable() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestActivityHandler testDisable");
+
+ // adjust the session intervals for testing
+ AdjustFactory.setSessionInterval(4000);
+ AdjustFactory.setSubsessionInterval(1000);
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ // set log level
+ config.setLogLevel(LogLevel.ERROR);
+
+ // start activity handler with config
+ ActivityHandler activityHandler = ActivityHandler.getInstance(config);
+
+ // check that is true by default
+ assertUtil.isTrue(activityHandler.isEnabled());
+
+ // disable sdk
+ activityHandler.setEnabled(false);
+
+ // check that it is disabled
+ assertUtil.isFalse(activityHandler.isEnabled());
+
+ // check if message the disable of the SDK
+ assertUtil.info("Pausing package handler and attribution handler to disable the SDK");
+
+ // it's necessary to sleep the activity for a while after each handler call
+ // to let the internal queue act
+ SystemClock.sleep(2000);
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "ERROR", false);
+
+ // test first session start without attribution handler
+ firstSessionStartWithoutTimerTests(false, true);
+
+ // try to do activities while SDK disabled
+ activityHandler.trackSubsessionStart();
+ activityHandler.trackEvent(new AdjustEvent("event1"));
+
+ SystemClock.sleep(3000);
+
+ // check that timer was not executed
+ assertUtil.notInTest("PackageHandler sendFirstPackage");
+
+ // but that it was paused
+ assertUtil.test("PackageHandler pauseSending");
+ assertUtil.debug("Wrote Activity state: ec:0 sc:1 ssc:1");
+
+ // check that it did not resume
+ assertUtil.notInTest("PackageHandler resumeSending");
+
+ // check that it did not wrote activity state from new session or subsession
+ assertUtil.notInDebug("Wrote Activity state");
+
+ // or that started the timer
+ assertUtil.notInTest("PackageHandler sendFirstPackage");
+
+ // check that it did not add any event package
+ assertUtil.notInTest("PackageHandler addPackage");
+
+ // only the first session package should be sent
+ assertEquals(1, mockPackageHandler.queue.size());
+
+ // put in offline mode
+ activityHandler.setOfflineMode(true);
+
+ // pausing due to offline mode
+ assertUtil.info("Pausing package and attribution handler to put in offline mode");
+
+ // wait to update status
+ SystemClock.sleep(1000);
+
+ // update attribution handler to paused
+ assertUtil.test("AttributionHandler pauseSending");
+
+ // update package handler to paused
+ assertUtil.test("PackageHandler pauseSending");
+
+ // re-enable the SDK
+ activityHandler.setEnabled(true);
+
+ // check that it is enabled
+ assertUtil.isTrue(activityHandler.isEnabled());
+
+ // check message of SDK still paused
+ assertUtil.info("Package and attribution handler remain paused due to the SDK is offline");
+
+ SystemClock.sleep(6000);
+
+ startSessionTest(true, true);
+
+ // check that it wrote the second session
+ assertUtil.debug("Wrote Activity state: ec:0 sc:2 ssc:1");
+
+ // and that it fired the timer
+ timerFiredTest();
+ assertUtil.debug("Wrote Activity state: ec:0 sc:2 ssc:1");
+
+ // track an event
+ activityHandler.trackEvent(new AdjustEvent("event1"));
+
+ SystemClock.sleep(1000);
+
+ // check that it did add the event package
+ assertUtil.test("PackageHandler addPackage");
+
+ // and send it
+ assertUtil.test("PackageHandler sendFirstPackage");
+
+ // it should have the second session and the event
+ assertEquals(3, mockPackageHandler.queue.size());
+
+ ActivityPackage secondSessionPackage = mockPackageHandler.queue.get(1);
+
+ // create activity package test
+ TestActivityPackage testSecondSessionPackage = new TestActivityPackage(secondSessionPackage);
+
+ // set the sub sessions
+ testSecondSessionPackage.subsessionCount = 1;
+
+ // test third session
+ testSecondSessionPackage.testSessionPackage(2);
+
+ ActivityPackage eventPackage = mockPackageHandler.queue.get(2);
+
+ // create activity package test
+ TestActivityPackage testEventPackage = new TestActivityPackage(eventPackage);
+
+ testEventPackage.suffix = " 'event1'";
+
+ // test event
+ testEventPackage.testEventPackage("event1");
+
+ // put in online mode
+ activityHandler.setOfflineMode(false);
+
+ // message that is finally resuming
+ assertUtil.info("Resuming package handler and attribution handler to put in online mode");
+
+ SystemClock.sleep(1000);
+
+ // check status update
+ assertUtil.test("AttributionHandler resumeSending");
+ assertUtil.test("PackageHandler resumeSending");
+
+ // track sub session
+ activityHandler.trackSubsessionStart();
+
+ SystemClock.sleep(1000);
+
+ // test sub session not paused
+ startSessionTest(true, false);
+
+ // after sending the first package saves the activity state
+ assertUtil.debug("Wrote Activity state: ec:1 sc:3 ssc:1");
+
+ timerFiredTest();
+
+ // save activity state
+ assertUtil.debug("Wrote Activity state: ec:1 sc:3 ssc:1");
+ }
+
+ public void testOpenUrl() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestActivityHandler testOpenUrl");
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ // set log level
+ config.setLogLevel(LogLevel.ASSERT);
+
+ // start activity handler with config
+ ActivityHandler activityHandler = ActivityHandler.getInstance(config);
+
+ SystemClock.sleep(3000);
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "ASSERT", false);
+
+ // test first session start
+ firstSessionStartTests(false, false);
+
+ Uri attributions = Uri.parse("AdjustTests://example.com/path/inApp?adjust_tracker=trackerValue&other=stuff&adjust_campaign=campaignValue&adjust_adgroup=adgroupValue&adjust_creative=creativeValue");
+ Uri extraParams = Uri.parse("AdjustTests://example.com/path/inApp?adjust_foo=bar&other=stuff&adjust_key=value");
+ Uri mixed = Uri.parse("AdjustTests://example.com/path/inApp?adjust_foo=bar&other=stuff&adjust_campaign=campaignValue&adjust_adgroup=adgroupValue&adjust_creative=creativeValue");
+ Uri emptyQueryString = Uri.parse("AdjustTests://");
+ Uri emptyString = Uri.parse("");
+ Uri nullUri = null;
+ Uri single = Uri.parse("AdjustTests://example.com/path/inApp?adjust_foo");
+ Uri prefix = Uri.parse("AdjustTests://example.com/path/inApp?adjust_=bar");
+ Uri incomplete = Uri.parse("AdjustTests://example.com/path/inApp?adjust_foo=");
+
+ long now = System.currentTimeMillis();
+
+ activityHandler.readOpenUrl(attributions, now);
+ activityHandler.readOpenUrl(extraParams, now);
+ activityHandler.readOpenUrl(mixed, now);
+ activityHandler.readOpenUrl(emptyQueryString, now);
+ activityHandler.readOpenUrl(emptyString, now);
+ activityHandler.readOpenUrl(nullUri, now);
+ activityHandler.readOpenUrl(single, now);
+ activityHandler.readOpenUrl(prefix, now);
+ activityHandler.readOpenUrl(incomplete, now);
+
+ SystemClock.sleep(1000);
+
+ // three click packages: attributions, extraParams and mixed
+ for (int i = 3; i > 0; i--) {
+ assertUtil.test("AttributionHandler getAttribution");
+ assertUtil.test("PackageHandler sendClickPackage");
+ }
+
+ // check that it did not send any other click package
+ assertUtil.notInTest("AttributionHandler getAttribution");
+ assertUtil.notInTest("PackageHandler sendClickPackage");
+
+ // checking the default values of the first session package
+ // 1 session + 3 click
+ assertEquals(4, mockPackageHandler.queue.size());
+
+ // get the click package
+ ActivityPackage attributionClickPackage = mockPackageHandler.queue.get(1);
+
+ // create activity package test
+ TestActivityPackage testAttributionClickPackage = new TestActivityPackage(attributionClickPackage);
+
+ // create the attribution
+ AdjustAttribution firstAttribution = new AdjustAttribution();
+ firstAttribution.trackerName = "trackerValue";
+ firstAttribution.campaign = "campaignValue";
+ firstAttribution.adgroup = "adgroupValue";
+ firstAttribution.creative = "creativeValue";
+
+ // and set it
+ testAttributionClickPackage.attribution = firstAttribution;
+
+ // test the first deeplink
+ testAttributionClickPackage.testClickPackage("deeplink");
+
+ // get the click package
+ ActivityPackage extraParamsClickPackage = mockPackageHandler.queue.get(2);
+
+ // create activity package test
+ TestActivityPackage testExtraParamsClickPackage = new TestActivityPackage(extraParamsClickPackage);
+
+ // other deep link parameters
+ testExtraParamsClickPackage.deepLinkParameters = "{\"key\":\"value\",\"foo\":\"bar\"}";
+
+ // test the second deeplink
+ testExtraParamsClickPackage.testClickPackage("deeplink");
+
+ // get the click package
+ ActivityPackage mixedClickPackage = mockPackageHandler.queue.get(3);
+
+ // create activity package test
+ TestActivityPackage testMixedClickPackage = new TestActivityPackage(mixedClickPackage);
+
+ // create the attribution
+ AdjustAttribution secondAttribution = new AdjustAttribution();
+ secondAttribution.campaign = "campaignValue";
+ secondAttribution.adgroup = "adgroupValue";
+ secondAttribution.creative = "creativeValue";
+
+ // and set it
+ testMixedClickPackage.attribution = secondAttribution;
+
+ // other deep link parameters
+ testMixedClickPackage.deepLinkParameters = "{\"foo\":\"bar\"}";
+
+ // test the third deeplink
+ testMixedClickPackage.testClickPackage("deeplink");
+ }
+
+ public void testFinishedTrackingActivity() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestActivityHandler testFinishedTrackingActivity");
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_PRODUCTION);
+
+ // set verbose log level
+ config.setLogLevel(LogLevel.VERBOSE);
+
+ config.setOnAttributionChangedListener(new OnAttributionChangedListener() {
+ @Override
+ public void onAttributionChanged(AdjustAttribution attribution) {
+ mockLogger.test("onAttributionChanged: " + attribution);
+ }
+ });
+
+ // start activity handler with config
+ ActivityHandler activityHandler = ActivityHandler.getInstance(config);
+
+ SystemClock.sleep(3000);
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_PRODUCTION, "ASSERT", false);
+
+ // test first session start
+ firstSessionStartTests(false, false);
+
+ JSONObject responseNull = null;
+
+ activityHandler.finishedTrackingActivity(responseNull);
+ SystemClock.sleep(1000);
+
+ // if the response is null
+ assertUtil.notInTest("AttributionHandler checkAttribution");
+ assertUtil.notInError("Unable to open deep link");
+ assertUtil.notInInfo("Open deep link");
+
+ // set package handler to respond with a valid attribution
+ JSONObject wrongDeeplinkResponse = null;
+ try {
+ wrongDeeplinkResponse = new JSONObject("{ " +
+ "\"deeplink\" : \"wrongDeeplink://\" }");
+ } catch (JSONException e) {
+ fail(e.getMessage());
+ }
+
+ activityHandler.finishedTrackingActivity(wrongDeeplinkResponse);
+ SystemClock.sleep(1000);
+
+ // check that it was unable to open the url
+ assertUtil.error("Unable to open deep link (wrongDeeplink://)");
+
+ // and it check the attribution
+ assertUtil.test("AttributionHandler checkAttribution");
+ // TODO add test that opens url
+
+ // checking the default values of the first session package
+ // should only have one package
+ assertEquals(1, mockPackageHandler.queue.size());
+
+ ActivityPackage activityPackage = mockPackageHandler.queue.get(0);
+
+ // create activity package test
+ TestActivityPackage testActivityPackage = new TestActivityPackage(activityPackage);
+
+ testActivityPackage.needsAttributionData = true;
+ testActivityPackage.environment = AdjustConfig.ENVIRONMENT_PRODUCTION;
+
+ // set first session
+ testActivityPackage.testSessionPackage(1);
+ }
+
+ public void testUpdateAttribution() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestActivityHandler testUpdateAttribution");
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ // start activity handler with config
+ ActivityHandler firstActivityHandler = ActivityHandler.getInstance(config);
+
+ SystemClock.sleep(3000);
+
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "INFO", false);
+
+ // test first session start
+ firstSessionStartTests(false, false);
+
+ JSONObject nullJsonObject = null;
+ AdjustAttribution nullAttribution = AdjustAttribution.fromJson(nullJsonObject);
+
+ // check if Attribution wasn't built
+ assertUtil.isNull(nullAttribution);
+
+ // check that it does not update a null attribution
+ assertUtil.isFalse(firstActivityHandler.tryUpdateAttribution(nullAttribution));
+
+ // create an empty attribution
+ JSONObject emptyJsonResponse = null;
+ try {
+ emptyJsonResponse = new JSONObject("{ }");
+ } catch (JSONException e) {
+ fail(e.getMessage());
+ }
+ AdjustAttribution emptyAttribution = AdjustAttribution.fromJson(emptyJsonResponse);
+
+ // check that updates attribution
+ assertUtil.isTrue(firstActivityHandler.tryUpdateAttribution(emptyAttribution));
+ assertUtil.debug("Wrote Attribution: tt:null tn:null net:null cam:null adg:null cre:null");
+
+ emptyAttribution = AdjustAttribution.fromJson(emptyJsonResponse);
+
+ // check that it does not update the attribution
+ assertUtil.isFalse(firstActivityHandler.tryUpdateAttribution(emptyAttribution));
+ assertUtil.notInDebug("Wrote Attribution");
+
+ // end session
+ firstActivityHandler.trackSubsessionEnd();
+ SystemClock.sleep(1000);
+
+ endSessionTest();
+
+ config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ config.setOnAttributionChangedListener(new OnAttributionChangedListener() {
+ @Override
+ public void onAttributionChanged(AdjustAttribution attribution) {
+ mockLogger.test("onAttributionChanged: " + attribution);
+ }
+ });
+
+ ActivityHandler restartActivityHandler = ActivityHandler.getInstance(config);
+
+ SystemClock.sleep(3000);
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "INFO", false);
+
+ firstSessionSubsessionsTest(2);
+
+ // check that it does not update the attribution after the restart
+ assertUtil.isFalse(restartActivityHandler.tryUpdateAttribution(emptyAttribution));
+ assertUtil.notInDebug("Wrote Attribution");
+
+ // new attribution
+ JSONObject firstAttributionJson = null;
+ try {
+ firstAttributionJson = new JSONObject("{ " +
+ "\"tracker_token\" : \"ttValue\" , " +
+ "\"tracker_name\" : \"tnValue\" , " +
+ "\"network\" : \"nValue\" , " +
+ "\"campaign\" : \"cpValue\" , " +
+ "\"adgroup\" : \"aValue\" , " +
+ "\"creative\" : \"ctValue\" }");
+ } catch (JSONException e) {
+ fail(e.getMessage());
+ }
+ AdjustAttribution firstAttribution = AdjustAttribution.fromJson(firstAttributionJson);
+
+ //check that it updates
+ assertUtil.isTrue(restartActivityHandler.tryUpdateAttribution(firstAttribution));
+ assertUtil.debug("Wrote Attribution: tt:ttValue tn:tnValue net:nValue cam:cpValue adg:aValue cre:ctValue");
+
+ // check that it launch the saved attribute
+ SystemClock.sleep(1000);
+
+ assertUtil.test("onAttributionChanged: tt:ttValue tn:tnValue net:nValue cam:cpValue adg:aValue cre:ctValue");
+
+ // check that it does not update the attribution
+ assertUtil.isFalse(restartActivityHandler.tryUpdateAttribution(firstAttribution));
+ assertUtil.notInDebug("Wrote Attribution");
+
+ // end session
+ restartActivityHandler.trackSubsessionEnd();
+ SystemClock.sleep(1000);
+
+ endSessionTest();
+
+ config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ config.setOnAttributionChangedListener(new OnAttributionChangedListener() {
+ @Override
+ public void onAttributionChanged(AdjustAttribution attribution) {
+ mockLogger.test("onAttributionChanged: " + attribution);
+ }
+ });
+
+ ActivityHandler secondRestartActivityHandler = ActivityHandler.getInstance(config);
+
+ SystemClock.sleep(3000);
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "INFO", false);
+
+ firstSessionSubsessionsTest(3);
+
+ // check that it does not update the attribution after the restart
+ assertUtil.isFalse(secondRestartActivityHandler.tryUpdateAttribution(firstAttribution));
+ assertUtil.notInDebug("Wrote Attribution");
+
+ // new attribution
+ JSONObject secondAttributionJson = null;
+ try {
+ secondAttributionJson = new JSONObject("{ " +
+ "\"tracker_token\" : \"ttValue2\" , " +
+ "\"tracker_name\" : \"tnValue2\" , " +
+ "\"network\" : \"nValue2\" , " +
+ "\"campaign\" : \"cpValue2\" , " +
+ "\"adgroup\" : \"aValue2\" , " +
+ "\"creative\" : \"ctValue2\" }");
+ } catch (JSONException e) {
+ fail(e.getMessage());
+ }
+ AdjustAttribution secondAttribution = AdjustAttribution.fromJson(secondAttributionJson);
+
+ //check that it updates
+ assertUtil.isTrue(secondRestartActivityHandler.tryUpdateAttribution(secondAttribution));
+ assertUtil.debug("Wrote Attribution: tt:ttValue2 tn:tnValue2 net:nValue2 cam:cpValue2 adg:aValue2 cre:ctValue2");
+
+ // check that it launch the saved attribute
+ SystemClock.sleep(1000);
+
+ assertUtil.test("onAttributionChanged: tt:ttValue2 tn:tnValue2 net:nValue2 cam:cpValue2 adg:aValue2 cre:ctValue2");
+
+ // check that it does not update the attribution
+ assertUtil.isFalse(secondRestartActivityHandler.tryUpdateAttribution(secondAttribution));
+ assertUtil.notInDebug("Wrote Attribution");
+ }
+
+ public void testOfflineMode() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestActivityHandler testOfflineMode");
+
+ // adjust the session intervals for testing
+ AdjustFactory.setSessionInterval(2000);
+ AdjustFactory.setSubsessionInterval(500);
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ // start activity handler with config
+ ActivityHandler activityHandler = ActivityHandler.getInstance(config);
+
+ // put SDK offline
+ activityHandler.setOfflineMode(true);
+
+ SystemClock.sleep(3000);
+
+ // check if message the disable of the SDK
+ assertUtil.info("Pausing package and attribution handler to put in offline mode");
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "INFO", false);
+
+ // test first session start
+ firstSessionStartTests(false, true);
+
+ // it didn't pause attribution handler because it wasn't lazily init
+ assertUtil.notInTest("AttributionHandler pauseSending");
+
+ // start the attribution handler by ending the session
+ activityHandler.trackSubsessionEnd();
+
+ SystemClock.sleep(1000);
+
+ assertUtil.test("PackageHandler pauseSending");
+
+ assertUtil.test("AttributionHandler init, startPaused: true");
+
+ assertUtil.test("AttributionHandler pauseSending");
+
+ // disable the SDK
+ activityHandler.setEnabled(false);
+
+ // check that it is disabled
+ assertUtil.isFalse(activityHandler.isEnabled());
+
+ // check if message the disable of the SDK
+ assertUtil.info("Pausing package handler and attribution handler to disable the SDK");
+
+ SystemClock.sleep(1000);
+
+ // test end session logs
+ endSessionTest();
+
+ // put SDK back online
+ activityHandler.setOfflineMode(false);
+
+ assertUtil.info("Package and attribution handler remain paused because the SDK is disabled");
+
+ SystemClock.sleep(1000);
+
+ // test the update status, still paused
+ assertUtil.test("AttributionHandler pauseSending");
+ assertUtil.test("PackageHandler pauseSending");
+
+ // try to do activities while SDK disabled
+ activityHandler.trackSubsessionStart();
+ activityHandler.trackEvent(new AdjustEvent("event1"));
+
+ SystemClock.sleep(3000);
+
+ // check that timer was not executed
+ assertUtil.notInTest("PackageHandler sendFirstPackage");
+
+ // check that it did not wrote activity state from new session or subsession
+ assertUtil.notInDebug("Wrote Activity state");
+
+ // check that it did not add any package
+ assertUtil.notInTest("PackageHandler addPackage");
+
+ // enable the SDK again
+ activityHandler.setEnabled(true);
+
+ // check that is enabled
+ assertUtil.isTrue(activityHandler.isEnabled());
+
+ SystemClock.sleep(1000);
+
+ // test that is not paused anymore
+ startSessionTest(true, false);
+
+ /*
+ // send new session to package handler
+ assertUtil.test("PackageHandler addPackage");
+ assertUtil.test("PackageHandler sendFirstPackage");
+ assertUtil.debug("Wrote Activity state: ec:0 sc:2");
+
+ // check that the second session package was added
+ // 2 session packages
+ assertEquals(2, mockPackageHandler.queue.size());
+
+ // get second session package
+ ActivityPackage secondSessionActivityPackage = mockPackageHandler.queue.get(1);
+
+ // create second session test package
+ TestActivityPackage testSecondSessionActivityPackage = new TestActivityPackage(secondSessionActivityPackage);
+
+ // test second session
+ testSecondSessionActivityPackage.testSessionPackage(2);
+
+ // set offline mode to false again
+ activityHandler.setOfflineMode(false);
+
+ SystemClock.sleep(1000);
+
+ // check online message
+ assertUtil.info("Resuming package handler to put in online mode");
+
+ // check that it did un-pause sending in the new session
+ assertUtil.test("PackageHandler resumeSending");
+
+ // send new session to package handler
+ assertUtil.test("PackageHandler addPackage");
+ assertUtil.test("PackageHandler sendFirstPackage");
+ assertUtil.debug("Wrote Activity state: ec:0 sc:3");
+ */
+ }
+
+ public void testSendReferrer() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestActivityHandler testSendReferrer");
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ // start activity handler with config
+ ActivityHandler activityHandler = ActivityHandler.getInstance(config);
+
+ SystemClock.sleep(3000);
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "INFO", false);
+
+ // test first session start
+ firstSessionStartTests(false, false);
+
+ long now = System.currentTimeMillis();
+
+ String reftag = "adjust_reftag=referrerValue";
+ String extraParams = "adjust_foo=bar&other=stuff&adjust_key=value";
+ String mixed = "adjust_foo=bar&other=stuff&adjust_reftag=referrerValue";
+ String empty = "";
+ String nullString = null;
+ String single = "adjust_foo";
+ String prefix = "adjust_=bar";
+ String incomplete = "adjust_foo=";
+
+ activityHandler.sendReferrer(reftag, now);
+ SystemClock.sleep(1000);
+ activityHandler.sendReferrer(extraParams, now);
+ SystemClock.sleep(1000);
+ activityHandler.sendReferrer(mixed, now);
+ SystemClock.sleep(1000);
+ activityHandler.sendReferrer(empty, now);
+ SystemClock.sleep(1000);
+ activityHandler.sendReferrer(nullString, now);
+ SystemClock.sleep(1000);
+ activityHandler.sendReferrer(single, now);
+ SystemClock.sleep(1000);
+ activityHandler.sendReferrer(prefix, now);
+ SystemClock.sleep(1000);
+ activityHandler.sendReferrer(incomplete, now);
+ SystemClock.sleep(1000);
+
+ // three click packages: reftag, extraParams and mixed
+ for (int i = 3; i > 0; i--) {
+ //assertUtil.test("AttributionHandler getAttribution");
+ assertUtil.test("PackageHandler sendClickPackage");
+ }
+
+ // check that it did not send any other click package
+ assertUtil.notInTest("PackageHandler sendClickPackage");
+
+ // checking the default values of the first session package
+ // 1 session + 3 click
+ assertEquals(4, mockPackageHandler.queue.size());
+
+ ActivityPackage reftagClickPackage = mockPackageHandler.queue.get(1);
+
+ TestActivityPackage reftagClickPackageTest = new TestActivityPackage(reftagClickPackage);
+
+ reftagClickPackageTest.reftag = "referrerValue";
+
+ reftagClickPackageTest.testClickPackage("reftag");
+
+ // get the click package
+ ActivityPackage extraParamsClickPackage = mockPackageHandler.queue.get(2);
+
+ // create activity package test
+ TestActivityPackage testExtraParamsClickPackage = new TestActivityPackage(extraParamsClickPackage);
+
+ // other deep link parameters
+ testExtraParamsClickPackage.deepLinkParameters = "{\"key\":\"value\",\"foo\":\"bar\"}";
+
+ // test the second deeplink
+ testExtraParamsClickPackage.testClickPackage("reftag");
+
+ // get the click package
+ ActivityPackage mixedClickPackage = mockPackageHandler.queue.get(3);
+
+ // create activity package test
+ TestActivityPackage testMixedClickPackage = new TestActivityPackage(mixedClickPackage);
+
+ testMixedClickPackage.reftag = "referrerValue";
+
+ // other deep link parameters
+ testMixedClickPackage.deepLinkParameters = "{\"foo\":\"bar\"}";
+
+ // test the third deeplink
+ testMixedClickPackage.testClickPackage("reftag");
+ }
+
+ public void testGetAttribution() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestActivityHandler testGetAttribution");
+
+ AdjustFactory.setTimerStart(500);
+ /***
+ * if (attribution == null || activityState.askingAttribution) {
+ * if (shouldGetAttribution) {
+ * getAttributionHandler().getAttribution();
+ * }
+ * }
+ *
+ * 9 possible states with variables
+ * attribution = null -> attrNul
+ * askingAttribution = true -> askAttr
+ * shouldGetAttribution = true -> shldGet
+ * getAttribution is called -> getAttr
+ *
+ * State Number attrNul | askAttr | shldGet -> getAttr
+ * 000->0 0 False | False | False -> False
+ * 001->0 1 False | False | True -> False
+ * 010->0 2 False | True | False -> False
+ * 011->1 3 False | True | True -> True
+ * 100->0 4 True | False | False -> False
+ * 101->1 5 True | False | True -> True
+ * 110->0 6 True | True | False -> False
+ * 111->1 7 True | True | True -> True
+ */
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ config.setOnAttributionChangedListener(new OnAttributionChangedListener() {
+ @Override
+ public void onAttributionChanged(AdjustAttribution attribution) {
+ mockLogger.test("onAttributionChanged " + attribution);
+ }
+ });
+
+ // start activity handler with config
+ ActivityHandler activityHandler = ActivityHandler.getInstance(config);
+
+ SystemClock.sleep(3000);
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "INFO", false);
+
+ // state 100->0 number 4
+ // attribution is null,
+ // askingAttribution is false by default,
+ // shouldGetAttribution is false after a session
+
+ // test first session start
+ firstSessionStartWithoutTimerTests(false, false);
+
+ timerFiredTest();
+
+ // there shouldn't be a getAttribution
+ assertUtil.notInTest("AttributionHandler getAttribution");
+
+ // save activity state
+ assertUtil.debug("Wrote Activity state: ec:0 sc:1 ssc:1");
+
+ // state 110->0 number 6
+ // attribution is null,
+ // askingAttribution is set to true,
+ // shouldGetAttribution is false after a session
+
+ // set asking attribution
+ activityHandler.setAskingAttribution(true);
+ assertUtil.debug("Wrote Activity state: ec:0 sc:1 ssc:1");
+
+ // trigger a new session
+ activityHandler.trackSubsessionStart();
+ SystemClock.sleep(2000);
+
+ subsessionGetAttributionTest(2, false);
+
+ // state 010->0 number 2
+ // attribution is set,
+ // askingAttribution is set to true,
+ // shouldGetAttribution is false after a session
+
+ JSONObject jsonAttribution = null;
+
+ try {
+ jsonAttribution = new JSONObject("{ " +
+ "\"tracker_token\" : \"ttValue\" , " +
+ "\"tracker_name\" : \"tnValue\" , " +
+ "\"network\" : \"nValue\" , " +
+ "\"campaign\" : \"cpValue\" , " +
+ "\"adgroup\" : \"aValue\" , " +
+ "\"creative\" : \"ctValue\" }");
+ } catch (JSONException e) {
+ fail(e.getMessage());
+ }
+ AdjustAttribution attribution = AdjustAttribution.fromJson(jsonAttribution);
+
+ // update the attribution
+ activityHandler.tryUpdateAttribution(attribution);
+
+ // attribution was updated
+ assertUtil.debug("Wrote Attribution: tt:ttValue tn:tnValue net:nValue cam:cpValue adg:aValue cre:ctValue");
+
+ // trigger a new sub session
+ activityHandler.trackSubsessionStart();
+ SystemClock.sleep(2000);
+
+ subsessionGetAttributionTest(3, false);
+
+ // state 000->0 number 0
+ // attribution is set,
+ // askingAttribution is set to false
+ // shouldGetAttribution is false after a session
+
+ activityHandler.setAskingAttribution(false);
+ assertUtil.debug("Wrote Activity state: ec:0 sc:1 ssc:3");
+
+ // trigger a new sub session
+ activityHandler.trackSubsessionStart();
+ SystemClock.sleep(2000);
+
+ subsessionGetAttributionTest(4, false);
+
+ // state 101->1 number 5
+ // attribution is null,
+ // askingAttribution is set to false from the previous activity handler
+ // shouldGetAttribution is true after restarting with no new session
+
+ // delete attribution to start as null
+ ActivityHandler.deleteAttribution(context);
+
+ // deleting the attribution file to simulate a first session
+ mockLogger.test("Was Attribution deleted? " + true);
+
+ // reset activity handler with previous saved activity state
+ config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ config.setOnAttributionChangedListener(new OnAttributionChangedListener() {
+ @Override
+ public void onAttributionChanged(AdjustAttribution attribution) {
+ mockLogger.test("onAttributionChanged " + attribution);
+ }
+ });
+ activityHandler = ActivityHandler.getInstance(config);
+
+ SystemClock.sleep(3000);
+
+ // test init values
+ initTests(AdjustConfig.ENVIRONMENT_SANDBOX, "INFO", false);
+
+ subsessionGetAttributionTest(5, true);
+
+ // state 111->1 number 7
+ // attribution is null,
+ // askingAttribution is set to true,
+ // shouldGetAttribution is still true
+
+ activityHandler.setAskingAttribution(true);
+ assertUtil.debug("Wrote Activity state: ec:0 sc:1 ssc:5");
+
+ // trigger a new sub session
+ activityHandler.trackSubsessionStart();
+ SystemClock.sleep(2000);
+
+ subsessionGetAttributionTest(6, true);
+
+ // state 011->1 number 3
+ // attribution is set,
+ // askingAttribution is set to true,
+ // shouldGetAttribution is still true
+
+ // update the attribution
+ activityHandler.tryUpdateAttribution(attribution);
+
+ // attribution was updated
+ assertUtil.debug("Wrote Attribution: tt:ttValue tn:tnValue net:nValue cam:cpValue adg:aValue cre:ctValue");
+
+ // trigger a new sub session
+ activityHandler.trackSubsessionStart();
+ SystemClock.sleep(2000);
+
+ subsessionGetAttributionTest(7, true);
+
+ // state 001->0 number 1
+ // attribution is set,
+ // askingAttribution is set to false,
+ // shouldGetAttribution is still true
+
+ activityHandler.setAskingAttribution(false);
+ assertUtil.debug("Wrote Activity state: ec:0 sc:1 ssc:7");
+
+ // trigger a new sub session
+ activityHandler.trackSubsessionStart();
+ SystemClock.sleep(2000);
+
+ subsessionGetAttributionTest(8, false);
+ }
+
+ private void firstSessionSubsessionsTest(int subsessionCount) {
+ subsessionGetAttributionTest(subsessionCount, null);
+ }
+
+ private void subsessionGetAttributionTest(int subsessionCount, Boolean getAttributionIsCalled) {
+ // test the new sub session
+ assertUtil.test("PackageHandler resumeSending");
+
+ // save activity state
+ assertUtil.debug("Wrote Activity state: ec:0 sc:1 ssc:" + subsessionCount);
+
+ // test the subsession message
+ assertUtil.info("Started subsession " + subsessionCount + " of session 1");
+
+ if (getAttributionIsCalled != null) {
+ if (getAttributionIsCalled) {
+ assertUtil.test("AttributionHandler getAttribution");
+ } else {
+ assertUtil.notInTest("AttributionHandler getAttribution");
+ }
+ }
+
+ // test the new timer
+ timerFiredTest();
+ }
+
+ private void initTests(String environment, String logLevel, boolean eventBuffering) {
+ // check environment level
+ if (environment == "sandbox") {
+ assertUtil.Assert("SANDBOX: Adjust is running in Sandbox mode. Use this setting for testing. Don't forget to set the environment to `production` before publishing!");
+ } else if (environment == "production") {
+ assertUtil.Assert("PRODUCTION: Adjust is running in Production mode. Use this setting only for the build that you want to publish. Set the environment to `sandbox` if you want to test your app!");
+ } else {
+ fail();
+ }
+
+ // check log level
+ assertUtil.test("MockLogger setLogLevel: " + logLevel);
+
+ // check event buffering
+ if (eventBuffering) {
+ assertUtil.info("Event buffering is enabled");
+ } else {
+ assertUtil.notInInfo("Event buffering is enabled");
+ }
+
+ // check Google play is not set
+ assertUtil.info("Unable to get Google Play Services Advertising ID at start time");
+ }
+
+ private void firstSessionStartTests(boolean attributionHandlerLaunched, boolean paused) {
+
+ firstSessionStartWithoutTimerTests(attributionHandlerLaunched, paused);
+
+ timerFiredTest();
+
+ // save activity state
+ assertUtil.debug("Wrote Activity state: ec:0 sc:1 ssc:1");
+ }
+
+ private void firstSessionStartWithoutTimerTests(boolean attributionHandlerLaunched, boolean paused) {
+ // test that the attribution file did not exist in the first run of the application
+ assertUtil.verbose("Attribution file not found");
+
+ // test that the activity state file did not exist in the first run of the application
+ assertUtil.verbose("Activity state file not found");
+
+ // test if package handler started paused
+ if (paused) {
+ assertUtil.test("PackageHandler init, startPaused: true");
+ } else {
+ assertUtil.test("PackageHandler init, startPaused: false");
+ }
+
+ startSessionTest(attributionHandlerLaunched, paused);
+
+ // after sending the first package saves the activity state
+ assertUtil.debug("Wrote Activity state: ec:0 sc:1 ssc:1 sl:0.0 ts:0.0");
+ }
+
+ private void startSessionTest(boolean attributionHandlerLaunched, boolean paused) {
+ // when a session package is being sent the attribution handler should resume sending
+ if (!attributionHandlerLaunched) {
+ //assertUtil.notInTest("AttributionHandler resumeSending");
+ //assertUtil.notInTest("AttributionHandler pauseSending");
+ } else if (!paused) {
+ assertUtil.test("AttributionHandler resumeSending");
+ } else {
+ assertUtil.test("AttributionHandler pauseSending");
+ }
+
+ // when a session package is being sent the package handler should resume sending
+ if (!paused) {
+ assertUtil.test("PackageHandler resumeSending");
+ } else {
+ assertUtil.test("PackageHandler pauseSending");
+ }
+
+ // if the package was build, it was sent to the Package Handler
+ assertUtil.test("PackageHandler addPackage");
+
+ // after adding, the activity handler ping the Package handler to send the package
+ assertUtil.test("PackageHandler sendFirstPackage");
+ }
+
+ private void timerFiredTest() {
+ // timer fired
+ assertUtil.test("PackageHandler sendFirstPackage");
+ }
+
+ private void endSessionTest() {
+ assertUtil.test("PackageHandler pauseSending");
+
+ assertUtil.test("AttributionHandler pauseSending");
+ }
+}
diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestActivityPackage.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestActivityPackage.java
new file mode 100644
index 000000000..a0310c949
--- /dev/null
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestActivityPackage.java
@@ -0,0 +1,333 @@
+package com.adjust.sdk.test;
+
+import com.adjust.sdk.ActivityKind;
+import com.adjust.sdk.ActivityPackage;
+import com.adjust.sdk.AdjustAttribution;
+
+import junit.framework.Assert;
+
+import java.util.Map;
+
+/**
+ * Created by pfms on 09/01/15.
+ */
+public class TestActivityPackage {
+
+ private ActivityPackage activityPackage;
+ private Map parameters;
+ public String appToken;
+ public String environment;
+ public String clientSdk;
+ public Boolean deviceKnow;
+ public boolean needsAttributionData;
+ public boolean playServices;
+ // session
+ public Integer sessionCount;
+ public String defaultTracker;
+ public Integer subsessionCount;
+ // event
+ public String eventToken;
+ public String eventCount;
+ public String suffix;
+ public String revenueString;
+ public String currency;
+ public String callbackParams;
+ public String partnerParams;
+ // click
+ public String reftag;
+ public String deepLinkParameters;
+ public AdjustAttribution attribution;
+
+ public TestActivityPackage(ActivityPackage activityPackage) {
+ this.activityPackage = activityPackage;
+ parameters = activityPackage.getParameters();
+
+ // default values
+ appToken = "123456789012";
+ environment = "sandbox";
+ clientSdk = "android4.0.0";
+ suffix = "";
+ attribution = new AdjustAttribution();
+ }
+
+ public void testSessionPackage(int sessionCount) {
+ // set the session count
+ this.sessionCount = sessionCount;
+
+ // test default package attributes
+ testDefaultAttributes("/session", ActivityKind.SESSION, "session");
+
+ // check default parameters
+ testDefaultParameters();
+
+ // session parameters
+ // last_interval
+ if (sessionCount == 1) {
+ assertParameterNull("last_interval");
+ } else {
+ assertParameterNotNull("last_interval");
+ }
+ // default_tracker
+ assertParameterEquals("default_tracker", defaultTracker);
+ }
+
+ public void testEventPackage(String eventToken) {
+ // set the event token
+ this.eventToken = eventToken;
+
+ // test default package attributes
+ testDefaultAttributes("/event", ActivityKind.EVENT, "event");
+
+ // check default parameters
+ testDefaultParameters();
+
+ // event parameters
+ // event_count
+ if (eventCount == null) {
+ assertParameterNotNull("event_count");
+ } else {
+ assertParameterEquals("event_count", eventCount);
+ }
+ // event_token
+ assertParameterEquals("event_token", eventToken);
+ // revenue and currency must come together
+ if (parameters.get("revenue") != null &&
+ parameters.get("currency") == null) {
+ assertFail();
+ }
+ if (parameters.get("revenue") == null &&
+ parameters.get("currency") != null) {
+ assertFail();
+ }
+ // revenue
+ assertParameterEquals("revenue", revenueString);
+ // currency
+ assertParameterEquals("currency", currency);
+ // callback_params
+ assertParameterEquals("callback_params", callbackParams);
+ // partner_params
+ assertParameterEquals("partner_params", partnerParams);
+ }
+
+ public void testClickPackage(String source) {
+ // test default package attributes
+ testDefaultAttributes("/sdk_click", ActivityKind.CLICK, "click");
+
+ // check default parameters
+ testDefaultParameters();
+
+ // click parameters
+ // source
+ assertParameterEquals("source", source);
+
+ // referrer
+ assertParameterEquals("reftag", reftag);
+
+ // params
+ assertParameterEquals("params", deepLinkParameters);
+
+ // click_time
+ assertParameterNotNull("click_time");
+
+ // attributions
+ if (attribution != null) {
+ // tracker
+ assertParameterEquals("tracker", attribution.trackerName);
+ // campaign
+ assertParameterEquals("campaign", attribution.campaign);
+ // adgroup
+ assertParameterEquals("adgroup", attribution.adgroup);
+ // creative
+ assertParameterEquals("creative", attribution.creative);
+ }
+ }
+
+ public void testAttributionPackage() {
+ // test default package attributes
+ testDefaultAttributes("attribution", ActivityKind.ATTRIBUTION, "attribution");
+
+ testDeviceInfoIds();
+ testConfig();
+ testActivityStateIds();
+ }
+
+ private void testDefaultAttributes(String path, ActivityKind activityKind, String activityKindString) {
+ // check the Sdk version is being tested
+ assertEquals(activityPackage.getClientSdk(), clientSdk);
+ // check the path
+ assertEquals(activityPackage.getPath(), path);
+ // test activity kind
+ // check the activity kind
+ assertEquals(activityPackage.getActivityKind(), activityKind);
+ // the conversion from activity kind to String
+ assertEquals(activityPackage.getActivityKind().toString(), activityKindString);
+ // the conversion from String to activity kind
+ assertEquals(activityPackage.getActivityKind(), ActivityKind.fromString(activityKindString));
+ // test suffix
+ assertEquals(activityPackage.getSuffix(), suffix);
+ }
+
+ private void testDefaultParameters() {
+ testDeviceInfo();
+ testConfig();
+ testActivityState();
+ // created_at
+ assertParameterNotNull("created_at");
+ }
+
+ private void testDeviceInfo() {
+ testDeviceInfoIds();
+ // fb_id
+ assertParameterNotNull("fb_id");
+ // package_name
+ assertParameterNotNull("package_name");
+ // app_version
+ // device_type
+ assertParameterNotNull("device_type");
+ // device_name
+ assertParameterNotNull("device_name");
+ // device_manufacturer
+ assertParameterNotNull("device_manufacturer");
+ // os_name
+ assertParameterEquals("os_name", "android");
+ // os_version
+ assertParameterNotNull("os_version");
+ // language
+ assertParameterNotNull("language");
+ // country
+ assertParameterNotNull("country");
+ // screen_size
+ assertParameterNotNull("screen_size");
+ // screen_format
+ assertParameterNotNull("screen_format");
+ // screen_density
+ assertParameterNotNull("screen_density");
+ // display_width
+ assertParameterNotNull("display_width");
+ // display_height
+ assertParameterNotNull("display_height");
+ }
+
+ private void testDeviceInfoIds() {
+ // play services
+ if (playServices) {
+ // mac_sha1
+ assertParameterNull("mac_sha1");
+ // mac_md5
+ assertParameterNull("mac_md5");
+ } else {
+ // mac_sha1
+ assertParameterNotNull("mac_sha1");
+ // mac_md5
+ assertParameterNotNull("mac_md5");
+ }
+ // android_id
+ assertParameterNotNull("android_id");
+ }
+
+ private void testConfig() {
+ // app_token
+ assertParameterEquals("app_token", appToken);
+ // environment
+ assertParameterEquals("environment", environment);
+ // device_known
+ testParameterBoolean("device_known", deviceKnow);
+ // needs_attribution_data
+ testParameterBoolean("needs_attribution_data", needsAttributionData);
+ // play services
+ if (playServices) {
+ // gps_adid
+ assertParameterNotNull("gps_adid");
+ // tracking_enabled
+ assertParameterNotNull("tracking_enabled");
+ } else {
+ // gps_adid
+ assertParameterNull("gps_adid");
+ // tracking_enabled
+ assertParameterNull("tracking_enabled");
+ }
+ }
+
+ private void testActivityState() {
+ testActivityStateIds();
+ // session_count
+ if (sessionCount == null) {
+ assertParameterNotNull("session_count");
+ } else {
+ assertParameterEquals("session_count", sessionCount);
+ }
+ // first session
+ if (sessionCount != null && sessionCount == 1) {
+ // subsession_count
+ assertParameterNull("subsession_count");
+ // session_length
+ assertParameterNull("session_length");
+ // time_spent
+ assertParameterNull("time_spent");
+ } else {
+ // subsession_count
+ if (subsessionCount == null)
+ assertParameterNotNull("subsession_count");
+ else
+ assertParameterEquals("subsession_count", subsessionCount);
+ // session_length
+ assertParameterNotNull("session_length");
+ // time_spent
+ assertParameterNotNull("time_spent");
+ }
+ }
+
+ private void testActivityStateIds() {
+ // android_uuid
+ assertParameterNotNull("android_uuid");
+ }
+
+ private void assertParameterNotNull(String parameterName) {
+ Assert.assertNotNull(activityPackage.getExtendedString(),
+ parameters.get(parameterName));
+ }
+
+ private void assertParameterNull(String parameterName) {
+ Assert.assertNull(activityPackage.getExtendedString(),
+ parameters.get(parameterName));
+ }
+
+ private void assertParameterEquals(String parameterName, String value) {
+ if (value == null) {
+ assertParameterNull(parameterName);
+ return;
+ }
+ Assert.assertEquals(activityPackage.getExtendedString(),
+ value, parameters.get(parameterName));
+ }
+
+ private void assertParameterEquals(String parameterName, int value) {
+ Assert.assertEquals(activityPackage.getExtendedString(),
+ value, Integer.parseInt(parameters.get(parameterName)));
+ }
+
+
+ private void assertEquals(String field, String value) {
+ Assert.assertEquals(activityPackage.getExtendedString(),
+ value, field);
+ }
+
+ private void assertEquals(Object field, Object value) {
+ Assert.assertEquals(activityPackage.getExtendedString(),
+ value, field);
+ }
+
+ private void assertFail() {
+ Assert.fail(activityPackage.getExtendedString());
+ }
+
+ private void testParameterBoolean(String parameterName, Boolean value) {
+ if (value == null) {
+ assertParameterNull(parameterName);
+ } else if (value) {
+ assertParameterEquals(parameterName, "1");
+ } else {
+ assertParameterEquals(parameterName, "0");
+ }
+ }
+}
diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestAttributionHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestAttributionHandler.java
new file mode 100644
index 000000000..b56429538
--- /dev/null
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestAttributionHandler.java
@@ -0,0 +1,322 @@
+package com.adjust.sdk.test;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+
+import com.adjust.sdk.ActivityHandler;
+import com.adjust.sdk.ActivityPackage;
+import com.adjust.sdk.AdjustConfig;
+import com.adjust.sdk.AdjustFactory;
+import com.adjust.sdk.AttributionHandler;
+
+import org.apache.http.client.methods.HttpUriRequest;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Created by pfms on 28/01/15.
+ */
+public class TestAttributionHandler extends ActivityInstrumentationTestCase2 {
+ private MockLogger mockLogger;
+ private MockActivityHandler mockActivityHandler;
+ private MockHttpClient mockHttpClient;
+ private AssertUtil assertUtil;
+ private UnitTestActivity activity;
+ private Context context;
+ private ActivityPackage attributionPackage;
+
+ public TestAttributionHandler() {
+ super(UnitTestActivity.class);
+ }
+
+ public TestAttributionHandler(Class activityClass) {
+ super(activityClass);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mockLogger = new MockLogger();
+ mockActivityHandler = new MockActivityHandler(mockLogger);
+ mockHttpClient = new MockHttpClient(mockLogger);
+
+ assertUtil = new AssertUtil(mockLogger);
+
+ AdjustFactory.setLogger(mockLogger);
+ AdjustFactory.setActivityHandler(mockActivityHandler);
+ AdjustFactory.setHttpClient(mockHttpClient);
+
+ activity = getActivity();
+ context = activity.getApplicationContext();
+
+ attributionPackage = getAttributionPackage();
+ }
+
+ private ActivityPackage getAttributionPackage() {
+ MockAttributionHandler mockAttributionHandler = new MockAttributionHandler(mockLogger);
+ MockPackageHandler mockPackageHandler = new MockPackageHandler(mockLogger);
+
+ AdjustFactory.setAttributionHandler(mockAttributionHandler);
+ AdjustFactory.setPackageHandler(mockPackageHandler);
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ // start activity handler with config
+ ActivityHandler activityHandler = ActivityHandler.getInstance(config);
+ SystemClock.sleep(3000);
+
+ ActivityPackage attributionPackage = activityHandler.getAttributionPackage();
+
+ TestActivityPackage attributionPackageTest = new TestActivityPackage(attributionPackage);
+
+ attributionPackageTest.testAttributionPackage();
+
+ mockLogger.reset();
+
+ return attributionPackage;
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ AdjustFactory.setActivityHandler(null);
+ AdjustFactory.setLogger(null);
+
+ activity = null;
+ context = null;
+ }
+
+ public void testGetAttribution() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestAttributionHandler testGetAttribution");
+
+ AttributionHandler attributionHandler = new AttributionHandler(mockActivityHandler,
+ attributionPackage, false);
+
+ // test null client
+ nullClientTest(attributionHandler);
+
+ // test client exception
+ clientExceptionTest(attributionHandler);
+
+ // test wrong json response
+ wrongJsonTest(attributionHandler);
+
+ // test empty response
+ emptyJsonResponseTest(attributionHandler);
+
+ // test server error
+ serverErrorTest(attributionHandler);
+
+ // test ok response with message
+ okMessageTest(attributionHandler);
+ }
+
+ public void testCheckAttribution() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestAttributionHandler testCheckAttribution");
+
+ AttributionHandler attributionHandler = new AttributionHandler(mockActivityHandler,
+ attributionPackage, false);
+
+ // test attribution with update in activity handler
+ attributionResponseTest(attributionHandler, true);
+
+ // test attribution without update in activity handler
+ attributionResponseTest(attributionHandler, false);
+ }
+
+ public void testAskIn() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestAttributionHandler testAskIn");
+
+ AttributionHandler attributionHandler = new AttributionHandler(mockActivityHandler,
+ attributionPackage, false);
+
+ String response = "Response: { \"ask_in\" : 4000 }";
+
+ callCheckAttributionWithGet(attributionHandler, ResponseType.ASK_IN, response);
+
+ // change the response to avoid a cycle;
+ mockHttpClient.responseType = ResponseType.MESSAGE;
+
+ // check attribution was called with ask_in
+ assertUtil.notInTest("ActivityHandler tryUpdateAttribution");
+
+ // it did update to true
+ assertUtil.test("ActivityHandler setAskingAttribution, true");
+
+ // and waited to for query
+ assertUtil.debug("Waiting to query attribution in 4000 milliseconds");
+
+ SystemClock.sleep(2000);
+
+ JSONObject askInJsonResponse = null;
+ try {
+ askInJsonResponse = new JSONObject("{ \"ask_in\" : 5000 }");
+ } catch (JSONException e) {
+ fail(e.getMessage());
+ }
+
+ attributionHandler.checkAttribution(askInJsonResponse);
+
+ SystemClock.sleep(3000);
+
+ // it was been waiting for 1000 + 2000 + 3000 = 6 seconds
+ // check that the mock http client was not called because the original clock was reseted
+ assertUtil.notInTest("HttpClient execute");
+
+ // check that it was finally called after 6 seconds after the second ask_in
+ SystemClock.sleep(3000);
+
+ // it did update to true
+ assertUtil.test("ActivityHandler setAskingAttribution, true");
+
+ // and waited to for query
+ assertUtil.debug("Waiting to query attribution in 5000 milliseconds");
+
+ okMessageTestLogs();
+
+ requestTest(mockHttpClient.lastRequest);
+ }
+
+ private void nullClientTest(AttributionHandler attributionHandler) {
+ startGetAttributionTest(attributionHandler, null);
+
+ // check response was not logged
+ assertUtil.notInVerbose("Response");
+ }
+
+ private void clientExceptionTest(AttributionHandler attributionHandler) {
+ startGetAttributionTest(attributionHandler, ResponseType.CLIENT_PROTOCOL_EXCEPTION);
+
+ // check the client error
+ assertUtil.error("Failed to get attribution (testResponseError)");
+ }
+
+ private void wrongJsonTest(AttributionHandler attributionHandler) {
+ startGetAttributionTest(attributionHandler, ResponseType.WRONG_JSON);
+
+ // check that the mock http client was called
+ assertUtil.test("HttpClient execute");
+
+ assertUtil.verbose("Response: not a json response");
+
+ assertUtil.error("Failed to parse json response: not a json response (Value not of type java.lang.String cannot be converted to JSONObject)");
+ }
+
+ private void emptyJsonResponseTest(AttributionHandler attributionHandler) {
+ startGetAttributionTest(attributionHandler, ResponseType.EMPTY_JSON);
+
+ // check that the mock http client was called
+ assertUtil.test("HttpClient execute");
+
+ assertUtil.verbose("Response: { }");
+
+ assertUtil.debug("No message found");
+
+ // check attribution was called without ask_in
+ assertUtil.test("ActivityHandler tryUpdateAttribution, null");
+ }
+
+ private void serverErrorTest(AttributionHandler attributionHandler) {
+ startGetAttributionTest(attributionHandler, ResponseType.INTERNAL_SERVER_ERROR);
+
+ // check that the mock http client was called
+ assertUtil.test("HttpClient execute");
+
+ // the response logged
+ assertUtil.verbose("Response: { \"message\": \"testResponseError\"}");
+
+ // the message in the response
+ assertUtil.error("testResponseError");
+
+ // check attribution was called without ask_in
+ assertUtil.test("ActivityHandler tryUpdateAttribution, null");
+
+ assertUtil.test("ActivityHandler setAskingAttribution, false");
+ }
+
+ private void okMessageTest(AttributionHandler attributionHandler) {
+ startGetAttributionTest(attributionHandler, ResponseType.MESSAGE);
+
+ okMessageTestLogs();
+ }
+
+ private void okMessageTestLogs() {
+ // check that the mock http client was called
+ assertUtil.test("HttpClient execute");
+
+ // the response logged
+ assertUtil.verbose("Response: { \"message\" : \"response OK\"}");
+
+ // the message in the response
+ assertUtil.debug("response OK");
+
+ // check attribution was called without ask_in
+ assertUtil.test("ActivityHandler tryUpdateAttribution, null");
+ }
+
+ private void callCheckAttributionWithGet(AttributionHandler attributionHandler,
+ ResponseType responseType,
+ String response) {
+ startGetAttributionTest(attributionHandler, responseType);
+
+ // check that the mock http client was called
+ assertUtil.test("HttpClient execute");
+
+ // the response logged
+ assertUtil.verbose(response);
+ }
+
+ private void attributionResponseTest(AttributionHandler attributionHandler, boolean updated) {
+ String response = "Response: { \"attribution\" : " +
+ "{\"tracker_token\" : \"ttValue\" , " +
+ "\"tracker_name\" : \"tnValue\" , " +
+ "\"network\" : \"nValue\" , " +
+ "\"campaign\" : \"cpValue\" , " +
+ "\"adgroup\" : \"aValue\" , " +
+ "\"creative\" : \"ctValue\" } }";
+
+ mockActivityHandler.updated = updated;
+
+ callCheckAttributionWithGet(attributionHandler, ResponseType.ATTRIBUTION, response);
+
+ // check attribution was called without ask_in
+ assertUtil.test("ActivityHandler tryUpdateAttribution, tt:ttValue tn:tnValue net:nValue cam:cpValue adg:aValue cre:ctValue");
+
+ // updated set askingAttribution to false
+ assertUtil.test("ActivityHandler setAskingAttribution, false");
+
+ // it did not update to true
+ assertUtil.notInTest("ActivityHandler setAskingAttribution, true");
+
+ // and waiting for query
+ assertUtil.notInDebug("Waiting to query attribution");
+ }
+
+ private void startGetAttributionTest(AttributionHandler attributionHandler, ResponseType responseType) {
+ mockHttpClient.responseType = responseType;
+
+ attributionHandler.getAttribution();
+
+ SystemClock.sleep(1000);
+
+ requestTest(mockHttpClient.lastRequest);
+ }
+
+ private void requestTest(HttpUriRequest request) {
+ if (request == null) return;
+
+ java.net.URI uri = request.getURI();
+
+ assertUtil.isEqual("https", uri.getScheme());
+
+ assertUtil.isEqual("app.adjust.com", uri.getAuthority());
+
+ assertUtil.isEqual("GET", request.getMethod());
+ }
+}
\ No newline at end of file
diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestPackageHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestPackageHandler.java
new file mode 100644
index 000000000..da75ea4ac
--- /dev/null
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestPackageHandler.java
@@ -0,0 +1,286 @@
+package com.adjust.sdk.test;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+
+import com.adjust.sdk.ActivityKind;
+import com.adjust.sdk.ActivityPackage;
+import com.adjust.sdk.AdjustFactory;
+import com.adjust.sdk.PackageHandler;
+
+/**
+ * Created by pfms on 30/01/15.
+ */
+public class TestPackageHandler extends ActivityInstrumentationTestCase2 {
+ private MockLogger mockLogger;
+ private MockActivityHandler mockActivityHandler;
+ protected MockRequestHandler mockRequestHandler;
+ private AssertUtil assertUtil;
+ private UnitTestActivity activity;
+ private Context context;
+
+
+ public TestPackageHandler() {
+ super(UnitTestActivity.class);
+ }
+
+ public TestPackageHandler(Class activityClass) {
+ super(activityClass);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mockLogger = new MockLogger();
+ mockActivityHandler = new MockActivityHandler(mockLogger);
+ mockRequestHandler = new MockRequestHandler(mockLogger);
+
+ assertUtil = new AssertUtil(mockLogger);
+
+ AdjustFactory.setLogger(mockLogger);
+ AdjustFactory.setRequestHandler(mockRequestHandler);
+
+ activity = getActivity();
+ context = activity.getApplicationContext();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ AdjustFactory.setRequestHandler(null);
+ AdjustFactory.setLogger(null);
+ }
+
+ public void testAddPackage() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestPackageHandler testAddPackage");
+
+ addFirstPackageTest();
+ addSecondPackageTest(null);
+ }
+
+ public void testSendFirst() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestPackageHandler testSendFirst");
+
+ PackageHandler packageHandler = startPackageHandler();
+
+ packageHandler.sendFirstPackage();
+ SystemClock.sleep(1000);
+
+ sendFirstTests(SendFirstState.EMPTY_QUEUE, null);
+
+ addAndSendFirstPackageTest(packageHandler);
+
+ // try to send when it is still sending
+ packageHandler.sendFirstPackage();
+ SystemClock.sleep(1000);
+
+ sendFirstTests(SendFirstState.IS_SENDING, null);
+
+ // try to send paused
+ packageHandler.pauseSending();
+ packageHandler.sendFirstPackage();
+ SystemClock.sleep(1000);
+
+ sendFirstTests(SendFirstState.PAUSED, null);
+
+ // unpause, it's still sending
+ packageHandler.resumeSending();
+ packageHandler.sendFirstPackage();
+ SystemClock.sleep(1000);
+
+ sendFirstTests(SendFirstState.IS_SENDING, null);
+
+ // verify that both paused and isSending are reset with a new session
+ PackageHandler secondSessionPackageHandler = new PackageHandler(mockActivityHandler, context, false);
+
+ secondSessionPackageHandler.sendFirstPackage();
+ SystemClock.sleep(1000);
+
+ // send the package to request handler
+ sendFirstTests(SendFirstState.SEND, "unknownFirstPackage");
+ }
+
+ public void testSendNext() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestPackageHandler testSendNext");
+
+ // init package handler
+ PackageHandler packageHandler = startPackageHandler();
+
+ // add and send the first package
+ addAndSendFirstPackageTest(packageHandler);
+
+ // try to send when it is still sending
+ packageHandler.sendFirstPackage();
+ SystemClock.sleep(1000);
+
+ sendFirstTests(SendFirstState.IS_SENDING, null);
+
+ // add a second package
+ addSecondPackageTest(packageHandler);
+
+ //send next package
+ packageHandler.sendNextPackage();
+ SystemClock.sleep(2000);
+
+ assertUtil.debug("Package handler wrote 1 packages");
+
+ // try to send the second package
+ sendFirstTests(SendFirstState.SEND, "unknownSecondPackage");
+ }
+
+ public void testCloseFirstPackage() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestPackageHandler testCloseFirstPackage");
+
+ PackageHandler packageHandler = startPackageHandler();
+
+ addAndSendFirstPackageTest(packageHandler);
+
+ // try to send when it is still sending
+ packageHandler.sendFirstPackage();
+ SystemClock.sleep(1000);
+
+ sendFirstTests(SendFirstState.IS_SENDING, null);
+
+ //send next package
+ packageHandler.closeFirstPackage();
+ SystemClock.sleep(2000);
+
+ assertUtil.notInDebug("Package handler wrote");
+
+ packageHandler.sendFirstPackage();
+ SystemClock.sleep(2000);
+
+ // try to send the first package again
+ sendFirstTests(SendFirstState.SEND, "unknownFirstPackage");
+ }
+
+ public void testCalls() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestPackageHandler testCalls");
+
+ PackageHandler packageHandler = startPackageHandler();
+
+ assertUtil.isEqual("Will retry later.", packageHandler.getFailureMessage());
+
+ packageHandler.finishedTrackingActivity(null);
+
+ assertUtil.test("ActivityHandler finishedTrackingActivity, null");
+
+ packageHandler.sendClickPackage(createUnknowPackage(""));
+
+ assertUtil.test("RequestHandler sendClickPackage, unknown");
+ }
+
+ private PackageHandler startPackageHandler() {
+ // delete package queue for fresh start
+ deletePackageQueue();
+
+ PackageHandler packageHandler = new PackageHandler(mockActivityHandler, context, false);
+
+ SystemClock.sleep(1000);
+
+ assertUtil.verbose("Package queue file not found");
+
+ return packageHandler;
+ }
+
+ private PackageHandler addFirstPackageTest() {
+ PackageHandler packageHandler = startPackageHandler();
+
+ ActivityPackage firstActivityPackage = createUnknowPackage("FirstPackage");
+
+ packageHandler.addPackage(firstActivityPackage);
+
+ SystemClock.sleep(1000);
+
+ addPackageTests(1, "unknownFirstPackage");
+
+ return packageHandler;
+ }
+
+ private PackageHandler addSecondPackageTest(PackageHandler packageHandler) {
+ if (packageHandler == null) {
+ packageHandler = new PackageHandler(mockActivityHandler, context, false);
+
+ SystemClock.sleep(1000);
+
+ // check that it can read the previously saved package
+ assertUtil.debug("Package handler read 1 packages");
+ }
+
+ ActivityPackage secondActivityPackage = createUnknowPackage("SecondPackage");
+
+ packageHandler.addPackage(secondActivityPackage);
+
+ SystemClock.sleep(1000);
+
+ addPackageTests(2, "unknownSecondPackage");
+
+ return packageHandler;
+ }
+
+ private void addPackageTests(int packageNumber, String packageString) {
+ assertUtil.debug("Added package " + packageNumber + " (" + packageString + ")");
+
+ assertUtil.debug("Package handler wrote " + packageNumber + " packages");
+ }
+
+ private void addAndSendFirstPackageTest(PackageHandler packageHandler) {
+ // add a package
+ ActivityPackage activityPackage = createUnknowPackage("FirstPackage");
+
+ // send the first package
+ packageHandler.addPackage(activityPackage);
+
+ packageHandler.sendFirstPackage();
+ SystemClock.sleep(2000);
+
+ addPackageTests(1, "unknownFirstPackage");
+
+ sendFirstTests(SendFirstState.SEND, "unknownFirstPackage");
+ }
+
+ private enum SendFirstState {
+ EMPTY_QUEUE, PAUSED, IS_SENDING, SEND
+ }
+
+ private void sendFirstTests(SendFirstState sendFirstState, String packageString) {
+ if (sendFirstState == SendFirstState.PAUSED) {
+ assertUtil.debug("Package handler is paused");
+ } else {
+ assertUtil.notInDebug("Package handler is paused");
+ }
+
+ if (sendFirstState == SendFirstState.IS_SENDING) {
+ assertUtil.verbose("Package handler is already sending");
+ } else {
+ assertUtil.notInVerbose("Package handler is already sending");
+ }
+
+ if (sendFirstState == SendFirstState.SEND) {
+ assertUtil.test("RequestHandler sendPackage, " + packageString);
+ } else {
+ assertUtil.notInTest("RequestHandler sendPackage");
+ }
+ }
+
+ private void deletePackageQueue() {
+ boolean packageQueueDeleted = PackageHandler.deletePackageQueue(context);
+
+ mockLogger.test("Was PackageQueue deleted? " + packageQueueDeleted);
+ }
+
+ private ActivityPackage createUnknowPackage(String suffix) {
+ ActivityPackage activityPackage = new ActivityPackage();
+ activityPackage.setActivityKind(ActivityKind.UNKNOWN);
+ activityPackage.setSuffix(suffix);
+
+ return activityPackage;
+ }
+}
diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestRequestHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestRequestHandler.java
new file mode 100644
index 000000000..19077a036
--- /dev/null
+++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestRequestHandler.java
@@ -0,0 +1,265 @@
+package com.adjust.sdk.test;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+
+import com.adjust.sdk.ActivityHandler;
+import com.adjust.sdk.ActivityPackage;
+import com.adjust.sdk.AdjustConfig;
+import com.adjust.sdk.AdjustFactory;
+import com.adjust.sdk.RequestHandler;
+
+/**
+ * Created by pfms on 30/01/15.
+ */
+public class TestRequestHandler extends ActivityInstrumentationTestCase2 {
+ private MockLogger mockLogger;
+ private MockPackageHandler mockPackageHandler;
+ private MockHttpClient mockHttpClient;
+ private AssertUtil assertUtil;
+ private UnitTestActivity activity;
+ private Context context;
+
+ private ActivityPackage sessionPackage;
+ private RequestHandler requestHandler;
+
+ public TestRequestHandler() {
+ super(UnitTestActivity.class);
+ }
+
+ public TestRequestHandler(Class activityClass) {
+ super(activityClass);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mockLogger = new MockLogger();
+ mockPackageHandler = new MockPackageHandler(mockLogger);
+ mockHttpClient = new MockHttpClient(mockLogger);
+
+ assertUtil = new AssertUtil(mockLogger);
+
+ AdjustFactory.setLogger(mockLogger);
+ AdjustFactory.setPackageHandler(mockPackageHandler);
+ AdjustFactory.setHttpClient(mockHttpClient);
+
+ activity = getActivity();
+ context = activity.getApplicationContext();
+
+ sessionPackage = getSessionPackage();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ AdjustFactory.setPackageHandler(null);
+ AdjustFactory.setLogger(null);
+ }
+
+ public void testSend() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestRequestHandler testSend");
+
+ requestHandler = new RequestHandler(mockPackageHandler);
+
+ sendPackageOrClickTest(false);
+
+ sendPackageOrClickTest(true);
+ }
+
+ /*
+ public void testTimeout() {
+ // assert test name to read better in logcat
+ mockLogger.Assert("TestRequestHandler testTimeout");
+
+ //mockHttpClient.timeout = true;
+
+ AdjustFactory.setHttpClient(null);
+
+ requestHandler = new RequestHandler(mockPackageHandler);
+
+ requestHandler.sendPackage(sessionPackage);
+
+ SystemClock.sleep(5000);
+
+ requestHandler.sendClickPackage(sessionPackage);
+
+ SystemClock.sleep(5000);
+ }
+ */
+
+ private void sendPackageOrClickTest(boolean sendClick) {
+ nullResponseTest(sendClick);
+
+ clientExceptionTest(sendClick);
+
+ serverErrorTest(sendClick);
+
+ wrongJsonTest(sendClick);
+
+ emptyJsonTest(sendClick);
+
+ messageTest(sendClick);
+
+ attributionTest(sendClick);
+ }
+
+ private void nullResponseTest(boolean sendClick) {
+ mockHttpClient.responseType = null;
+
+ sendPackageOrClick(sendClick);
+
+ assertUtil.test("HttpClient execute, responseType: null");
+
+ closeFirstPackage(sendClick);
+ }
+
+ private void clientExceptionTest(boolean sendClick) {
+ mockHttpClient.responseType = ResponseType.CLIENT_PROTOCOL_EXCEPTION;
+
+ sendPackageOrClick(sendClick);
+
+ assertUtil.test("HttpClient execute, responseType: CLIENT_PROTOCOL_EXCEPTION");
+
+ assertUtil.test("PackageHandler getFailureMessage");
+
+ assertUtil.error("Failed to track session. (Client protocol error: org.apache.http.client.ClientProtocolException: testResponseError) Mock Failure Message.");
+
+ closeFirstPackage(sendClick);
+ }
+
+ private void serverErrorTest(boolean sendClick) {
+ mockHttpClient.responseType = ResponseType.INTERNAL_SERVER_ERROR;
+
+ sendPackageOrClick(sendClick);
+
+ assertUtil.test("HttpClient execute, responseType: INTERNAL_SERVER_ERROR");
+
+ assertUtil.verbose("Response: { \"message\": \"testResponseError\"}");
+
+ assertUtil.error("testResponseError");
+
+ assertUtil.test("PackageHandler finishedTrackingActivity, {\"message\":\"testResponseError\"}");
+
+ sendNextPackage(sendClick);
+ }
+
+ private void wrongJsonTest(boolean sendClick) {
+ mockHttpClient.responseType = ResponseType.WRONG_JSON;
+
+ sendPackageOrClick(sendClick);
+
+ assertUtil.test("HttpClient execute, responseType: WRONG_JSON");
+
+ assertUtil.verbose("Response: not a json response");
+
+ assertUtil.error("Failed to parse json response: not a json response (Value not of type java.lang.String cannot be converted to JSONObject)");
+
+ closeFirstPackage(sendClick);
+ }
+
+ private void emptyJsonTest(boolean sendClick) {
+ mockHttpClient.responseType = ResponseType.EMPTY_JSON;
+
+ sendPackageOrClick(sendClick);
+
+ assertUtil.test("HttpClient execute, responseType: EMPTY_JSON");
+
+ assertUtil.verbose("Response: { }");
+
+ assertUtil.debug("No message found");
+
+ assertUtil.test("PackageHandler finishedTrackingActivity, {}");
+
+ sendNextPackage(sendClick);
+ }
+
+ private void messageTest(boolean sendClick) {
+ mockHttpClient.responseType = ResponseType.MESSAGE;
+
+ sendPackageOrClick(sendClick);
+
+ assertUtil.test("HttpClient execute, responseType: MESSAGE");
+
+ assertUtil.verbose("Response: { \"message\" : \"response OK\"}");
+
+ assertUtil.debug("response OK");
+
+ assertUtil.test("PackageHandler finishedTrackingActivity, {\"message\":\"response OK\"}");
+
+ sendNextPackage(sendClick);
+ }
+
+ private void attributionTest(boolean sendClick) {
+ mockHttpClient.responseType = ResponseType.ATTRIBUTION;
+
+ sendPackageOrClick(sendClick);
+
+ assertUtil.test("HttpClient execute, responseType: ATTRIBUTION");
+
+ assertUtil.verbose("Response: { \"attribution\" : {\"tracker_token\" : \"ttValue\" , \"tracker_name\" : \"tnValue\" , \"network\" : \"nValue\" , \"campaign\" : \"cpValue\" , \"adgroup\" : \"aValue\" , \"creative\" : \"ctValue\" } }");
+
+ assertUtil.debug("No message found");
+
+ assertUtil.test("PackageHandler finishedTrackingActivity, {\"attribution\":{\"tracker_token\":\"ttValue\",\"tracker_name\":\"tnValue\",\"network\":\"nValue\",\"campaign\":\"cpValue\",\"adgroup\":\"aValue\",\"creative\":\"ctValue\"}}");
+
+ sendNextPackage(sendClick);
+ }
+
+ private void sendPackageOrClick(boolean sendClick) {
+ if (sendClick) {
+ requestHandler.sendClickPackage(sessionPackage);
+ } else {
+ requestHandler.sendPackage(sessionPackage);
+ }
+ SystemClock.sleep(1000);
+ }
+
+ private void closeFirstPackage(boolean sendClick) {
+ if (sendClick) {
+ assertUtil.notInTest("PackageHandler closeFirstPackage");
+ } else {
+ assertUtil.test("PackageHandler closeFirstPackage");
+ }
+ }
+
+ private void sendNextPackage(boolean sendClick) {
+ if (sendClick) {
+ assertUtil.notInTest("PackageHandler sendNextPackage");
+ } else {
+ assertUtil.test("PackageHandler sendNextPackage");
+ }
+ }
+
+ private ActivityPackage getSessionPackage() {
+ MockAttributionHandler mockAttributionHandler = new MockAttributionHandler(mockLogger);
+
+ AdjustFactory.setAttributionHandler(mockAttributionHandler);
+
+ // deleting the activity state file to simulate a first session
+ boolean activityStateDeleted = ActivityHandler.deleteActivityState(context);
+ boolean attributionDeleted = ActivityHandler.deleteAttribution(context);
+
+ mockLogger.test("Was AdjustActivityState deleted? " + activityStateDeleted);
+
+ // deleting the attribution file to simulate a first session
+ mockLogger.test("Was Attribution deleted? " + attributionDeleted);
+
+
+ // create the config to start the session
+ AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX);
+
+ // start activity handler with config
+ ActivityHandler activityHandler = ActivityHandler.getInstance(config);
+ SystemClock.sleep(3000);
+
+ ActivityPackage sessionPackage = mockPackageHandler.queue.get(0);
+
+ mockLogger.reset();
+
+ return sessionPackage;
+ }
+}
diff --git a/Adjust/test/src/com/adjust/sdk/test/MockPackageHandler.java b/Adjust/test/src/com/adjust/sdk/test/MockPackageHandler.java
deleted file mode 100644
index e40e38a66..000000000
--- a/Adjust/test/src/com/adjust/sdk/test/MockPackageHandler.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.adjust.sdk.test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.json.JSONObject;
-
-import com.adjust.sdk.ActivityPackage;
-import com.adjust.sdk.IPackageHandler;
-import com.adjust.sdk.ResponseData;
-
-public class MockPackageHandler implements IPackageHandler {
-
- private MockLogger testLogger;
- private String prefix = "PackageHandler ";
- public List queue;
-
- public MockPackageHandler(MockLogger testLogger) {
- this.testLogger = testLogger;
- queue = new ArrayList();
- }
-
- @Override
- public void addPackage(ActivityPackage pack) {
- testLogger.test(prefix + "addPackage");
- queue.add(pack);
- }
-
- @Override
- public void sendFirstPackage() {
- testLogger.test(prefix + "sendFirstPackage");
- }
-
- @Override
- public void sendNextPackage() {
- testLogger.test(prefix + "sendNextPackage");
- }
-
- @Override
- public void closeFirstPackage() {
- testLogger.test(prefix + "closeFirstPackage");
- }
-
- @Override
- public void pauseSending() {
- testLogger.test(prefix + "pauseSending");
- }
-
- @Override
- public void resumeSending() {
- testLogger.test(prefix + "resumeSending");
- }
-
- @Override
- public String getFailureMessage() {
- testLogger.debug(prefix + "getFailureMessage");
- return "Will retry later.";
- }
-
- @Override
- public boolean dropsOfflineActivities() {
- return false; // TODO: implement?
- }
-
- @Override
- public void finishedTrackingActivity(ActivityPackage activityPackage, ResponseData responseData, JSONObject jsonResponse) {
- testLogger.test(prefix + "finishedTrackingActivity");
- testLogger.test(responseData.toString());
- }
-}
diff --git a/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java b/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java
deleted file mode 100644
index 10ca63d7e..000000000
--- a/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java
+++ /dev/null
@@ -1,678 +0,0 @@
-package com.adjust.sdk.test;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import android.content.Context;
-import android.net.Uri;
-import android.os.SystemClock;
-import android.test.ActivityInstrumentationTestCase2;
-
-import com.adjust.sdk.ActivityHandler;
-import com.adjust.sdk.ActivityKind;
-import com.adjust.sdk.ActivityPackage;
-import com.adjust.sdk.AdjustFactory;
-import com.adjust.sdk.Constants;
-import com.adjust.sdk.Logger.LogLevel;
-
-public class TestActivityHandler extends ActivityInstrumentationTestCase2 {
-
- protected MockLogger mockLogger;
- protected MockPackageHandler mockPackageHandler;
- protected UnitTestActivity activity;
-
- public TestActivityHandler(){
- super(UnitTestActivity.class);
- }
-
- public TestActivityHandler(Class mainActivity){
- super(mainActivity);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mockLogger = new MockLogger();
- mockPackageHandler = new MockPackageHandler(mockLogger);
-
- AdjustFactory.setLogger(mockLogger);
- AdjustFactory.setPackageHandler(mockPackageHandler);
-
- activity = getActivity();
- }
-
- @Override
- protected void tearDown() throws Exception{
- super.tearDown();
-
- AdjustFactory.setPackageHandler(null);
- AdjustFactory.setLogger(null);
- AdjustFactory.setTimerInterval(-1);
- AdjustFactory.setSessionInterval(-1);
- AdjustFactory.setSubsessionInterval(-1);
- }
-
- public void testFirstSession() {
- Context context = activity.getApplicationContext();
-
- // deleting the activity state file to simulate a first session
- mockLogger.test("Was AdjustActivityState deleted? " + ActivityHandler.deleteActivityState(context));
-
- ActivityHandler activityHandler = new ActivityHandler(activity);
- // start the first session
- activityHandler.trackSubsessionStart();
- // it's necessary to sleep the activity for a while after each handler call
- // to let the internal queue act
- SystemClock.sleep(1000);
-
- // test if the environment was set to Sandbox from the bundle
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ASSERT, "SANDBOX: Adjust is running in Sandbox mode"));
-
- // test that the file did not exist in the first run of the application
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.VERBOSE, "Activity state file not found"));
-
- // when a session package is being sent the package handler should resume sending
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler resumeSending"));
-
- // if the package was build, it was sent to the Package Handler
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler addPackage"));
-
- // after adding, the activity handler ping the Package handler to send the package
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler sendFirstPackage"));
-
- // checking the default values of the first session package
- // should only have one package
- assertEquals(1, mockPackageHandler.queue.size());
-
- ActivityPackage activityPackage = mockPackageHandler.queue.get(0);
-
- // check the Sdk version is being tested
- assertEquals(activityPackage.getExtendedString(),
- "android3.6.2", activityPackage.getClientSdk());
-
- // check the server url
- assertEquals(Constants.BASE_URL, "https://app.adjust.io");
-
- Map parameters = activityPackage.getParameters();
-
- // check appToken?
- // device specific attributes: setMacShortMd5, setMacSha1, setAndroidId, setUserAgent
- // how to test setFbAttributionId, defaultTracker?
-
- // session atributes
- // sessionCount 1, because is the first session
- assertEquals(activityPackage.getExtendedString(),
- 1, Integer.parseInt(parameters.get("session_count")));
- // subSessionCount -1, because we didn't had any subsessions yet
- // because only values > 0 are added to parameters, therefore is not present
- assertNull(activityPackage.getExtendedString(),
- parameters.get("subsession_count"));
- // sessionLenght -1, same as before
- assertNull(activityPackage.getExtendedString(),
- parameters.get("session_length"));
- // timeSpent -1, same as before
- assertNull(activityPackage.getExtendedString(),
- parameters.get("time_spent"));
- // createdAt
- // test diff with current now?
- // lastInterval -1, same as before
- assertNull(activityPackage.getExtendedString(),
- parameters.get("last_interval"));
- // packageType should be SESSION_START
- assertEquals(activityPackage.getExtendedString(),
- "/startup", activityPackage.getPath());
-
- // TODO: test resetSessionAttributes ?
-
- // check that the activity state is written by the first session or timer
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Wrote activity state"));
-
- // ending of first session
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.INFO, "First session"));
-
- SystemClock.sleep(1000);
- // by this time also the timer should have fired
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler sendFirstPackage"));
- }
-
- public void testSessions() {
- Context context = activity.getApplicationContext();
-
- // starting from a clean slate
- mockLogger.test("Was AdjustActivityState deleted? " + ActivityHandler.deleteActivityState(context));
-
- // adjust the intervals for testing
- AdjustFactory.setSessionInterval(2000);
- AdjustFactory.setSubsessionInterval(100);
-
- ActivityHandler activityHandler = new ActivityHandler(activity);
-
- // start the first session
- activityHandler.trackSubsessionStart();
-
- // wait enough to be a new subsession, but not a new session
- SystemClock.sleep(1500);
-
- activityHandler.trackSubsessionStart();
- // wait enough to be a new session
- SystemClock.sleep(4000);
- activityHandler.trackSubsessionStart();
- // test the subsession end
- activityHandler.trackSubsessionEnd();
- SystemClock.sleep(1000);
-
- // check that a new subsession was created
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.INFO, "Started subsession 2 of session 1"));
-
- // check that it's now on the 2nd session
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Session 2"));
-
- // check that 2 packages were added to the package handler
- assertEquals(2, mockPackageHandler.queue.size());
-
- // get the second session package and its parameters
- ActivityPackage activityPackage = mockPackageHandler.queue.get(1);
- Map parameters = activityPackage.getParameters();
-
- // the session and subsession count should be 2
- assertEquals(activityPackage.getExtendedString(),
- 2, Integer.parseInt(parameters.get("session_count")));
- assertEquals(activityPackage.getExtendedString(),
- 2, Integer.parseInt(parameters.get("subsession_count")));
-
- // TODO test updated timeSpent and sessionLenght
- mockLogger.test("timeSpent " + parameters.get("time_spent"));
- mockLogger.test("sessionLength " + parameters.get("session_length"));
-
- // check that the package handler was paused
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler pauseSending"));
- }
-
- public void testEventsBuffered() {
- Context context = activity.getApplicationContext();
-
- // starting from a clean slate
- mockLogger.test("Was AdjustActivityState deleted? " + ActivityHandler.deleteActivityState(context));
-
- ActivityHandler activityHandler = new ActivityHandler(activity, "123456789012", "sandbox", "verbose", true);
-
- // start the first session
- activityHandler.trackSubsessionStart();
-
- // construct the parameters of the the event
- Map eventParameters = new HashMap();
- eventParameters .put("key", "value");
- eventParameters .put("foo", "bar");
-
- // the first event has parameters, the second none
- activityHandler.trackEvent("abc123", eventParameters);
- activityHandler.trackRevenue(4.45, "abc123", eventParameters);
- SystemClock.sleep(2000);
-
- // check that event buffering is enabled
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.INFO, "Event buffering is enabled"));
-
- // check that the package builder added the session, event and revenue package
- assertEquals(mockLogger.toString(),
- 3, mockPackageHandler.queue.size());
-
- // check the first event
- ActivityPackage activityPackage = mockPackageHandler.queue.get(1);
- Map packageParameters = activityPackage.getParameters();
-
- // check the event count in the package parameters
- assertEquals(activityPackage.getExtendedString(),
- 1, Integer.parseInt(packageParameters.get("event_count")));
-
- // check the event token
- assertEquals(activityPackage.getExtendedString(),
- "abc123", packageParameters.get("event_token"));
-
- // check the injected parameters
- assertEquals(activityPackage.getExtendedString(),
- "eyJrZXkiOiJ2YWx1ZSIsImZvbyI6ImJhciJ9", packageParameters.get("params"));
-
- // check the event path
- assertEquals(activityPackage.getExtendedString(),
- "/event", activityPackage.getPath());
-
- // check the event suffix
- assertEquals(activityPackage.getExtendedString(),
- " 'abc123'", activityPackage.getSuffix());
-
- // check that the event was buffered
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.INFO, "Buffered event 'abc123'"));
-
- // check the event count in the written activity state
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Wrote activity state: ec:1"));
-
- // check the event count in the logger
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Event 1"));
-
- // check the second event/ first revenue
- activityPackage = mockPackageHandler.queue.get(2);
- packageParameters = activityPackage.getParameters();
-
- // check the event count in the package parameters
- assertEquals(activityPackage.getExtendedString(),
- 2, Integer.parseInt(packageParameters.get("event_count")));
-
- // check the amount, transforming cents into rounded decimal cents
- // note that the 4.45 cents ~> 45 decimal cents
- assertEquals(activityPackage.getExtendedString(),
- 45, Long.parseLong(packageParameters.get("amount")));
-
- // check the event token
- assertEquals(activityPackage.getExtendedString(),
- "abc123", packageParameters.get("event_token"));
-
- // check the injected parameters
- assertEquals(activityPackage.getExtendedString(),
- "eyJrZXkiOiJ2YWx1ZSIsImZvbyI6ImJhciJ9", packageParameters.get("params"));
-
- // check the revenue path
- assertEquals(activityPackage.getExtendedString(),
- "/revenue", activityPackage.getPath());
-
- // check the revenue suffix
- // note that the amount was rounded to the decimal cents
- assertEquals(activityPackage.getExtendedString(),
- " (4.5 cent, 'abc123')", activityPackage.getSuffix());
-
- // check that the revenue was buffered
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.INFO, "Buffered revenue (4.5 cent, 'abc123')"));
-
- // check the event count in the written activity state
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Wrote activity state: ec:2"));
-
- // check the event count in the logger
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Event 2 (revenue)"));
- }
-
- public void testEventsNotBuffered() {
- Context context = activity.getApplicationContext();
-
- // starting from a clean slate
- mockLogger.test("Was AdjustActivityState deleted? " + ActivityHandler.deleteActivityState(context));
-
- ActivityHandler activityHandler = new ActivityHandler(activity);
- // start the first session
- activityHandler.trackSubsessionStart();
-
- // construct the parameters of the the event
- Map eventParameters = new HashMap();
- eventParameters .put("key", "value");
- eventParameters .put("foo", "bar");
-
- // the first event has parameters, the second none
- activityHandler.trackEvent("abc123", null);
-
- activityHandler.trackRevenue (0, null, null);
- SystemClock.sleep(2000);
-
- // check that the package added the session, event and revenue package
- assertEquals(mockLogger.toString(),
- 3, mockPackageHandler.queue.size());
-
- // check the first event
- ActivityPackage activityPackage = mockPackageHandler.queue.get(1);
- Map packageParameters = activityPackage.getParameters();
-
- // check that it contains the information of the tracking being enabled
- assertNotNull(activityPackage.getExtendedString(),
- packageParameters.get("tracking_enabled"));
-
-
- // check the event count in the package parameters
- assertEquals(activityPackage.getExtendedString(),
- 1, Integer.parseInt(packageParameters.get("event_count")));
-
- // check the event token
- assertEquals(activityPackage.getExtendedString(),
- "abc123", packageParameters.get("event_token"));
-
- // check the that the parameters were not injected
- assertNull(activityPackage.getExtendedString(),
- packageParameters.get("params"));
-
- // check the event path
- assertEquals(activityPackage.getExtendedString(),
- "/event", activityPackage.getPath());
-
- // check the event suffix
- assertEquals(activityPackage.getExtendedString(),
- " 'abc123'", activityPackage.getSuffix());
-
- // check that the package handler was called
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler sendFirstPackage"));
-
- // check the event count in the written activity state
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Wrote activity state: ec:1"));
-
- // check the event count in the logger
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Event 1"));
-
- // check the fourth event/ second revenue
- activityPackage = mockPackageHandler.queue.get(2);
- packageParameters = activityPackage.getParameters();
-
- // check the event count in the package parameters
- assertEquals(activityPackage.getExtendedString(),
- 2, Integer.parseInt(packageParameters.get("event_count")));
-
- // check the amount, transforming cents into rounded decimal cents
- // note that the 4.45 cents ~> 45 decimal cents
- assertEquals(activityPackage.getExtendedString(),
- 0, Long.parseLong(packageParameters.get("amount")));
-
- // check that the event token is null
- assertNull(activityPackage.getExtendedString(),
- packageParameters.get("event_token"));
-
- // check the that the parameters were not injected
- assertNull(activityPackage.getExtendedString(),
- packageParameters.get("params"));
-
- // check the revenue path
- assertEquals(activityPackage.getExtendedString(),
- "/revenue", activityPackage.getPath());
-
- // check the revenue suffix
- // note that the amount was rounded to the decimal cents
- assertEquals(activityPackage.getExtendedString(),
- " (0.0 cent)", activityPackage.getSuffix());
-
- // check that the package handler was called
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler sendFirstPackage"));
-
- // check the event count in the written activity state
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Wrote activity state: ec:2"));
-
- // check the event count in the logger
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Event 2 (revenue)"));
- }
-
- public void testChecks() {
- Context context = activity.getApplicationContext();
-
- // starting from a clean slate
- mockLogger.test("Was AdjustActivityState deleted? " + ActivityHandler.deleteActivityState(context));
-
- // activity with null environment and app token
- ActivityHandler activityHandler = new ActivityHandler(activity, null, null, null, false);
- activityHandler.trackSubsessionStart();
- activityHandler.trackSubsessionEnd();
- activityHandler.trackEvent("123456", null);
- activityHandler.trackRevenue(0, null, null);
- SystemClock.sleep(1000);
-
- // check message for null environment
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ASSERT, "Missing environment"));
- // TODO test package with env UNKNOWN
-
- // check first missing app token message from init
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR, "Missing App Token."));
-
- // check second missing app token message from track session start
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR, "Missing App Token."));
-
- // check third missing app token message from track session end
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR, "Missing App Token."));
-
- // check fourth missing app token message from track event
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR, "Missing App Token."));
-
- // check fifth missing app token message from track revenue
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR, "Missing App Token."));
-
- // activity with invalid app token and environment
- activityHandler = new ActivityHandler(activity, "12345678901", "notValid", "verbose", false);
- activityHandler.trackSubsessionStart();
-
- SystemClock.sleep(1000);
-
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ASSERT, "Malformed environment 'notValid'"));
- // TODO test package with env MALFORMED
-
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR, "Malformed App Token '12345678901'"));
-
- // activity handler with production environment, invalid event and revenue
- activityHandler = new ActivityHandler(activity, "qwerty123456", "production", "verbose", false);
- activityHandler.trackSubsessionStart();
- activityHandler.trackEvent(null, null);
- activityHandler.trackRevenue(-0.1, null, null);
- activityHandler.trackEvent("12345", null);
- SystemClock.sleep(1000);
-
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ASSERT,
- "PRODUCTION: Adjust is running in Production mode. Use this setting only for the build that you want to publish. Set the environment to `sandbox` if you want to test your app!"));
-
- // check that event token can not be null for event
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR, "Missing Event Token"));
-
- // check that revenue can not be less than 0
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR, "Invalid amount -0.1"));
-
- // check the lenght of the event token
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR, "Malformed Event Token '12345'"));
-
- }
-
- public void testDisable() {
- Context context = activity.getApplicationContext();
-
- // starting from a clean slate
- mockLogger.test("Was AdjustActivityState deleted? " + ActivityHandler.deleteActivityState(context));
-
- // set the timer for a shorter time for testing
- AdjustFactory.setTimerInterval(700);
-
- ActivityHandler activityHandler = new ActivityHandler(activity, "qwerty123456", "sandbox", "verbose", false);
-
- // verify the default value, when not started
- assertTrue(activityHandler.isEnabled());
-
- activityHandler.setEnabled(false);
-
- // verify the default value, when not started
- assertFalse(activityHandler.isEnabled());
-
- // start the first session
- activityHandler.trackSubsessionStart();
- activityHandler.trackEvent("123456", null);
- activityHandler.trackRevenue(0.1, null, null);
- activityHandler.trackSubsessionEnd();
- activityHandler.trackSubsessionStart();
-
- SystemClock.sleep(1000);
-
- // verify the changed value after the activity handler is started
- assertFalse(activityHandler.isEnabled());
-
- // making sure the first session was sent
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.INFO, "First session"));
-
- // delete the first session package from the log
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler sendFirstPackage"));
-
- // making sure the timer fired did not call the package handler
- assertFalse(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler sendFirstPackage"));
-
- // test if the event was not triggered
- assertFalse(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Event 1"));
- // test if the revenue was not triggered
- assertFalse(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Event 1 (revenue)"));
-
- // verify that the application was paused
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler pauseSending"));
- // verify that it was not resumed
- assertFalse(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler resumeSending"));
-
- // enable again
- activityHandler.setEnabled(true);
-
- SystemClock.sleep(1000);
-
- // verify that the timer was able to resume sending
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler resumeSending"));
-
- activityHandler.trackEvent("123456", null);
- activityHandler.trackRevenue(0.1, null, null);
- activityHandler.trackSubsessionEnd();
- activityHandler.trackSubsessionStart();
- SystemClock.sleep(1000);
-
- // verify the changed value, when the activity state is started
- assertTrue(activityHandler.isEnabled());
-
- // test that the event was triggered
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Event 1"));
- // test that the revenue was triggered
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Event 2 (revenue)"));
-
- // verify that the application was paused
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler pauseSending"));
- // verify that it was also resumed
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler resumeSending"));
- // */
- }
-
- public void testOpenUrl() {
- Context context = activity.getApplicationContext();
-
- // starting from a clean slate
- mockLogger.test("Was AdjustActivityState deleted? " + ActivityHandler.deleteActivityState(context));
-
- ActivityHandler activityHandler = new ActivityHandler(activity);
- activityHandler.trackSubsessionStart();
-
- Uri normal = Uri.parse("AdjustTests://example.com/path/inApp?adjust_foo=bar&other=stuff&adjust_key=value");
- Uri emptyQueryString = Uri.parse("AdjustTests://");
- Uri emptyString = Uri.parse("");
- Uri nullString = null;
- Uri single = Uri.parse("AdjustTests://example.com/path/inApp?adjust_foo");
- Uri prefix = Uri.parse("AdjustTests://example.com/path/inApp?adjust_=bar");
- Uri incomplete = Uri.parse("AdjustTests://example.com/path/inApp?adjust_foo=");
-
- activityHandler.readOpenUrl(normal);
- activityHandler.readOpenUrl(emptyQueryString);
- activityHandler.readOpenUrl(emptyString);
- activityHandler.readOpenUrl(nullString);
- activityHandler.readOpenUrl(single);
- activityHandler.readOpenUrl(prefix);
- activityHandler.readOpenUrl(incomplete);
-
- SystemClock.sleep(1000);
-
- // check that all supposed packages were sent
- // 1 session + 1 reattributions
- assertEquals(2, mockPackageHandler.queue.size());
-
- // check that the normal url was parsed and sent
- ActivityPackage activityPackage = mockPackageHandler.queue.get(1);
-
- // testing the activity kind is the correct one
- ActivityKind activityKind = activityPackage.getActivityKind();
- assertEquals(activityPackage.getExtendedString(),
- ActivityKind.REATTRIBUTION, activityKind);
-
- // testing the conversion from activity kind to string
- String activityKindString = activityKind.toString();
- assertEquals(activityPackage.getExtendedString(),
- "reattribution", activityKindString);
-
- // testing the conversion from string to activity kind
- activityKind = ActivityKind.fromString(activityKindString);
- assertEquals(activityPackage.getExtendedString(),
- ActivityKind.REATTRIBUTION, activityKind);
-
- // package type should be reattribute
- assertEquals(activityPackage.getExtendedString(),
- "/reattribute", activityPackage.getPath());
-
- // suffix should be empty
- assertEquals(activityPackage.getExtendedString(),
- "", activityPackage.getSuffix());
-
- Map parameters = activityPackage.getParameters();
-
- // check that deep link parameters contains the base64 with the 2 keys
- assertEquals(activityPackage.getExtendedString(),
- "{\"foo\":\"bar\",\"key\":\"value\"}", parameters.get("deeplink_parameters"));
-
- // check that added and set both session and reattribution package
- assertTrue(mockLogger.toString(), mockLogger.containsTestMessage("PackageHandler addPackage"));
- assertTrue(mockLogger.toString(), mockLogger.containsTestMessage("PackageHandler sendFirstPackage"));
- assertTrue(mockLogger.toString(), mockLogger.containsTestMessage("PackageHandler addPackage"));
- assertTrue(mockLogger.toString(), mockLogger.containsTestMessage("PackageHandler sendFirstPackage"));
-
- // check that sent the reattribution package
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Reattribution {key=value, foo=bar}"));
- }
-
- public void testFinishedTrackingActivity() {
- Context context = activity.getApplicationContext();
-
- // starting from a clean slate
- mockLogger.test("Was AdjustActivityState deleted? " + ActivityHandler.deleteActivityState(context));
-
- ActivityHandler activityHandler = new ActivityHandler(activity);
- activityHandler.trackSubsessionStart();
-
- activityHandler.finishedTrackingActivity(null, "testFinishedTrackingActivity://");
-
- SystemClock.sleep(1000);
-
- assertTrue(mockLogger.toString(), mockLogger.containsMessage(LogLevel.ERROR, "Unable to open deep link (testFinishedTrackingActivity://)"));
-
- }
-}
diff --git a/Adjust/test/src/com/adjust/sdk/test/TestPackageHandler.java b/Adjust/test/src/com/adjust/sdk/test/TestPackageHandler.java
deleted file mode 100644
index 6382d6e00..000000000
--- a/Adjust/test/src/com/adjust/sdk/test/TestPackageHandler.java
+++ /dev/null
@@ -1,241 +0,0 @@
-package com.adjust.sdk.test;
-
-import android.content.Context;
-import android.os.SystemClock;
-import android.test.ActivityInstrumentationTestCase2;
-
-import com.adjust.sdk.ActivityPackage;
-import com.adjust.sdk.AdjustFactory;
-import com.adjust.sdk.Logger.LogLevel;
-import com.adjust.sdk.PackageBuilder;
-import com.adjust.sdk.PackageHandler;
-
-public class TestPackageHandler extends
- ActivityInstrumentationTestCase2 {
-
- protected MockLogger mockLogger;
- protected MockRequestHandler mockRequestHandler;
- protected Context context;
-
- public TestPackageHandler() {
- super(UnitTestActivity.class);
- }
-
- public TestPackageHandler(Class activityClass) {
- super(activityClass);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mockLogger = new MockLogger();
- mockRequestHandler = new MockRequestHandler(mockLogger);
-
- AdjustFactory.setLogger(mockLogger);
- AdjustFactory.setRequestHandler(mockRequestHandler);
-
- context = getActivity().getApplicationContext();
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
-
- AdjustFactory.setRequestHandler(null);
- AdjustFactory.setLogger(null);
- }
-
- public void testFirstPackage() {
- // delete previously created Package queue file to make a new queue
- mockLogger.test("Was AdjustPackageQueue deleted? " + PackageHandler.deletePackageQueue(context));
-
- // initialize Package Handler
- // TODO: create and inject activityHandler
- PackageHandler packageHandler = new PackageHandler(null, context, false);
- // it's necessary to sleep the activity for a while after each handler call
- // to let the internal queue act
- SystemClock.sleep(1000);
-
- // test that the file did not exist in the first run of the application
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.VERBOSE, "Package queue file not found"));
-
- // enable sending packages to Request Handler
- packageHandler.resumeSending();
-
- // build and add a package the queue
- PackageBuilder builder = new PackageBuilder(context);
- ActivityPackage sessionPackage = builder.buildSessionPackage();
- packageHandler.addPackage(sessionPackage);
- SystemClock.sleep(1000);
-
- // check that added first package to a previous empty queue
- //TODO add the toString of the activity package
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Added package 1 (session)"));
-
- //TODO add the verbose message
-
- // it should write the package queue with the first session package
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Package handler wrote 1 packages"));
-
- // set the package handler in the mock request handler to respond
- mockRequestHandler.setPackageHandler(packageHandler);
-
- // send the first package in the queue to the mock request handler
- packageHandler.sendFirstPackage();
- SystemClock.sleep(1000);
-
- // check that the Request Handler was called to send the package
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("RequestHandler sendPackage"));
-
- // check that the package was removed from the queue and 0 packages were written
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Package handler wrote 0 packages"));
- }
-
- public void testPause() {
- // initialize Package Handler
- // TODO: create and inject activityHandler
- PackageHandler packageHandler = new PackageHandler(null, context, false);
- SystemClock.sleep(1000);
-
- // disable sending packages to Request Handler
- packageHandler.pauseSending();
- SystemClock.sleep(1000);
-
- // build and add a package the queue
- PackageBuilder builder = new PackageBuilder(context);
- ActivityPackage sessionPackage = builder.buildSessionPackage();
- packageHandler.addPackage(sessionPackage);
- SystemClock.sleep(1000);
-
- // check that a package was added
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Added package "));
-
- // set the package handler in the mock request handler to verify if it was called
- mockRequestHandler.setPackageHandler(packageHandler);
-
- // try to send the first package in the queue to the mock request handler
- packageHandler.sendFirstPackage();
- SystemClock.sleep(1000);
-
- // check that the mock request handler was NOT called to send the package
- assertFalse(mockLogger.toString(),
- mockLogger.containsTestMessage("RequestHandler sendPackage"));
-
- // check that the package handler is paused
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Package handler is paused"));
- }
-
- public void testDropOfflineActivities() {
- // initialize Package Handler with
- // TODO: create and inject activityHandler
- PackageHandler packageHandler = new PackageHandler(null, context, true);
- SystemClock.sleep(1000);
-
- // check that it did NOT try to read the package queue file
- assertFalse(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Package handler read "));
- assertFalse(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.VERBOSE, "Package queue file not found"));
- assertFalse(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR, "Failed to"));
-
- // enable sending packages to Request Handler
- packageHandler.resumeSending();
-
- // build and add a package the queue
- PackageBuilder builder = new PackageBuilder(context);
- ActivityPackage sessionPackage = builder.buildSessionPackage();
- packageHandler.addPackage(sessionPackage);
- SystemClock.sleep(1000);
-
- // we'll add 2 packages so we can later see the second being sent
- packageHandler.addPackage(sessionPackage);
- SystemClock.sleep(1000);
-
- // check that it did NOT try to write the package queue file
- assertFalse(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Package handler wrote"));
- assertFalse(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR, "Failed to serialize packages"));
- assertFalse(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR, "Failed to write packages"));
-
- // set the mock request handler to simulate an error sending the package
- mockRequestHandler.setPackageHandler(packageHandler);
- mockRequestHandler.setErrorNextSend(true);
-
- // try to send the first package in the queue to the mock request handler
- packageHandler.sendFirstPackage();
- SystemClock.sleep(1000);
-
- // check that the Request Handler was called to send the first package
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("RequestHandler sendPackage"));
-
- // check the failure message for the package handler was called
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("Dropping offline activity"));
-
- // check that the Request Handler was called to send the second package
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("RequestHandler sendPackage"));
- }
-
- public void testMultiplePackages() {
- // delete previously created Package queue file to make a new queue
- mockLogger.test("Was AdjustPackageQueue deleted? " + PackageHandler.deletePackageQueue(context));
-
- // initialize Package Handler
- // TODO: create and inject activityHandler
- PackageHandler packageHandler = new PackageHandler(null, context, false);
- SystemClock.sleep(1000);
-
- // test that the file did not exist in the first run of the application
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.VERBOSE, "Package queue file not found"));
-
- // enable sending packages to Request Handler
- packageHandler.resumeSending();
-
- // build and add 3 packages the queue
- PackageBuilder builder = new PackageBuilder(context);
- ActivityPackage sessionPackage = builder.buildSessionPackage();
-
- packageHandler.addPackage(sessionPackage);
- packageHandler.addPackage(sessionPackage);
- packageHandler.addPackage(sessionPackage);
- SystemClock.sleep(1000);
-
- // check that added the third package to the queue and wrote to a file
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Added package 3"));
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Package handler wrote 3 packages"));
-
- // try to send two packages without closing the first
- packageHandler.sendFirstPackage();
- packageHandler.sendFirstPackage();
- SystemClock.sleep(1000);
-
- // check that the package handler was already sending one package before
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.VERBOSE, "Package handler is already sending"));
-
- // create a new package handler to simulate a new launch
- // TODO: create and inject activityHandler
- packageHandler = new PackageHandler(null, context, false);
- SystemClock.sleep(1000);
-
- // check that it reads the same 3 packages in the file
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.DEBUG, "Package handler read 3 packages"));
- }
-}
diff --git a/Adjust/test/src/com/adjust/sdk/test/TestRequestHandler.java b/Adjust/test/src/com/adjust/sdk/test/TestRequestHandler.java
deleted file mode 100644
index 842b09ed1..000000000
--- a/Adjust/test/src/com/adjust/sdk/test/TestRequestHandler.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package com.adjust.sdk.test;
-
-import android.content.Context;
-import android.os.SystemClock;
-import android.test.ActivityInstrumentationTestCase2;
-
-import com.adjust.sdk.ActivityPackage;
-import com.adjust.sdk.AdjustFactory;
-import com.adjust.sdk.Logger.LogLevel;
-import com.adjust.sdk.PackageBuilder;
-import com.adjust.sdk.RequestHandler;
-
-public class TestRequestHandler extends ActivityInstrumentationTestCase2 {
-
- protected MockLogger mockLogger;
- protected MockPackageHandler mockPackageHandler;
- protected MockHttpClient mockHttpClient;
-
- protected RequestHandler requestHandler;
- protected ActivityPackage sessionPackage;
-
- public TestRequestHandler() {
- super(UnitTestActivity.class);
- }
-
- public TestRequestHandler(Class activityClass) {
- super(activityClass);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mockLogger = new MockLogger();
- mockPackageHandler = new MockPackageHandler(mockLogger);
- mockHttpClient = new MockHttpClient(mockLogger);
-
- // inject the mocks used in the request handler
- AdjustFactory.setLogger(mockLogger);
- AdjustFactory.setHttpClient(mockHttpClient);
-
- Context context = getActivity().getApplicationContext();
-
- // inject the mock package handler to our request handler
- requestHandler = new RequestHandler(mockPackageHandler);
-
- // it's necessary to sleep the activity for a while after each handler call
- // to let the internal queue act
- SystemClock.sleep(1000);
-
- // build a default session package
- PackageBuilder builder = new PackageBuilder(context);
- sessionPackage = builder.buildSessionPackage();
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
-
- AdjustFactory.setHttpClient(null);
- AdjustFactory.setLogger(null);
- }
-
- public void testSendFirstPackage() {
- // send a default session package
- requestHandler.sendPackage(sessionPackage);
- SystemClock.sleep(1000);
-
- // check that the http client was called
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("HttpClient execute HttpUriRequest request"));
-
- // check the status received is ok
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.INFO, "Tracked session"));
-
- // calls delegate
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler finishedTrackingActivity"));
-
- // check the response data
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("[kind:unknown success:true willRetry:false error:null trackerToken:token trackerName:name network:network campaign:campaign adgroup:adgroup creative:creative]"));
-
- // check that the package handler was called to send the next package
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler sendNextPackage"));
- }
-
- public void testErrorSendPackage() {
- // set the mock http client to throw an Exception
- mockHttpClient.setMessageError("testErrorSendPackage");
-
- // send a default session package
- requestHandler.sendPackage(sessionPackage);
- SystemClock.sleep(1000);
-
- // check that the http client was called
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("HttpClient execute HttpUriRequest request"));
-
- // check the error message
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR,
- "Failed to track session. (Client protocol error: org.apache.http.client.ClientProtocolException: testErrorSendPackage) Will retry later."));
-
- // check that the package handler was called to close the failed package
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler closeFirstPackage"));
- }
-
- public void testResponseError() {
- // set the mock http client to send an error response
- mockHttpClient.setResponseError("testResponseError");
-
- // send a default session package
- requestHandler.sendPackage(sessionPackage);
- SystemClock.sleep(1000);
-
- // check that the http client was called
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("HttpClient execute HttpUriRequest request"));
-
- // check the status received is not ok
- assertTrue(mockLogger.toString(),
- mockLogger.containsMessage(LogLevel.ERROR, "Failed to track session. (testResponseError)"));
-
- // calls delegate
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler finishedTrackingActivity"));
-
- // check the response data
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("[kind:unknown success:false willRetry:false error:testResponseError trackerToken:null trackerName:null network:null campaign:null adgroup:null creative:null]"));
-
- // check that the package handler was called to send the next package
- assertTrue(mockLogger.toString(),
- mockLogger.containsTestMessage("PackageHandler sendNextPackage"));
-
-
- }
-
-}
diff --git a/Adjust/test/src/com/adjust/sdk/test/UnitTestActivity.java b/Adjust/test/src/com/adjust/sdk/test/UnitTestActivity.java
deleted file mode 100644
index 5fdd82425..000000000
--- a/Adjust/test/src/com/adjust/sdk/test/UnitTestActivity.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.adjust.sdk.test;
-
-import java.util.Map;
-
-import com.adjust.sdk.test.R;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.Menu;
-
-
-public class UnitTestActivity extends Activity {
-
- public UnitTestActivity() {
- super();
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.main, menu);
- return true;
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- //Adjust.onResume(this);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- //Adjust.onPause();
- }
-
- protected void trackEvent(String eventToken) {
- //Adjust.trackEvent(eventToken);
- }
-
- protected void trackEvent(String eventToken, Map parameters) {
- //Adjust.trackEvent(eventToken, parameters);
- }
-}
diff --git a/Adjust/test/src/main/AndroidManifest.xml b/Adjust/test/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..966df7ebc
--- /dev/null
+++ b/Adjust/test/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Adjust/test/src/main/java/com/adjust/sdk/test/UnitTestActivity.java b/Adjust/test/src/main/java/com/adjust/sdk/test/UnitTestActivity.java
new file mode 100644
index 000000000..4f18fadda
--- /dev/null
+++ b/Adjust/test/src/main/java/com/adjust/sdk/test/UnitTestActivity.java
@@ -0,0 +1,38 @@
+package com.adjust.sdk.test;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+
+public class UnitTestActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_unit_test);
+ }
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_unit_test, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.action_settings) {
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/Adjust/test/res/drawable-hdpi/ic_launcher.png b/Adjust/test/src/main/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from Adjust/test/res/drawable-hdpi/ic_launcher.png
rename to Adjust/test/src/main/res/drawable-hdpi/ic_launcher.png
diff --git a/Adjust/test/res/drawable-mdpi/ic_launcher.png b/Adjust/test/src/main/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from Adjust/test/res/drawable-mdpi/ic_launcher.png
rename to Adjust/test/src/main/res/drawable-mdpi/ic_launcher.png
diff --git a/Adjust/test/res/drawable-xhdpi/ic_launcher.png b/Adjust/test/src/main/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from Adjust/test/res/drawable-xhdpi/ic_launcher.png
rename to Adjust/test/src/main/res/drawable-xhdpi/ic_launcher.png
diff --git a/Adjust/test/src/main/res/drawable-xxhdpi/ic_launcher.png b/Adjust/test/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..4df189464
Binary files /dev/null and b/Adjust/test/src/main/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/Adjust/test/res/layout/activity_main.xml b/Adjust/test/src/main/res/layout/activity_unit_test.xml
similarity index 79%
rename from Adjust/test/res/layout/activity_main.xml
rename to Adjust/test/src/main/res/layout/activity_unit_test.xml
index 6f4ae9d52..b4e91d10a 100644
--- a/Adjust/test/res/layout/activity_main.xml
+++ b/Adjust/test/src/main/res/layout/activity_unit_test.xml
@@ -2,15 +2,15 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
- tools:context=".UnitTestActivity" >
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".UnitTestActivity">
+ android:layout_height="wrap_content" />
diff --git a/Adjust/test/src/main/res/menu/menu_unit_test.xml b/Adjust/test/src/main/res/menu/menu_unit_test.xml
new file mode 100644
index 000000000..e2e0db43e
--- /dev/null
+++ b/Adjust/test/src/main/res/menu/menu_unit_test.xml
@@ -0,0 +1,8 @@
+
diff --git a/Adjust/test/src/main/res/values-w820dp/dimens.xml b/Adjust/test/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 000000000..63fc81644
--- /dev/null
+++ b/Adjust/test/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Adjust/test/src/main/res/values/dimens.xml b/Adjust/test/src/main/res/values/dimens.xml
new file mode 100644
index 000000000..47c822467
--- /dev/null
+++ b/Adjust/test/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Adjust/test/res/values/strings.xml b/Adjust/test/src/main/res/values/strings.xml
similarity index 59%
rename from Adjust/test/res/values/strings.xml
rename to Adjust/test/src/main/res/values/strings.xml
index a7d425676..5ac683268 100644
--- a/Adjust/test/res/values/strings.xml
+++ b/Adjust/test/src/main/res/values/strings.xml
@@ -1,9 +1,8 @@
- AdjustIOTestsTest
- MainActivity
- Settings
+ Test
Hello world!
+ Settings
diff --git a/README.md b/README.md
index c54297d97..71988e8fd 100644
--- a/README.md
+++ b/README.md
@@ -3,11 +3,18 @@
This is the Android SDK of adjust™. You can read more about adjust™ at
adjust.com.
+## Example app
+
+There is an example app inside the [`example` directory][example]. You can open
+the Android project to see an example on how the adjust SDK can be integrated.
+
## Basic Installation
These are the minimal steps required to integrate the adjust SDK into your
-Android project. We are going to assume that you use Eclipse for your Android
-development.
+Android project. We are going to assume that you use Android Studio for your
+Android development and target an Android API level 9 (Gingerbread) or later.
+
+If you're using the [Maven Repository][maven] you can start with [step 3](#step3).
### 1. Get the SDK
@@ -16,123 +23,112 @@ archive in a folder of your choice.
### 2. Create the Adjust project
-In the Eclipse menu select `File → New → Project...`.
+In the Android Studio menu select `File → Import Module...`.
-![][project]
+![][import_module]
-From the Wizard expand the `Android` group and select `Android Project from
-Existing Code` and click `Next`.
+In the `Source directory` field, locate the folder you extracted in step 1.
+Select and choose the folder `./android_sdk/Adjust/adjust`. Make sure the
+module name `:adjust` appears before finishing.
-![][android]
+![][select_module]
-On the top of the next screen click the `Browse...` button and locate the
-folder you extracted in step 1. Select the Adjust subfolder and click `Open`.
-In the `Projects:` group make sure the Adjust project is selected. Also tick
-the option `Copy projects into workspace` and click `Finish`.
+The `adjust` module should be imported into your Android Studio project
+afterwards.
-![][import]
+![][imported_module]
-### 3. Add the adjust library to your project
+### 3. Add the adjust library to your project
-In the Package Explorer right click on your Android project and select
-`Properties`.
+Open the `build.gradle` file of your app and find the `dependencies` block. Add
+the following line:
-![][properties]
+```
+compile project(":adjust")
+```
-In the left pane select `Android`. In the bottom right group `Library` click
-the `Add...` button. From the list select the Adjust library project and
-click `OK`. Save your changed project properties by clicking `OK` again.
+![][gradle_adjust]
-![][library]
+If you are using Maven, add this line instead:
-### 4. Add permissions
+```
+compile 'com.adjust.sdk:adjust-android:4.0.0'
+```
-#### Google Play Store
+### 4. Add Google Play Services
-Since the 1st of August of 2014, apps in the Google Play Store must use the [Google Advertising ID][google_ad_id] to uniquely identify the devices. To allow the adjust SDK to use the Google Advertising ID, you must integrate the [Google Play Services][google_play_services].
+Since the 1st of August of 2014, apps in the Google Play Store must use the
+[Google Advertising ID][google_ad_id] to uniquely identify devices. To allow
+the adjust SDK to use the Google Advertising ID, you must integrate the [Google
+Play Services][google_play_services]. If you haven't done this yet, follow
+these steps:
-In the Package Explorer open the `AndroidManifest.xml` of your Android project.
-Add the `uses-permission` tag for `INTERNET` if it's not present already.
+1. Open the `build.gradle` file of your app and find the `dependencies` block. Add the
+following line:
-```xml
-
-```
-
-If you are using Proguard, add these lines to your Proguard file:
+ ```
+ compile 'com.google.android.gms:play-services:6.5.87'
+ ```
-````
--keep class com.adjust.sdk.** { *; }
--keep class com.google.android.gms.common.** { *; }
--keep class com.google.android.gms.ads.identifier.** { *; }
-```
+ If you don't need all of the Google Play Services, you can avoid [dex
+ issues][multidex] by using only the ads part:
-#### Other Stores
+ ```
+ compile 'com.google.android.gms:play-services-ads:6.5.87'
+ ```
-In the Package Explorer open the `AndroidManifest.xml` of your Android project.
-Add the `uses-permission` tags for `INTERNET` and `ACCESS_WIFI_STATE` if they
-aren't present already.
+ ![][gradle_gps]
-```xml
-
-
-```
+2. In the Package Explorer open the `AndroidManifest.xml` of your Android project.
+Add the following `meta-data` tag inside the `` element.
-![][permissions]
-If you are using Proguard, add these lines to your Proguard file:
+ ```xml
+
+ ```
-````
--keep class com.adjust.sdk.** { *; }
-```
+ ![][manifest_gps]
-### 5. Add Adjust settings
+### 5. Add permissions
-Still in the `AndroidManifest.xml`, add the following `meta-data` tags inside
-the `application` tag.
+In the Package Explorer open the `AndroidManifest.xml` of your Android project.
+Add the `uses-permission` tag for `INTERNET` if it's not present already.
```xml
-
-
-
+
```
-![][settings]
+If you are *not* targeting the Google Play Store, add both of these permissions instead:
-Replace `{YourAppToken}` with your App Token. You can find in your [dashboard].
-
-You can increase or decrease the amount of logs you see by changing the value
-of `AdjustLogLevel` to one of the following:
+```xml
+
+
+```
-- `verbose` - enable all logging
-- `debug` - enable more logging
-- `info` - the default
-- `warn` - disable info logging
-- `error` - disable warnings as well
-- `assert` - disable errors as well
+![][manifest_permissions]
-Depending on whether or not you build your app for testing or for production
-you must adjust the `AdjustEnvironment` setting:
+If you are using Proguard, add these lines to your Proguard file:
-- `sandbox` - for testing
-- `production` - before publishing
+```
+-keep class com.adjust.sdk.** { *; }
+-keep class com.google.android.gms.common.** { *; }
+-keep class com.google.android.gms.ads.identifier.** { *; }
+```
-**Important:** This value should be set to `sandbox` if and only if you or
-someone else is testing your app. Make sure to set the environment to
-`production` just before you publish the app. Set it back to `sandbox` when you
-start testing it again.
+If you are *not* targeting the Google Play Store, you can remove the
+`com.google.android.gms` rules.
-We use this environment to distinguish between real traffic and artificial
-traffic from test devices. It is very important that you keep this value
-meaningful at all times! Especially if you are tracking revenue.
+![][proguard]
### 6. Add broadcast receiver
-Still in your `AndroidManifest.xml`, add the following `receiver` tag inside
-the `application` tag.
+In your `AndroidManifest.xml` add the following `receiver` tag inside the
+`application` tag.
```xml
@@ -142,26 +138,110 @@ the `application` tag.
![][receiver]
-We use this broadcast receiver to retrieve the install referrer to improve
-conversion tracking.
+We use this broadcast receiver to retrieve the install referrer, in order to
+improve conversion tracking.
If you are already using a different broadcast receiver for the
`INSTALL_REFERRER` intent, follow [these instructions][referrer] to add the
Adjust receiver.
-### 7. Integrate adjust into your app
+### 7. Integrate Adjust into your app
+
+To start with, we'll set up basic session tracking.
+
+#### Basic Setup
+
+We recommend using a global android [Application][android_application] class to
+initialize the SDK. If don't have one in your app already, follow these steps:
+
+1. Create a class that extends `Application`.
+ ![][application_class]
+
+2. Open the `AndroidManifest.xml` file of your app and locate the `` element.
+3. Add the attribute `android:name` and set it to the name of your new application class pefixed by a dot.
+
+ In our example app we use an `Application` class named `GlobalApplication`, so the manifest file is configured as:
+ ```xml
+
+ ...
+
+ ```
+
+ ![][manifest_application]
+
+In your `Application` class find or create the `onCreate` method and add the
+following code to initialize the adjust SDK:
+
+```java
+import com.adjust.sdk.Adjust;
+import com.adjust.sdk.AdjustConfig;
+
+public class YourApplicationClass extends Application {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ String appToken = "{YourAppToken}";
+ String environment = AdjustConfig.ENVIRONMENT_SANDBOX;
+ AdjustConfig config = new AdjustConfig(this, appToken, environment);
+ Adjust.onCreate(config);
+ }
+}
+```
+
+![][application_config]
+
+Replace `{YourAppToken}` with your app token. You can find this in your
+[dashboard].
+
+Depending on whether you build your app for testing or for production, you must
+set `environment` with one of these values:
+
+```java
+String environment = AdjustConfig.ENVIRONMENT_SANDBOX;
+String environment = AdjustConfig.ENVIRONMENT_PRODUCTION;
+```
+
+**Important:** This value should be set to `AdjustConfig.ENVIRONMENT_SANDBOX`
+if and only if you or someone else is testing your app. Make sure to set the
+environment to `AdjustConfig.ENVIRONMENT_PRODUCTION` just before you publish
+the app. Set it back to `AdjustConfig.ENVIRONMENT_SANDBOX` when you start
+developing and testing it again.
+
+We use this environment to distinguish between real traffic and test traffic
+from test devices. It is very important that you keep this value meaningful at
+all times! This is especially important if you are tracking revenue.
+
+#### Adjust Logging
+
+You can increase or decrease the amount of logs you see in tests by calling
+`setLogLevel` on your `AdjustConfig` instance with one of the following
+parameters:
+
+```java
+config.setLogLevel(LogLevel.VERBOSE); // enable all logging
+config.setLogLevel(LogLevel.DEBUG); // enable more logging
+config.setLogLevel(LogLevel.INFO); // the default
+config.setLogLevel(LogLevel.WARN); // disable info logging
+config.setLogLevel(LogLevel.ERROR]; // disable warnings as well
+config.setLogLevel(LogLevel.ASSERT); // disable errors as well
+```
+
+### 8. Update your activities
To provide proper session tracking it is required to call certain Adjust
methods every time any Activity resumes or pauses. Otherwise the SDK might miss
a session start or session end. In order to do so you should follow these steps
for **each** Activity of your app:
-- Open the source file of your Activity.
-- Add the `import` statement at the top of the file.
-- In your Activity's `onResume` method call `Adjust.onResume`. Create the
- method if needed.
-- In your Activity's `onPause` method call `Adjust.onPause`. Create the
+1. Open the source file of your Activity.
+2. Add the `import` statement at the top of the file.
+3. In your Activity's `onResume` method call `Adjust.onResume`. Create the
method if needed.
+4. In your Activity's `onPause` method call `Adjust.onPause`. Create the method
+ if needed.
After these steps your activity should look like this:
@@ -171,7 +251,7 @@ import com.adjust.sdk.Adjust;
public class YourActivity extends Activity {
protected void onResume() {
super.onResume();
- Adjust.onResume(this);
+ Adjust.onResume();
}
protected void onPause() {
super.onPause();
@@ -187,50 +267,59 @@ Repeat these steps for **every** Activity of your app. Don't forget these steps
when you create new Activities in the future. Depending on your coding style
you might want to implement this in a common superclass of all your Activities.
-### 8. Build your app
+### 9. Build your app
Build and run your Android app. In your LogCat viewer you can set the filter
-`tag:Adjust` to hide all other logs. After your app has launched you should
-see the following Adjust log: `Tracked session start`
+`tag:Adjust` to hide all other logs. After your app has launched you should see
+the following Adjust log: `Install tracked`
-![][log]
+![][log_message]
## Additional Features
Once you have integrated the adjust SDK into your project, you can take
advantage of the following features.
-### 9. Add tracking of custom events.
+### 10. Add tracking of custom events
-You can tell adjust about every event you want. Suppose you want to track
-every tap on a button. You would have to create a new Event Token in your
-[dashboard]. Let's say that Event Token is `abc123`. In your button's `onClick`
-method you could then add the following line to track the click:
+You can use adjust to track any event in your app. Suppose you want to track
+every tap on a button. You would have to create a new event token in your
+[dashboard]. Let's say that event token is `abc123`. In your button's `onClick`
+method you could then add the following lines to track the click:
```java
-Adjust.trackEvent("abc123");
+AdjustEvent event = new AdjustEvent("abc123");
+Adjust.trackEvent(event);
```
-You can also register a callback URL for that event in your [dashboard] and we
-will send a GET request to that URL whenever the event gets tracked. In that
-case you can also put some key-value-pairs in a dictionary and pass it to the
-`trackEvent` method. We will then append these named parameters to your
+The event instance can be used to configure the event even more before tracking
+it.
+
+### 11. Add callback parameters
+
+You can register a callback URL for your events in your [dashboard]. We will
+send a GET request to that URL whenever the event gets tracked. You can add
+callback parameters to that event by calling `addCallbackParameter` on the
+event instance before tracking it. We will then append these parameters to your
callback URL.
For example, suppose you have registered the URL
-`http://www.adjust.com/callback` for your event with Event Token `abc123` and
-execute the following lines:
+`http://www.adjust.com/callback` then track an event like this:
```java
-Map parameters = new HashMap();
-parameters.put("key", "value");
-parameters.put("foo", "bar");
-Adjust.trackEvent("abc123", parameters);
+AdjustEvent event = new AdjustEvent("abc123");
+
+event.addCallbackParameter("key", "value");
+event.addCallbackParameter("foo", "bar");
+
+Adjust.trackEvent(event);
```
In that case we would track the event and send a request to:
- http://www.adjust.com/callback?key=value&foo=bar
+```
+http://www.adjust.com/callback?key=value&foo=bar
+```
It should be mentioned that we support a variety of placeholders like
`{android_id}` that can be used as parameter values. In the resulting callback
@@ -239,182 +328,178 @@ Also note that we don't store any of your custom parameters, but only append
them to your callbacks. If you haven't registered a callback for an event,
these parameters won't even be read.
-### 10. Add tracking of revenue
+You can read more about using URL callbacks, including a full list of available
+values, in our [callbacks guide][callbacks-guide].
-If your users can generate revenue by clicking on advertisements or making
-purchases you can track those revenues. If, for example, a click is worth one
-cent, you could make the following call to track that revenue:
-```java
-Adjust.trackRevenue(1.0);
-```
+### 12. Partner parameters
+
+You can also add parameters to be transmitted to network partners, for the
+integrations that have been activated in your adjust dashboard.
-The parameter is supposed to be in cents and will get rounded to one decimal
-point. If you want to differentiate between different kinds of revenue you can
-get different Event Tokens for each kind. Again, you need to create those Event
-Tokens in your [dashboard]. In that case you would make a call like this:
+This works similarly to the callback parameters mentioned above, but can be
+added by calling the `addPartnerParameter` method on your `AdjustEvent` instance.
```java
-Adjust.trackRevenue(1.0, "abc123");
-```
+AdjustEvent event = new AdjustEvent("abc123");
-Again, you can register a callback and provide a dictionary of named
-parameters, just like it worked with normal events.
+event.addPartnerParameter("key", "value");
+event.addPartnerParameter("foo", "bar");
-```java
-Map parameters = new HashMap();
-parameters.put("key", "value");
-parameters.put("foo", "bar");
-Adjust.trackRevenue(1.0, "abc123", parameters);
+Adjust.trackEvent(event);
```
-### 11. Set listener for delegate notifications
+You can read more about special partners and these integrations in our [guide
+to special partners.][special-partners]
-Every time your app tries to track a session, an event or some revenue, you can
-be notified about the success of that operation and receive additional
-information about the current install.
+### 13. Add tracking of revenue
-Please make sure to consider [applicable attribution data policies.][attribution-data]
+If your users can generate revenue by tapping on advertisements or making
+in-app purchases you can track those revenues with events. Lets say a tap is
+worth one Euro cent. You could then track the revenue event like this:
-The simplest way is to create a single anonymous listener for these notifications.
+```java
+AdjustEvent event = new AdjustEvent("abc123");
+event.setRevenue(0.01, "EUR");
+Adjust.trackEvent(event);
+```
-- Open the source file of your main activity, find its `onResume` method and
- add the following code below your `Adjust.onResume` call:
+This can be combined with callback parameters of course.
- ```java
- Adjust.onResume(this);
+Please note that while the currency token must be set, adjust will not perform
+a currency conversion. **You should always transmit revenues in the currency
+that you have set in your adjust dashboard.**
- Adjust.setOnFinishedListener(new OnFinishedListener() {
- public void onFinishedTracking(ResponseData responseData) {
- }
- });
- ```
+You can read more about revenue and event tracking in the [event tracking
+guide.][event-tracking]
-- Alternatively, you could implement the `OnFinishedListener` interface in your
- activity and pass the activity:
+### 14. Set up deep link reattributions
- ```java
- Adjust.setOnFinishedListener(this);
- ```
+You can set up the adjust SDK to handle deep links that are used to open your
+app. We will only read certain adjust specific parameters. This is essential if
+you are planning to run retargeting or re-engagement campaigns with deep links.
-This `OnFinishedListener` will only be set if this activity has been active
-before. You can set it in all activities to make sure that it is always set,
-regardless of what activities have been active. In this case it makes sense to
-implement the `OnFinishedListener` interface in one class and set the listener to
-the same object in every `onResume` method.
-
-The listener method `onFinishedTracking` will get called every time any
-activity was tracked or failed to track. Within this listener function you have
-access to the `responseData` parameter. Here is a quick summary of its
-interface:
-
-- `ActivityKind getActivityKind()` indicates what kind of activity
- was tracked. Returns one of these values:
-
- ```java
- ActivityKind.SESSION
- ActivityKind.EVENT
- ActivityKind.REVENUE
- ActivityKind.REATTRIBUTION
- ```
+For each activity that accepts deep links, find the `onCreate` method and add
+the folowing call to adjust:
-- `String getActivityKindString()` human readable version of the activity kind. Possible values:
+```java
+protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
- ```
- session
- event
- revenue
- reattribution
- ```
+ Intent intent = getIntent();
+ Uri data = intent.getData();
+ Adjust.appWillOpenUrl(data);
+ //...
+}
+```
-- `boolean wasSuccess()` indicates whether or not the tracking attempt was
- successful.
-- `boolean willRetry()` is true when the request failed, but will be
- retried.
-- `String getError()` an error message when the activity failed to track or
- the response could not be parsed. Is `null` otherwise.
-- `String getTrackerToken()` the tracker token of the current install. Is `null` if
- request failed or response could not be parsed.
-- `String getTrackerName()` the tracker name of the current install. Is `null` if
- request failed or response could not be parsed.
-- `String getNetwork()` the network grouping level of the current install. Is `null` if
- request failed, unavailable, or response could not be parsed.
-- `String getCampaign()` the campaign grouping level of the current install. Is `null` if
- request failed, unavailable or response could not be parsed.
-- `String getAdgroup()` the ad group grouping level of the current install. Is `null` if
- request failed, unavailable or response could not be parsed.
-- `String getCreative()` the creative grouping level of the current install. Is `null` if
- request failed, unavailable or response could not be parsed.
-
-### 12. Enable event buffering
+### 15. Enable event buffering
If your app makes heavy use of event tracking, you might want to delay some
HTTP requests in order to send them in one batch every minute. You can enable
-event buffering by adding the following line to your Adjust settings in your
-`AndroidManifest.xml` file.
+event buffering with your `AdjustConfig` instance:
-```xml
-
+```java
+AdjustConfig config = new AdjustConfig(this, appToken, environment);
+
+config.setEventBufferingEnabled(true);
+
+Adjust.onCreate(config);
```
-### 13. Disable tracking
+### 16. Set listener for attribution changes
+
+You can register a listener to be notified of tracker attribution changes. Due
+to the different sources considered for attribution, this information can not
+by provided synchronously. The simplest way is to create a single anonymous
+listener:
-You can disable the adjust SDK from tracking by invoking the method
-`setEnabled` with the enabled parameter as `false`. This setting is remembered
-between sessions, but it can only be activated after the first session.
+Please make sure to consider our [applicable attribution data
+policies][attribution-data].
+
+With the `AdjustConfig` instance, before starting the SDK, add the anonymous listener:
```java
-Adjust.setEnabled(false);
+AdjustConfig config = new AdjustConfig(this, appToken, environment);
+
+config.setOnAttributionChangedListener(new OnAttributionChangedListener() {
+ @Override
+ public void onAttributionChanged(AdjustAttribution attribution) {
+ }
+});
+
+Adjust.onCreate(config);
```
-You can verify if the adjust SDK is currently active with the method
-`isEnabled`. It is always possible to activate the adjust SDK by invoking
-`setEnable` with the enabled parameter as `true`.
+Alternatively, you could implement the `OnAttributionChangedListener`
+interface in your `Application` class and set it as listener:
-### 14. Handle deep linking
+```java
+AdjustConfig config = new AdjustConfig(this, appToken, environment);
+config.setOnAttributionChangedListener(this);
+Adjust.onCreate(config);
+```
-You can also set up the adjust SDK to read deep links that come to your app. We
-will only read the data that is injected by adjust tracker URLs. This is
-essential if you are planning to run retargeting or re-engagement campaigns
-with deep links.
+The listener function will be called when the SDK receives the final attribution
+information. Within the listener function you have access to the `attribution`
+parameter. Here is a quick summary of its properties:
-For each activity that accepts deep links, find the `onCreate` method and add
-the folowing call to adjust:
+- `String trackerToken` the tracker token of the current install.
+- `String trackerName` the tracker name of the current install.
+- `String network` the network grouping level of the current install.
+- `String campaign` the campaign grouping level of the current install.
+- `String adgroup` the ad group grouping level of the current install.
+- `String creative` the creative grouping level of the current install.
-```java
-protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+### 16. Disable tracking
- Intent intent = getIntent();
- Uri data = intent.getData();
- Adjust.appWillOpenUrl(data);
- //...
-}
+You can disable the adjust SDK from tracking any activities of the current
+device by calling `setEnabled` with parameter `false`. This setting is
+remembered between sessions.
+
+```java
+Adjust.setEnabled(false);
```
-[adjust.com]: http://adjust.com
-[dashboard]: http://adjust.com
-[releases]: https://github.com/adjust/adjust_android_sdk/releases
-[project]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/project.png
-[android]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/android.png
-[import]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/import2.png
-[properties]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/properties.png
-[library]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/library.png
-[permissions]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/permissions.png
-[settings]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/settings.png
-[receiver]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/receiver.png
-[activity]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/activity4.png
-[log]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/log4.png
-[referrer]: doc/referrer.md
-[attribution-data]: https://github.com/adjust/sdks/blob/master/doc/attribution-data.md
-[google_play_services]: http://developer.android.com/google/play-services/index.html
-[google_ad_id]: https://developer.android.com/google/play-services/id.html
+You can check if the adjust SDK is currently enabled by calling the function
+`isEnabled`. It is always possible to activate the adjust SDK by invoking
+`setEnabled` with the enabled parameter as `true`.
+
+[dashboard]: http://adjust.com
+[releases]: https://github.com/adjust/adjust_android_sdk/releases
+[import_module]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/01_import_module.png
+[select_module]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/02_select_module.png
+[imported_module]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/03_imported_module.png
+[gradle_adjust]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/04_gradle_adjust.png
+[gradle_gps]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/05_gradle_gps.png
+[manifest_gps]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/06_manifest_gps.png
+[manifest_permissions]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/07_manifest_permissions.png
+[proguard]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/08_proguard.png
+[receiver]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/09_receiver.png
+[application_class]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/11_application_class.png
+[manifest_application]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/12_manifest_application.png
+[application_config]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/13_application_config.png
+[activity]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/14_activity.png
+[log_message]: https://raw.github.com/adjust/sdks/master/Resources/android/v4/15_log_message.png
+
+[referrer]: doc/referrer.md
+[attribution-data]: https://github.com/adjust/sdks/blob/master/doc/attribution-data.md
+[google_play_services]: http://developer.android.com/google/play-services/setup.html
+[android_application]: http://developer.android.com/reference/android/app/Application.html
+[application_name]: http://developer.android.com/guide/topics/manifest/application-element.html#nm
+[google_ad_id]: https://developer.android.com/google/play-services/id.html
+[callbacks-guide]: https://docs.adjust.com/en/callbacks
+[event-tracking]: https://docs.adjust.com/en/event-tracking
+[special-partners]: https://docs.adjust.com/en/special-partners
+[multidex]: https://developer.android.com/tools/building/multidex.html
+[maven]: http://maven.org
+[example]: https://github.com/adjust/android_sdk/tree/master/Adjust/example
## License
The adjust SDK is licensed under the MIT License.
-Copyright (c) 2012-2013 adjust GmbH,
+Copyright (c) 2012-2015 adjust GmbH,
http://www.adjust.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
diff --git a/VERSION b/VERSION
index b72762837..fcdb2e109 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.6.2
+4.0.0
diff --git a/doc/android_studio.md b/doc/android_studio.md
deleted file mode 100644
index 6eef32dc5..000000000
--- a/doc/android_studio.md
+++ /dev/null
@@ -1,85 +0,0 @@
-## Integrate adjust using Android Studio
-
-If you want to use the adjust SDK on your android application using the Android
-Studio you have to follow some steps to circumvent the current limitations that
-it has. This limitation is being addressed at [google].
-
-The following steps were taken from a [stack overflow] question.
-
-### Import the adjust SDK as a new module
-
-1. In the `Android Studio` menu click `File → New Module`.
-
- ![][new_module]
-
-2. Select the option `Android Library`. Press `Next`.
-
- ![][android_library]
-
-3. Fill the form with follow values:
-
- ```
- Module name: Adjust
- Package name: com.adjust.sdk
- Minimum required SDK: API 8: Android 2.2 (Froyo)
- Theme: None
- ```
-
- Select the `target SDK` and `Compile with` android SDK options according to
- your project. Untick the `Create activity` box and leave the rest
- unticked. Press `Next`.
-
- ![][form]
-
-4. Make sure that an `Adjust` folder was created in the your `Android Studio`
- project and that the `Adjust → src → main → java → com.adjust.sdk` folder is
- empty.
-
- ![][empty]
-
-4. Download and extract the latest adjust android SDK from our [releases] page.
-
-5. From the extracted folder, select and copy the files from
- `Adjust/src/com/adjust/sdk/`.
-
- ![][copy]
-
-6. In your `Android Studio` project. Select the folder `Adjust → src → main →
- java → com.adjust.sdk` and paste.
-
- ![][paste]
-
-7. Confirm the copy in the popup dialog that appears.
-
- ![][confirm_copy]
-
-### Add the `Adjust` module to your project
-
-8. In the `Android Studio` menu click `File → Project Structure...`.
-
- ![][project_structure]
-
-9. Select the `Dependencies` tab of your module and add `Adjust` as a `Module
- dependency`.
-
- ![][dependencies]
-
- ![][modules]
-
-10. From this step you can follow the [android sdk] guide from step `4 - Add
- permissions` onwards.
-
-[stack overflow]: http://stackoverflow.com/questions/20310164/how-to-import-eclipse-library-project-from-github-to-android-studio-project
-[google]: https://code.google.com/p/android/issues/detail?id=62122
-[releases]: https://github.com/adjust/android_sdk/releases
-[android sdk]: https://github.com/adjust/android_sdk/blob/master/README.md#4-add-permissions
-[new_module]: https://raw.github.com/adjust/sdks/master/Resources/android/android_studio_01_new_module.png
-[android_library]: https://raw.github.com/adjust/sdks/master/Resources/android/android_studio_02_android_library.png
-[form]: https://raw.github.com/adjust/sdks/master/Resources/android/android_studio_03_form.png
-[empty]: https://raw.github.com/adjust/sdks/master/Resources/android/android_studio_04_empty.png
-[copy]: https://raw.github.com/adjust/sdks/master/Resources/android/android_studio_05_copy.png
-[paste]: https://raw.github.com/adjust/sdks/master/Resources/android/android_studio_06_paste.png
-[confirm_copy]: https://raw.github.com/adjust/sdks/master/Resources/android/android_studio_07_confirm_copy.png
-[project_structure]: https://raw.github.com/adjust/sdks/master/Resources/android/android_studio_08_project_structure.png
-[dependencies]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/android_studio_09_dependencies.png
-[modules]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/android_studio_10_modules.png
diff --git a/doc/criteo_plugin.md b/doc/criteo_plugin.md
new file mode 100644
index 000000000..eb2d25a4e
--- /dev/null
+++ b/doc/criteo_plugin.md
@@ -0,0 +1,100 @@
+## Criteo plugin
+
+Integrate adjust with Criteo events by following these steps:
+
+1. Locate the `plugin` folder inside the downloaded archive from our
+ [releases page](https://github.com/adjust/android_sdk/releases).
+
+2. Open the `adjust` module in Android Studio and locate the
+ `plugin` package folder in `adjust/java/com/adjust/sdk`.
+
+3. Drag the `AdjustCriteo.java` and `CriteoProduct.java` files from the
+ downloaded `plugin` folder into the `plugin` folder in the `adjust` project.
+
+Now you can integrate each of the different Criteo events, like in the
+following examples:
+
+### View Homepage
+
+```java
+AdjustEvent event = new AdjustEvent("{viewHomepageEventToken}");
+
+Adjust.trackEvent(event);
+```
+
+### View Search
+
+```java
+import com.adjust.sdk.plugin.AdjustCriteo;
+
+AdjustEvent event = new AdjustEvent("{viewSearchEventToken}");
+AdjustCriteo.injectViewSearchIntoEvent(event, "2015-01-01", "2015-01-07");
+
+Adjust.trackEvent(event);
+```
+
+### View Listing
+
+```java
+import com.adjust.sdk.plugin.AdjustCriteo;
+
+AdjustEvent event = new AdjustEvent("{viewListingEventToken}");
+
+CriteoProduct product1 = new CriteoProduct(100, 1, "productId1");
+CriteoProduct product2 = new CriteoProduct(77.7f, 3, "productId2");
+CriteoProduct product3 = new CriteoProduct(50, 2, "productId3");
+
+List products = Arrays.asList(product1, product2, product3);
+
+AdjustCriteo.injectViewListingIntoEvent(event, products, "customerId1");
+
+Adjust.trackEvent(event);
+```
+
+### View Product
+
+```java
+import com.adjust.sdk.plugin.AdjustCriteo;
+
+AdjustEvent event = new AdjustEvent("{viewProductEventToken}");
+
+AdjustCriteo.injectViewProductIntoEvent(event, "productId1", "customerId1");
+
+Adjust.trackEvent(event);
+```
+
+### Cart
+
+```java
+import com.adjust.sdk.plugin.AdjustCriteo;
+
+AdjustEvent event = new AdjustEvent("{cartEventToken}");
+
+CriteoProduct product1 = new CriteoProduct(100, 1, "productId1");
+CriteoProduct product2 = new CriteoProduct(77.7f, 3, "productId2");
+CriteoProduct product3 = new CriteoProduct(50, 2, "productId3");
+
+List products = Arrays.asList(product1, product2, product3);
+
+AdjustCriteo.injectCartIntoEvent(event, products, "customerId1");
+
+Adjust.trackEvent(event);
+```
+
+### Transaction confirmation
+
+```java
+import com.adjust.sdk.plugin.AdjustCriteo;
+
+AdjustEvent event = new AdjustEvent("{transactionConfirmedEventToken}");
+
+CriteoProduct product1 = new CriteoProduct(100, 1, "productId1");
+CriteoProduct product2 = new CriteoProduct(77.7f, 3, "productId2");
+CriteoProduct product3 = new CriteoProduct(50, 2, "productId3");
+
+List products = Arrays.asList(product1, product2, product3);
+
+AdjustCriteo.injectTransactionConfirmedIntoEvent(event, products, "customerId1");
+
+Adjust.trackEvent(event);
+```
diff --git a/doc/eclipse.md b/doc/eclipse.md
new file mode 100644
index 000000000..d9565c37a
--- /dev/null
+++ b/doc/eclipse.md
@@ -0,0 +1,69 @@
+## Integrate adjust using Eclipse.
+
+Since SDK version 4.0.0 we recommend using Android Studio. Follow these steps
+to integrate the SDK using Eclipse.
+
+## Basic Installation
+
+The most straightforward way to integrate the adjust SDK in an Eclipse project
+is by linking the compiled jar.
+
+### 1. Get the Jar
+
+You can get the jar in the latest [releases page][releases]. Another way is to
+download it from the [Maven repository][maven] by searching for
+[`com.adjust.sdk`][maven_search].
+
+### 2. Add the adjust library to your project
+
+After downloading the jar file, drag it into the `libs` folder inside your
+project. This will make the adjust SDK available in your app.
+
+### 3. Add Google Play Services
+
+Since the 1st of August of 2014, apps in the Google Play Store must use the
+[Google Advertising ID][google_ad_id] to uniquely identify devices. To allow
+the adjust SDK to use the Google Advertising ID, you must integrate the [Google
+Play Services][google_play_services]. If you haven't done this yet, follow
+these steps:
+
+1. Copy the library project at
+
+ ```
+ /extras/google/google_play_services/libproject/google-play-services_lib/
+ ```
+
+ to the location where you maintain your Android app projects.
+
+2. Import the library project into your Eclipse workspace. Click `File >
+ Import`, select `Android > Existing Android Code into Workspace`, and browse
+ to the copy of the library project to import it.
+
+3. In your app project, reference Google Play services library project. See
+ [Referencing a Library Project for Eclipse][eclipse_library] for more
+ information on how to do this.
+
+ You should be referencing a copy of the library that you copied to your
+ development workspace. You should not reference the library directly from
+ the Android SDK directory.
+
+4. After you've added the Google Play services library as a dependency for your app project,
+open your app's manifest file and add the following tag as a child of the [][application] element:
+
+ ```xml
+
+ ```
+
+### 4. Next steps
+
+You can follow our [guide][guide_permissions] from the `5. Add permissions` section foward.
+
+[releases]: https://github.com/adjust/adjust_android_sdk/releases
+[google_ad_id]: https://developer.android.com/google/play-services/id.html
+[maven]: http://maven.org
+[maven_search]: http://search.maven.org/#search%7Cga%7C1%7Ccom.adjust.sdk
+[application]: http://developer.android.com/guide/topics/manifest/application-element.html
+[eclipse_library]: http://developer.android.com/tools/projects/projects-eclipse.html#ReferencingLibraryProject
+[guide_permissions]: https://github.com/adjust/android_sdk/tree/master4#5-add-permissions
+[google_play_services]: http://developer.android.com/google/play-services/setup.html
diff --git a/doc/google_play_services.md b/doc/google_play_services.md
deleted file mode 100644
index 4994e47e8..000000000
--- a/doc/google_play_services.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# Google Play Services
-
-The default behavior of the Android SDK of adjust is to send the [Google Advertising ID][google_ad_id] when it's
-available. Only if the app does not use [Google Play Services][ensure], we try to obtain the Android Id and Mac Address of the device.
-
-Although we have this protection to prevent the use of Mac Address or Android Id for a Google Play Services app, it's possible to remove from the source the files that access this device functions. Follow the steps to do so:
-
-1. Get the Android SDK of adjust by following the first step of our [guide][get_sdk].
-
-2. Find the folder `Adjust/src/com/adjust/sdk/deviceIds/`. It contains both the files `MacAddressUtil.java` and `AndroidIdUtil.java`
-
-3. Delete the folder from the project. Alternately, in Eclipse find in the package `com.adjust.sdk.deviceIds` and delete it.
-
-[google_ad_id]:https://developer.android.com/google/play-services/id.html
-[ensure]:http://developer.android.com/google/play-services/setup.html#ensure
-[get_sdk]:https://github.com/adjust/android_sdk#1-get-the-sdk
diff --git a/doc/migrate.md b/doc/migrate.md
index 0a2a272a1..8c2ae9add 100644
--- a/doc/migrate.md
+++ b/doc/migrate.md
@@ -1,4 +1,99 @@
-## Migrate your adjust SDK for Android to 3.6.2 from v2.1.x
+## Migrate your adjust SDK for Android to 4.0.0 from 3.6.2
+
+### The Application class
+
+One major change is how the adjust SDK is initialized. You should now use a
+global Android [Application][android_application] class instead of the manifest file.
+
+If you don't already use one for your app, follow the steps in our [Readme][basic-setup].
+
+A second major change is how to configure the adjust SDK. All initial setup is now done with
+a new config object. Inside the `onCreate` method of the `Application` class:
+
+1. Create an the config object `AdjustConfig` with the app token, the environment and `this`.
+2. Optionally configure it.
+3. Launch the SDK by invoking `Adjust.onCreate` with the config object.
+
+Here is an example of how the setup might look before in the manifest and
+after the migration in the `Application` class:
+
+##### Before
+
+```xml
+
+
+
+```
+
+##### After
+
+```java
+import com.adjust.sdk.Adjust;
+import com.adjust.sdk.AdjustConfig;
+
+public class YourApplicationClass extends Application {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ // configure Adjust
+ String appToken = "{YourAppToken}";
+ String environment = AdjustConfig.ENVIRONMENT_SANDBOX;
+ AdjustConfig config = new AdjustConfig(this, appToken, environment);
+ config.setLogLevel(LogLevel.INFO); // if not configured, INFO is used by default
+ Adjust.onCreate(config);
+ }
+}
+```
+
+### Event tracking
+
+We also introduced proper event objects that can be set up before they are
+tracked. Again, an example of how it might look like before and after:
+
+##### Before
+
+```java
+Map parameters = new HashMap();
+parameters.put("key", "value");
+parameters.put("foo", "bar");
+Adjust.trackEvent("abc123", parameters);
+```
+
+##### After
+
+```java
+AdjustEvent event = new AdjustEvent("abc123");
+event.addCallbackParameter("key", "value");
+event.addCallbackParameter("foo", "bar");
+Adjust.trackEvent(event);
+```
+
+### Revenue tracking
+
+Revenues are now handled like normal events. You just set a revenue and a
+currency to track revenues. Note that it is no longer possible to track revenues
+without associated event tokens. You might need to create an additional event token
+in your dashboard.
+
+*Please note* - the revenue format has been changed from a cent float to a whole
+currency-unit float. Current revenue tracking must be adjusted to whole currency
+units (i.e., divided by 100) in order to remain consistent.
+
+##### Before
+
+```java
+Adjust.trackRevenue(1.0, "abc123");
+```
+
+##### After
+
+```java
+AdjustEvent event = new AdjustEvent("abc123");
+event.setRevenue(0.01, "EUR");
+Adjust.trackEvent(event);
+```
+
+## Additional steps if you come from v2.1.x
We renamed the main class `com.adeven.adjustio.AdjustIo` to
`com.adjust.sdk.Adjust`. Follow these steps to update all adjust SDK calls.
@@ -148,4 +243,7 @@ meaningful at all times! Especially if you are tracking revenue.
[import]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/import2.png
[activity]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/activity4.png
[settings]: https://raw.github.com/adjust/adjust_sdk/master/Resources/android/settings.png
+[android_application]: http://developer.android.com/reference/android/app/Application.html
+[application_name]: http://developer.android.com/guide/topics/manifest/application-element.html#nm
+[basic-setup]: https://github.com/adjust/android_sdk/tree/master#basic-setup
diff --git a/doc/mixpanel.md b/doc/mixpanel.md
index 51972666a..d7e5b7790 100644
--- a/doc/mixpanel.md
+++ b/doc/mixpanel.md
@@ -1,42 +1,61 @@
##Integrate adjust with Mixpanel SDK
-The Mixpanel API allows to register common properties to be sent in all activities as `super properties`, as it is explained in the [Mixpanel page][mixpanel_android].
-To integrate adjust with all tracked events of Mixpanel, you must set the `super properties` after receiving the response data of each activity.
-Follow the steps of the [delegate notifications][response_callbacks] chapter in our Android SDK guide to implement it.
-The delegate function can be set as the following, to use the Mixpanel API:
+The Mixpanel API allows to register common properties to be sent in all
+activities as `super properties`, as it is explained in the [Mixpanel
+page][mixpanel_android]. To integrate adjust with all tracked events of
+Mixpanel, you must set the `super properties` after receiving the response data
+of each activity. Follow the steps of the [listener][listener] chapter in our
+Android SDK guide to implement it. The delegate function can be set as the
+following, to use the Mixpanel API:
```java
- Adjust.setOnFinishedListener(new OnFinishedListener() {
- public void onFinishedTracking(ResponseData responseData) {
- MixpanelAPI mixpanel =
- MixpanelAPI.getInstance(context, MIXPANEL_TOKEN);
-
- // The adjust properties will be sent
- // with all future track calls.
- JSONObject props = new JSONObject();
-
- if (responseData.getNetwork() != null)
- props.put("[Adjust]Network", responseData.getNetwork());
-
- if (responseData.getCampaign() != null)
- props.put("[Adjust]Campaign", responseData.getCampaign());
-
- if (responseData.getAdgroup() != null)
- props.put("[Adjust]Adgroup", responseData.getAdgroup());
-
- if (responseData.getCreative() != null)
- props.put("[Adjust]Creative", responseData.getCreative());
-
- if (props.length() > 0)
- mixpanel.registerSuperProperties(props);
-
- }
- });
-```
+public class YourApplicationClass extends Application {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ // configure Adjust
+ String appToken = "{YourAppToken}";
+ String environment = AdjustConfig.ENVIRONMENT_SANDBOX;
+ AdjustConfig config = new AdjustConfig(this, appToken, environment);
+
+ config.setOnAttributionChangedListener(new OnAttributionChangedListener() {
+ @Override
+ public void onAttributionChanged(Attribution attribution) {
+ MixpanelAPI mixpanel = MixpanelAPI.getInstance(context, MIXPANEL_TOKEN);
+
+ // The adjust properties will be sent
+ // with all future track calls.
+ JSONObject props = new JSONObject();
+
+ insertJsonProperty(props, "[Adjust]Network", attribution.network);
+
+ insertJsonProperty(props, "[Adjust]Campaign", attribution.campaign);
+ insertJsonProperty(props, "[Adjust]Adgroup", attribution.adgroup);
+
+ insertJsonProperty(props, "[Adjust]Creative", attribution.creative);
+
+ if (props.length() > 0)
+ mixpanel.registerSuperProperties(props);
+ }
+
+ private void insertJsonProperty(JSONObject props, String name, String value) {
+ try {
+ if (value != null) {
+ props.put(name, value);
+ }
+ } catch(JSONException e) { }
+ }
+ });
+
+ Adjust.onCreate(config);
+ }
+}
+```
-Before you implement this interface, please take care to consider [possible conditions for usage of some of your data][attribution_data].
+Before you implement this interface, please take care to consider [possible
+conditions for usage of some of your data][attribution_data].
[mixpanel_android]: https://mixpanel.com/help/reference/android#superproperties
[attribution_data]: https://github.com/adjust/sdks/blob/master/doc/attribution-data.md
-[response_callbacks]: https://github.com/adjust/android_sdk/tree/master#11-set-listener-for-delegate-notifications
+[listener]: https://github.com/adjust/android_sdk/tree/master#13-set-listener-for-delegate-notifications
diff --git a/doc/preinstalled.md b/doc/preinstalled.md
index 88b43fd5f..486005dc6 100644
--- a/doc/preinstalled.md
+++ b/doc/preinstalled.md
@@ -1,16 +1,23 @@
## Using Adjust for pre-installed apps
-If you want to use the Adjust SDK to recognize users that found your app pre-installed on their device, follow these steps.
+If you want to use the Adjust SDK to recognize users that found your app
+pre-installed on their device, follow these steps.
1. Integrate Adjust as described in our [README].
2. Create a new tracker in your [dashboard].
-3. Open your `AndroidManifest.xml` and add the following line to your Adjust settings:
+3. Open your Application class and add set the default tracker of your
+ `AdjustConfig`:
- ```xml
-
+ ```java
+ djustConfig config = new AdjustConfig(this, appToken, environment);
+ config.setDefaultTracker("{TrackerToken}");
+ Adjust.onCreate(config);
```
- Replace `{TrackerToken}` with the tracker token you created in step 2. Please note that the dashboard displays a tracker URL (including `http://app.adjust.io/`). In your source code, you should specify only the six-character token and not the entire URL.
+ Replace `{TrackerToken}` with the tracker token you created in step 2.
+ Please note that the dashboard displays a tracker URL (including
+ `http://app.adjust.io/`). In your source code, you should specify only the
+ six-character token and not the entire URL.
4. Build and run your app. You should see a line like the following in LogCat:
@@ -18,6 +25,5 @@ If you want to use the Adjust SDK to recognize users that found your app pre-ins
Default tracker: 'abc123'
```
-
[README]: ../README.md
[dashboard]: http://adjust.com
diff --git a/doc/referrer.md b/doc/referrer.md
index 60742aa32..dc7056861 100644
--- a/doc/referrer.md
+++ b/doc/referrer.md
@@ -4,12 +4,13 @@ If multiple SDKs need to register a broadcast receiver for the
`INSTALL_REFERRER` intent in your app, you will have to implement your own
`BroadcastReceiver` that calls all the other receivers that you want to
support. It should look like this [1]:
+
```java
public class InstallReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// Adjust
- new ReferrerReceiver().onReceive(context, intent);
+ new AdjustReferrerReceiver().onReceive(context, intent);
// Google Analytics
new CampaignTrackingReceiver().onReceive(context, intent);
@@ -20,6 +21,7 @@ public class InstallReceiver extends BroadcastReceiver {
Make sure to adjust the list of supported receviers and fix the imports. You
also need to update your `AndroidManifest.xml` to use your own
`InstallReceiver`:
+
```xml