From 44b23ddffacc0ac84b297be114b6670896070658 Mon Sep 17 00:00:00 2001 From: Pedro Date: Mon, 14 Apr 2014 17:45:45 +0200 Subject: [PATCH 01/12] Created new Activity kind package reattribution --- .../src/com/adjust/sdk/ActivityHandler.java | 53 +++++++++++++++++++ Adjust/src/com/adjust/sdk/ActivityKind.java | 14 +++-- Adjust/src/com/adjust/sdk/Adjust.java | 11 ++++ Adjust/src/com/adjust/sdk/PackageBuilder.java | 20 +++++++ Adjust/src/com/adjust/sdk/Util.java | 24 +++++++++ 5 files changed, 117 insertions(+), 5 deletions(-) diff --git a/Adjust/src/com/adjust/sdk/ActivityHandler.java b/Adjust/src/com/adjust/sdk/ActivityHandler.java index d71a6e8aa..3b740d500 100644 --- a/Adjust/src/com/adjust/sdk/ActivityHandler.java +++ b/Adjust/src/com/adjust/sdk/ActivityHandler.java @@ -24,6 +24,7 @@ import java.io.ObjectOutputStream; import java.io.OptionalDataException; import java.lang.ref.WeakReference; +import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.concurrent.Executors; @@ -36,6 +37,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; @@ -49,6 +51,7 @@ public class ActivityHandler extends HandlerThread { 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 final SessionHandler sessionHandler; private IPackageHandler packageHandler; @@ -195,6 +198,13 @@ public Boolean isEnabled() { } } + 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; @@ -202,6 +212,8 @@ private static final class SessionHandler extends Handler { 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; @@ -240,6 +252,10 @@ public void handleMessage(Message message) { PackageBuilder revenueBuilder = (PackageBuilder) message.obj; sessionHandler.trackRevenueInternal(revenueBuilder); break; + case DEEP_LINK: + Uri url = (Uri) message.obj; + sessionHandler.readOpenUrlInternal(url); + break; } } } @@ -412,6 +428,43 @@ private void trackRevenueInternal(PackageBuilder revenueBuilder) { logger.debug(String.format(Locale.US, "Event %d (revenue)", activityState.eventCount)); } + private void readOpenUrlInternal(Uri url) { + if (url == null) + return; + + Map adjustDeepLinks = new HashMap(); + String queryString = url.getQuery(); + if (queryString == null) + return; + + String[] queryPairs = queryString.split("&"); + for (String pair : queryPairs) { + int splitIdx = pair.indexOf("="); + if (splitIdx == -1) + continue; + String key = Util.splitUrlUntil(pair, splitIdx); + if (key == null || !key.startsWith(ADJUST_PREFIX)) + continue; + String keyWoutPrefix = key.substring(ADJUST_PREFIX.length()); + String value = Util.splitUrlFrom(pair, splitIdx + 1); + if (keyWoutPrefix.length() > 0 + && value != null + && value.length() > 0) { + adjustDeepLinks.put(keyWoutPrefix, value); + } + } + if (adjustDeepLinks.size() > 0) { + PackageBuilder builder = new PackageBuilder(context); + injectGeneralAttributes(builder); + + ActivityPackage reattributionPackage = builder.buildReattributionPackage(); + packageHandler.addPackage(reattributionPackage); + packageHandler.sendFirstPackage(); + + logger.debug(String.format("Reattribution %s", adjustDeepLinks.toString())); + } + } + private boolean canTrackEvent(PackageBuilder revenueBuilder) { return checkAppTokenNotNull(appToken) && checkActivityState(activityState) diff --git a/Adjust/src/com/adjust/sdk/ActivityKind.java b/Adjust/src/com/adjust/sdk/ActivityKind.java index 3d57697ce..32942b616 100644 --- a/Adjust/src/com/adjust/sdk/ActivityKind.java +++ b/Adjust/src/com/adjust/sdk/ActivityKind.java @@ -1,7 +1,7 @@ package com.adjust.sdk; public enum ActivityKind { - UNKNOWN, SESSION, EVENT, REVENUE; + UNKNOWN, SESSION, EVENT, REVENUE, REATTRIBUTION; public static ActivityKind fromString(String string) { if ("session".equals(string)) { @@ -10,17 +10,21 @@ public static ActivityKind fromString(String 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"; - default: return "unknown"; + 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/Adjust.java b/Adjust/src/com/adjust/sdk/Adjust.java index bee0ec505..eea8bb223 100644 --- a/Adjust/src/com/adjust/sdk/Adjust.java +++ b/Adjust/src/com/adjust/sdk/Adjust.java @@ -14,6 +14,7 @@ import java.util.Map; import android.app.Activity; +import android.net.Uri; /** * The main interface to Adjust. @@ -148,6 +149,16 @@ public static Boolean isEnabled() { return false; } + public static void appWillOpenUrl(Uri url) { + try { + activityHandler.readOpenUrl(url); + } catch (NullPointerException e) { + if (logger != null) + logger.error(NO_ACTIVITY_HANDLER_FOUND); + } + + } + // Special appDidLaunch method used by SDK wrappers such as our Adobe Air SDK. protected static void appDidLaunch(Activity activity, String appToken, String environment, String logLevel, boolean eventBuffering) { diff --git a/Adjust/src/com/adjust/sdk/PackageBuilder.java b/Adjust/src/com/adjust/sdk/PackageBuilder.java index 0f3c1db35..55614d3ae 100644 --- a/Adjust/src/com/adjust/sdk/PackageBuilder.java +++ b/Adjust/src/com/adjust/sdk/PackageBuilder.java @@ -50,6 +50,9 @@ public class PackageBuilder { private double amountInCents; private Map callbackParameters; + // reattributions + private Map deepLinkParameters; + public PackageBuilder(Context context) { this.context = context; @@ -147,6 +150,10 @@ public void setCallbackParameters(Map callbackParameters) { this.callbackParameters = callbackParameters; } + public void setDeepLinkParameters(Map deepLinkParameters) { + this.deepLinkParameters = deepLinkParameters; + } + public boolean isValidForEvent() { if (null == eventToken) { Logger logger = AdjustFactory.getLogger(); @@ -210,6 +217,19 @@ public ActivityPackage buildRevenuePackage() { return revenuePackage; } + public ActivityPackage buildReattributionPackage() { + Map parameters = getDefaultParameters(); + addMap(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(); diff --git a/Adjust/src/com/adjust/sdk/Util.java b/Adjust/src/com/adjust/sdk/Util.java index 06f1eb262..2c0ad4075 100644 --- a/Adjust/src/com/adjust/sdk/Util.java +++ b/Adjust/src/com/adjust/sdk/Util.java @@ -26,6 +26,7 @@ import java.io.FileReader; import java.io.IOException; import java.math.BigInteger; +import java.net.URLDecoder; import java.security.MessageDigest; import java.text.SimpleDateFormat; import java.util.Locale; @@ -365,4 +366,27 @@ public static String getGpsAdid(Context context) { return gpsAdid; } + + public static String splitUrlUntil(String url, int splitIdx) { + String leftSide = null; + try { + leftSide = URLDecoder.decode(url.substring(0, splitIdx), "UTF-8"); + } catch (Exception e) { + Logger logger = AdjustFactory.getLogger(); + logger.error(String.format("Unable to split url %s until %d index (%s)", url, splitIdx, e.getMessage())); + } + return leftSide; + } + + public static String splitUrlFrom(String url, int splitIdx) { + String rightSide = null; + try { + rightSide = URLDecoder.decode(url.substring(splitIdx), "UTF-8"); + } catch (Exception e) { + Logger logger = AdjustFactory.getLogger(); + logger.error(String.format("Unable to split url %s from %d index (%s)", url, splitIdx, e.getMessage())); + } + return rightSide; + } + } From 6985138911d30441ec053e22ff7e0d2ec6082b6c Mon Sep 17 00:00:00 2001 From: Pedro Date: Tue, 15 Apr 2014 11:46:22 +0200 Subject: [PATCH 02/12] Deleted tabs for spaces --- Adjust/src/com/adjust/sdk/Util.java | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/Adjust/src/com/adjust/sdk/Util.java b/Adjust/src/com/adjust/sdk/Util.java index 2c0ad4075..f5b0d74aa 100644 --- a/Adjust/src/com/adjust/sdk/Util.java +++ b/Adjust/src/com/adjust/sdk/Util.java @@ -368,25 +368,24 @@ public static String getGpsAdid(Context context) { } public static String splitUrlUntil(String url, int splitIdx) { - String leftSide = null; - try { + String leftSide = null; + try { leftSide = URLDecoder.decode(url.substring(0, splitIdx), "UTF-8"); } catch (Exception e) { Logger logger = AdjustFactory.getLogger(); logger.error(String.format("Unable to split url %s until %d index (%s)", url, splitIdx, e.getMessage())); } - return leftSide; - } - - public static String splitUrlFrom(String url, int splitIdx) { - String rightSide = null; - try { - rightSide = URLDecoder.decode(url.substring(splitIdx), "UTF-8"); - } catch (Exception e) { - Logger logger = AdjustFactory.getLogger(); - logger.error(String.format("Unable to split url %s from %d index (%s)", url, splitIdx, e.getMessage())); - } - return rightSide; - } + return leftSide; + } + public static String splitUrlFrom(String url, int splitIdx) { + String rightSide = null; + try { + rightSide = URLDecoder.decode(url.substring(splitIdx), "UTF-8"); + } catch (Exception e) { + Logger logger = AdjustFactory.getLogger(); + logger.error(String.format("Unable to split url %s from %d index (%s)", url, splitIdx, e.getMessage())); + } + return rightSide; + } } From e3bdd0aab69d63d8ae080832f071a6616d647d15 Mon Sep 17 00:00:00 2001 From: Pedro Date: Tue, 15 Apr 2014 11:47:03 +0200 Subject: [PATCH 03/12] Deep link tests --- .../src/com/adjust/sdk/ActivityHandler.java | 1 + .../adjust/sdk/test/TestActivityHandler.java | 67 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/Adjust/src/com/adjust/sdk/ActivityHandler.java b/Adjust/src/com/adjust/sdk/ActivityHandler.java index 3b740d500..348a68f22 100644 --- a/Adjust/src/com/adjust/sdk/ActivityHandler.java +++ b/Adjust/src/com/adjust/sdk/ActivityHandler.java @@ -456,6 +456,7 @@ private void readOpenUrlInternal(Uri url) { if (adjustDeepLinks.size() > 0) { PackageBuilder builder = new PackageBuilder(context); injectGeneralAttributes(builder); + builder.setDeepLinkParameters(adjustDeepLinks); ActivityPackage reattributionPackage = builder.buildReattributionPackage(); packageHandler.addPackage(reattributionPackage); diff --git a/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java b/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java index 8b1fa15c9..fe6fd8ec3 100644 --- a/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java +++ b/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java @@ -4,10 +4,12 @@ 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.Logger.LogLevel; @@ -570,4 +572,69 @@ public void testDisable() { 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(), + "eyJmb28iOiJiYXIiLCJrZXkiOiJ2YWx1ZSJ9", parameters.get("deeplink_parameters")); + + } } From 3267a784d0048d1d88e2f4db7df82ed813e255d1 Mon Sep 17 00:00:00 2001 From: Pedro Date: Tue, 15 Apr 2014 12:07:16 +0200 Subject: [PATCH 04/12] Changed debug levels --- .../src/com/adjust/sdk/ActivityHandler.java | 4 ++-- .../adjust/sdk/test/TestActivityHandler.java | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Adjust/src/com/adjust/sdk/ActivityHandler.java b/Adjust/src/com/adjust/sdk/ActivityHandler.java index 348a68f22..0e3ecfc01 100644 --- a/Adjust/src/com/adjust/sdk/ActivityHandler.java +++ b/Adjust/src/com/adjust/sdk/ActivityHandler.java @@ -462,7 +462,7 @@ private void readOpenUrlInternal(Uri url) { packageHandler.addPackage(reattributionPackage); packageHandler.sendFirstPackage(); - logger.debug(String.format("Reattribution %s", adjustDeepLinks.toString())); + logger.info(String.format("Reattribution %s", adjustDeepLinks.toString())); } } @@ -540,7 +540,7 @@ private void writeActivityState() { try { objectStream.writeObject(activityState); - logger.verbose(String.format("Wrote activity state: %s", activityState)); + logger.debug(String.format("Wrote activity state: %s", activityState)); } catch (NotSerializableException e) { logger.error("Failed to serialize activity state"); } finally { diff --git a/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java b/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java index fe6fd8ec3..bb7e8aa80 100644 --- a/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java +++ b/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java @@ -127,7 +127,7 @@ public void testFirstSession() { // check that the activity state is written by the first session or timer assertTrue(mockLogger.toString(), - mockLogger.containsMessage(LogLevel.VERBOSE, "Wrote activity state")); + mockLogger.containsMessage(LogLevel.DEBUG, "Wrote activity state")); // ending of first session assertTrue(mockLogger.toString(), @@ -253,7 +253,7 @@ public void testEventsBuffered() { // check the event count in the written activity state assertTrue(mockLogger.toString(), - mockLogger.containsMessage(LogLevel.VERBOSE, "Wrote activity state: ec:1")); + mockLogger.containsMessage(LogLevel.DEBUG, "Wrote activity state: ec:1")); // check the event count in the logger assertTrue(mockLogger.toString(), @@ -295,7 +295,7 @@ public void testEventsBuffered() { // check the event count in the written activity state assertTrue(mockLogger.toString(), - mockLogger.containsMessage(LogLevel.VERBOSE, "Wrote activity state: ec:2")); + mockLogger.containsMessage(LogLevel.DEBUG, "Wrote activity state: ec:2")); // check the event count in the logger assertTrue(mockLogger.toString(), @@ -357,7 +357,7 @@ public void testEventsNotBuffered() { // check the event count in the written activity state assertTrue(mockLogger.toString(), - mockLogger.containsMessage(LogLevel.VERBOSE, "Wrote activity state: ec:1")); + mockLogger.containsMessage(LogLevel.DEBUG, "Wrote activity state: ec:1")); // check the event count in the logger assertTrue(mockLogger.toString(), @@ -399,7 +399,7 @@ public void testEventsNotBuffered() { // check the event count in the written activity state assertTrue(mockLogger.toString(), - mockLogger.containsMessage(LogLevel.VERBOSE, "Wrote activity state: ec:2")); + mockLogger.containsMessage(LogLevel.DEBUG, "Wrote activity state: ec:2")); // check the event count in the logger assertTrue(mockLogger.toString(), @@ -636,5 +636,14 @@ public void testOpenUrl() { assertEquals(activityPackage.getExtendedString(), "eyJmb28iOiJiYXIiLCJrZXkiOiJ2YWx1ZSJ9", 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.INFO, "Reattribution {key=value, foo=bar}")); } } From eafae14e72349500eb716ef40a9a93f1f990ad56 Mon Sep 17 00:00:00 2001 From: Pedro Filipe Date: Tue, 15 Apr 2014 15:51:33 +0200 Subject: [PATCH 05/12] Update Readme with deep linking --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 2acb49219..38e1d1b89 100644 --- a/README.md +++ b/README.md @@ -331,6 +331,23 @@ Adjust.setEnabled(false); 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`. +### 14. Handle deep linking + +You can also set up the adjust sdk to read deep links that come to your app. We will read only the data that is injected by adjust when you use deep links with adjust tracker URLs. This is a core feature if you are planning to run retargeting or re-engagement campaigns with deep links. + +On the activities that accept deep links, find the `onCreate` method and add the folowing call to adjust: + +```java +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + Uri data = intent.getData(); + Adjust.appWillOpenUrl(data); + //... +} +``` + [adjust.io]: http://adjust.io [dashboard]: http://adjust.io [releases]: https://github.com/adjust/adjust_android_sdk/releases From 373348763639910fb6ffbec37316b82595df6a5a Mon Sep 17 00:00:00 2001 From: Pedro Date: Tue, 15 Apr 2014 15:56:11 +0200 Subject: [PATCH 06/12] Changed version to v3.3.0 --- Adjust/build.gradle | 2 +- Adjust/pom.xml | 2 +- Adjust/src/com/adjust/sdk/Constants.java | 2 +- .../test/src/com/adjust/sdk/test/TestActivityHandler.java | 2 +- VERSION | 2 +- doc/migrate.md | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Adjust/build.gradle b/Adjust/build.gradle index b2fc5a8c2..1864f6d42 100644 --- a/Adjust/build.gradle +++ b/Adjust/build.gradle @@ -24,7 +24,7 @@ android { compileSdkVersion 19 defaultConfig { versionCode 11 - versionName '3.2.0' + versionName '3.3.0' minSdkVersion 8 targetSdkVersion 19 } diff --git a/Adjust/pom.xml b/Adjust/pom.xml index 2526a7254..fcc97f2d0 100644 --- a/Adjust/pom.xml +++ b/Adjust/pom.xml @@ -5,7 +5,7 @@ 4.0.0 adjust-android com.adjust.sdk - 3.2.0 + 3.3.0 jar UTF-8 diff --git a/Adjust/src/com/adjust/sdk/Constants.java b/Adjust/src/com/adjust/sdk/Constants.java index fdee2ec34..b1452e100 100644 --- a/Adjust/src/com/adjust/sdk/Constants.java +++ b/Adjust/src/com/adjust/sdk/Constants.java @@ -19,7 +19,7 @@ public interface Constants { int THIRTY_MINUTES = 30 * ONE_MINUTE; String BASE_URL = "https://app.adjust.io"; - String CLIENT_SDK = "android3.2.0"; + String CLIENT_SDK = "android3.3.0"; String LOGTAG = "Adjust"; String SESSION_STATE_FILENAME = "AdjustIoActivityState"; diff --git a/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java b/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java index bb7e8aa80..ad458067a 100644 --- a/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java +++ b/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java @@ -92,7 +92,7 @@ public void testFirstSession() { // check the Sdk version is being tested assertEquals(activityPackage.getExtendedString(), - "android3.2.0", activityPackage.getClientSdk()); + "android3.3.0", activityPackage.getClientSdk()); Map parameters = activityPackage.getParameters(); diff --git a/VERSION b/VERSION index 944880fa1..15a279981 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.2.0 +3.3.0 diff --git a/doc/migrate.md b/doc/migrate.md index a81730331..872a88e81 100644 --- a/doc/migrate.md +++ b/doc/migrate.md @@ -1,4 +1,4 @@ -## Migrate your adjust SDK for Android to 3.2.0 from v2.1.x +## Migrate your adjust SDK for Android to 3.3.0 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. @@ -24,7 +24,7 @@ We renamed the main class `com.adeven.adjustio.AdjustIo` to 4. In the same fashion, replace `adeven.adjustio` with `adjust.sdk` in all manifest files to update the package name of the `ReferrerReceiver`. -5. Download version v3.2.0 and create a new Android project from the `Adjust` folder. +5. Download version v3.3.0 and create a new Android project from the `Adjust` folder. ![][import] @@ -36,7 +36,7 @@ We renamed the main class `com.adeven.adjustio.AdjustIo` to 8. Build your project to confirm that everything is properly connected again. -The adjust SDK v3.2.0 added delegate notifications. Check out the [README] for +The adjust SDK v3.3.0 added delegate notifications. Check out the [README] for details. From afe90fc8935850806ebe700305f548eae9507755 Mon Sep 17 00:00:00 2001 From: Pedro Date: Wed, 16 Apr 2014 13:52:36 +0200 Subject: [PATCH 07/12] Fixed reading fields on migrating devices --- Adjust/src/com/adjust/sdk/ActivityState.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Adjust/src/com/adjust/sdk/ActivityState.java b/Adjust/src/com/adjust/sdk/ActivityState.java index ec2ab40d5..0632dbde3 100644 --- a/Adjust/src/com/adjust/sdk/ActivityState.java +++ b/Adjust/src/com/adjust/sdk/ActivityState.java @@ -93,8 +93,19 @@ private void readObject(ObjectInputStream stream) throws NotActiveException, IOE lastActivity = fields.get("lastActivity", -1l); createdAt = fields.get("createdAt", -1l); lastInterval = fields.get("lastInterval", -1l); - uuid = (String)fields.get("uuid", null); - enabled = fields.get("enabled", true); + + // 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); + } catch (Exception e) { + Logger logger = AdjustFactory.getLogger(); + logger.debug(String.format("Unable to read new field in migration device with error (%s)", + e.getMessage())); + } // create UUID for migrating devices if (uuid == null) { From f7cc247a174edab1b18d4a68d5baac3d7a6c8e01 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Wed, 16 Apr 2014 15:19:59 +0200 Subject: [PATCH 08/12] Reword deep linking section in Readme --- README.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 38e1d1b89..355bd5bb8 100644 --- a/README.md +++ b/README.md @@ -320,27 +320,32 @@ event buffering by adding the following line to your Adjust settings in your ### 13. Disable tracking -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. +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. ```java Adjust.setEnabled(false); ``` -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`. +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`. ### 14. Handle deep linking -You can also set up the adjust sdk to read deep links that come to your app. We will read only the data that is injected by adjust when you use deep links with adjust tracker URLs. This is a core feature if you are planning to run retargeting or re-engagement campaigns with deep links. +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. -On the activities that accept deep links, find the `onCreate` method and add the folowing call to adjust: +For each activity that accepts deep links, find the `onCreate` method and add +the folowing call to adjust: ```java protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - + Intent intent = getIntent(); Uri data = intent.getData(); Adjust.appWillOpenUrl(data); From efe76946f129f6efc04bc2069ab7df813723f93e Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Wed, 16 Apr 2014 15:37:04 +0200 Subject: [PATCH 09/12] Refactor ActivityHandler.java --- .../src/com/adjust/sdk/ActivityHandler.java | 55 ++++++++++--------- .../adjust/sdk/test/TestActivityHandler.java | 2 +- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/Adjust/src/com/adjust/sdk/ActivityHandler.java b/Adjust/src/com/adjust/sdk/ActivityHandler.java index 0e3ecfc01..a57144a77 100644 --- a/Adjust/src/com/adjust/sdk/ActivityHandler.java +++ b/Adjust/src/com/adjust/sdk/ActivityHandler.java @@ -429,41 +429,46 @@ private void trackRevenueInternal(PackageBuilder revenueBuilder) { } private void readOpenUrlInternal(Uri url) { - if (url == null) + if (url == null) { return; + } - Map adjustDeepLinks = new HashMap(); String queryString = url.getQuery(); - if (queryString == null) + if (queryString == null) { return; + } + + Map adjustDeepLinks = new HashMap(); String[] queryPairs = queryString.split("&"); for (String pair : queryPairs) { - int splitIdx = pair.indexOf("="); - if (splitIdx == -1) - continue; - String key = Util.splitUrlUntil(pair, splitIdx); - if (key == null || !key.startsWith(ADJUST_PREFIX)) - continue; - String keyWoutPrefix = key.substring(ADJUST_PREFIX.length()); - String value = Util.splitUrlFrom(pair, splitIdx + 1); - if (keyWoutPrefix.length() > 0 - && value != null - && value.length() > 0) { - adjustDeepLinks.put(keyWoutPrefix, value); - } - } - if (adjustDeepLinks.size() > 0) { - PackageBuilder builder = new PackageBuilder(context); - injectGeneralAttributes(builder); - builder.setDeepLinkParameters(adjustDeepLinks); + String[] pairComponents = pair.split("="); + if (pairComponents.length != 2) continue; - ActivityPackage reattributionPackage = builder.buildReattributionPackage(); - packageHandler.addPackage(reattributionPackage); - packageHandler.sendFirstPackage(); + String key = pairComponents[0]; + if (!key.startsWith(ADJUST_PREFIX)) continue; + + String value = pairComponents[1]; + if (value.length() == 0) continue; - logger.info(String.format("Reattribution %s", adjustDeepLinks.toString())); + 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(String.format("Reattribution %s", adjustDeepLinks.toString())); } private boolean canTrackEvent(PackageBuilder revenueBuilder) { diff --git a/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java b/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java index ad458067a..acd78f9ec 100644 --- a/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java +++ b/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java @@ -644,6 +644,6 @@ public void testOpenUrl() { // check that sent the reattribution package assertTrue(mockLogger.toString(), - mockLogger.containsMessage(LogLevel.INFO, "Reattribution {key=value, foo=bar}")); + mockLogger.containsMessage(LogLevel.DEBUG, "Reattribution {key=value, foo=bar}")); } } From 2999f98d12409c12f531587e44558eabb5d803aa Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Wed, 16 Apr 2014 15:40:08 +0200 Subject: [PATCH 10/12] Add comment about where to put new ActivityState fields --- Adjust/src/com/adjust/sdk/ActivityState.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Adjust/src/com/adjust/sdk/ActivityState.java b/Adjust/src/com/adjust/sdk/ActivityState.java index 0632dbde3..01c212b7f 100644 --- a/Adjust/src/com/adjust/sdk/ActivityState.java +++ b/Adjust/src/com/adjust/sdk/ActivityState.java @@ -101,6 +101,7 @@ private void readObject(ObjectInputStream stream) throws NotActiveException, IOE 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(String.format("Unable to read new field in migration device with error (%s)", From 38dbdd8106a134b88001ad1f0d421c6cf3b44be5 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Wed, 16 Apr 2014 15:43:40 +0200 Subject: [PATCH 11/12] Remove unused util functions --- Adjust/src/com/adjust/sdk/Util.java | 65 ++++++++++------------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/Adjust/src/com/adjust/sdk/Util.java b/Adjust/src/com/adjust/sdk/Util.java index f5b0d74aa..64b8daf96 100644 --- a/Adjust/src/com/adjust/sdk/Util.java +++ b/Adjust/src/com/adjust/sdk/Util.java @@ -26,7 +26,6 @@ import java.io.FileReader; import java.io.IOException; import java.math.BigInteger; -import java.net.URLDecoder; import java.security.MessageDigest; import java.text.SimpleDateFormat; import java.util.Locale; @@ -68,19 +67,19 @@ protected static String getUserAgent(final Context context) { 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) + 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); } @@ -352,40 +351,18 @@ public static String dateFormat(long date) { return dateFormat.format(date); } - public static String getGpsAdid(Context context) { - String gpsAdid = null; - try { - AdvertisingIdClient.Info info = AdvertisingIdClient.getAdvertisingIdInfo(context); - if (!info.isLimitAdTrackingEnabled()) { - gpsAdid = info.getId(); - } - } catch (Exception e) { - Logger logger = AdjustFactory.getLogger(); - logger.error(String.format("Error getting Google Play Services advertising ID, (%s)", e.getMessage())); - } - - return gpsAdid; - } - - public static String splitUrlUntil(String url, int splitIdx) { - String leftSide = null; + public static String getGpsAdid(Context context) { + String gpsAdid = null; try { - leftSide = URLDecoder.decode(url.substring(0, splitIdx), "UTF-8"); + AdvertisingIdClient.Info info = AdvertisingIdClient.getAdvertisingIdInfo(context); + if (!info.isLimitAdTrackingEnabled()) { + gpsAdid = info.getId(); + } } catch (Exception e) { Logger logger = AdjustFactory.getLogger(); - logger.error(String.format("Unable to split url %s until %d index (%s)", url, splitIdx, e.getMessage())); + logger.error(String.format("Error getting Google Play Services advertising ID, (%s)", e.getMessage())); } - return leftSide; - } - public static String splitUrlFrom(String url, int splitIdx) { - String rightSide = null; - try { - rightSide = URLDecoder.decode(url.substring(splitIdx), "UTF-8"); - } catch (Exception e) { - Logger logger = AdjustFactory.getLogger(); - logger.error(String.format("Unable to split url %s from %d index (%s)", url, splitIdx, e.getMessage())); - } - return rightSide; + return gpsAdid; } } From 2d1b273823e9c0ce8cd8230ecc717d01b572a175 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Wed, 16 Apr 2014 15:51:02 +0200 Subject: [PATCH 12/12] Use JSON encoding for deeplink parameters --- Adjust/src/com/adjust/sdk/PackageBuilder.java | 19 +++++++++++++++---- .../adjust/sdk/test/TestActivityHandler.java | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Adjust/src/com/adjust/sdk/PackageBuilder.java b/Adjust/src/com/adjust/sdk/PackageBuilder.java index 55614d3ae..3445a1566 100644 --- a/Adjust/src/com/adjust/sdk/PackageBuilder.java +++ b/Adjust/src/com/adjust/sdk/PackageBuilder.java @@ -21,7 +21,7 @@ public class PackageBuilder { - private Context context; + private Context context; // general private String appToken; @@ -219,7 +219,7 @@ public ActivityPackage buildRevenuePackage() { public ActivityPackage buildReattributionPackage() { Map parameters = getDefaultParameters(); - addMap(parameters, "deeplink_parameters", deepLinkParameters); + addMapJson(parameters, "deeplink_parameters", deepLinkParameters); ActivityPackage reattributionPackage = getDefaultActivityPackage(); reattributionPackage.setPath("/reattribute"); @@ -273,7 +273,7 @@ private Map getDefaultParameters() { private void injectEventParameters(Map parameters) { addInt(parameters, "event_count", eventCount); addString(parameters, "event_token", eventToken); - addMap(parameters, "params", callbackParameters); + addMapBase64(parameters, "params", callbackParameters); } private String getAmountString() { @@ -329,7 +329,7 @@ private void addDuration(Map parameters, String key, long durati addInt(parameters, key, durationInSeconds); } - private void addMap(Map parameters, String key, Map map) { + private void addMapBase64(Map parameters, String key, Map map) { if (null == map) { return; } @@ -340,4 +340,15 @@ private void addMap(Map parameters, String key, Map parameters, String key, Map map) { + if (null == map) { + return; + } + + JSONObject jsonObject = new JSONObject(map); + String jsonString = jsonObject.toString(); + + addString(parameters, key, jsonString); + } } diff --git a/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java b/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java index acd78f9ec..22bbe28db 100644 --- a/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java +++ b/Adjust/test/src/com/adjust/sdk/test/TestActivityHandler.java @@ -634,7 +634,7 @@ public void testOpenUrl() { // check that deep link parameters contains the base64 with the 2 keys assertEquals(activityPackage.getExtendedString(), - "eyJmb28iOiJiYXIiLCJrZXkiOiJ2YWx1ZSJ9", parameters.get("deeplink_parameters")); + "{\"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"));