From 439511b4073d295faae32576ca629051dd084d0e Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Mon, 6 May 2013 11:33:32 +0200 Subject: [PATCH 01/37] Rename eventId to eventToken, adeven/adjust#340 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9691994ab..b74d76f10 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Build and run your Android app. In your LogCat viewer you can set the filter `ta Once you have integrated the AdjustIo SDK into you project, you can take advantage of the following features wherever you see fit. ### Add tracking of custom events. -You can tell AdjustIo about every event you consider to be of your interest. Suppose you want to track every tap on a button. Currently you would have to ask us for an eventId and we would give you one, like `abc123`. In your button's onClick method you could then add the following code to track the click: +You can tell AdjustIo about every event you consider to be of your interest. Suppose you want to track every tap on a button. Currently you would have to ask us for an event token and we would give you one, like `abc123`. In your button's onClick method you could then add the following code to track the click: AdjustIo.trackEvent("abc123"); @@ -74,7 +74,7 @@ If your users can generate revenue by clicking on advertisements you can track t AdjustIo.trackRevenue(1.0f); -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 eventIds for each kind. Again, you need to ask us for eventIds that you can then use. In that case you would make a call like this: +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 ask us for event tokens that you can then use. In that case you would make a call like this: AdjustIo.trackRevenue(1.0f, "abc123"); From a419a0e572e39539d0445c73dd9885d3263a89c4 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Mon, 6 May 2013 18:21:57 +0200 Subject: [PATCH 02/37] Use java highlighted code blocks in readme --- README.md | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b74d76f10..2ecb4a35d 100644 --- a/README.md +++ b/README.md @@ -33,16 +33,20 @@ In the left pane select `Android`. In the bottom right group `Library` click the 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. - - +```java + + +``` ![][permissions] In the Package Explorer open the launch activity of your Android App. Add the `import` statement to the top of the source file. In the `onCreate` method of your activity call the method `appDidLaunch`. This tells AdjustIo about the launch of your Application. - import com.adeven.adjustio.AdjustIo; - // ... - AdjustIo.appDidLaunch(getApplication()); +```java +import com.adeven.adjustio.AdjustIo; +// ... +AdjustIo.appDidLaunch(getApplication()); +``` ![][activity] @@ -58,7 +62,9 @@ Once you have integrated the AdjustIo SDK into you project, you can take advanta ### Add tracking of custom events. You can tell AdjustIo about every event you consider to be of your interest. Suppose you want to track every tap on a button. Currently you would have to ask us for an event token and we would give you one, like `abc123`. In your button's onClick method you could then add the following code to track the click: - AdjustIo.trackEvent("abc123"); +```java +AdjustIo.trackEvent("abc123"); +``` You can also register a callback URL for that event and we will send a request to that URL whenever the event happens. Additianally you can put some key-value-pairs in a Map and pass it to the trackEvent method. In that case we will forward these named parameters to your callback URL. Suppose you registered the URL `http://www.adeven.com/callback` for your event and execute the following lines: @@ -72,18 +78,24 @@ In that case we would track the event and send a request to `http://www.adeven.c ### Add tracking of revenue If your users can generate revenue by clicking on advertisements you can track those revenues. If the click is worth one Cent, you could make the following call to track that revenue: - AdjustIo.trackRevenue(1.0f); +```java +AdjustIo.trackRevenue(1.0f); +``` 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 ask us for event tokens that you can then use. In that case you would make a call like this: - AdjustIo.trackRevenue(1.0f, "abc123"); +```java +AdjustIo.trackRevenue(1.0f, "abc123"); +``` You can also register a callback URL again and provide a map of named parameters, just like it worked with normal events. - Map parameters = new HashMap(); - parameters.put("key", "value"); - parameters.put("foo", "bar"); - AdjustIo.trackRevenue(1.0f, "abc123", parameters); +```java +Map parameters = new HashMap(); +parameters.put("key", "value"); +parameters.put("foo", "bar"); +AdjustIo.trackRevenue(1.0f, "abc123", parameters); +``` In any case, don't forget to import AdjustIo. Again, there is no point in sending parameters if you haven't registered a callback URL for that revenue event. From 99917c757169bd626d0c39cfa6dddc12acdf343b Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Mon, 6 May 2013 18:24:24 +0200 Subject: [PATCH 03/37] Add appToken, remove request parameter app_id adeven/adjust_backend#148 --- .../src/com/adeven/adjustio/AdjustIo.java | 26 +++++++++---------- README.md | 4 +-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java index 4d2c5fdac..bb6c86583 100644 --- a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java +++ b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java @@ -34,19 +34,19 @@ public class AdjustIo { * Generally obtained by calling getApplication() */ - public static void appDidLaunch(Context context) { + public static void appDidLaunch(String appToken, Context context) { if (!Util.checkPermissions(context)) { return; } String macAddress = Util.getMacAddress(context); - packageName = context.getPackageName(); - macSha1 = Util.sha1(macAddress); - macShort = macAddress.replaceAll(":", ""); - userAgent = Util.getUserAgent(context); - androidId = Util.getAndroidId(context); - attributionId = Util.getAttributionId(context); + AdjustIo.appToken = appToken; + AdjustIo.macSha1 = Util.sha1(macAddress); + AdjustIo.macShort = macAddress.replaceAll(":", ""); + AdjustIo.userAgent = Util.getUserAgent(context); + AdjustIo.androidId = Util.getAndroidId(context); + AdjustIo.attributionId = Util.getAttributionId(context); trackSessionStart(); } @@ -89,10 +89,10 @@ public static void trackEvent(String eventToken, Map parameters) .setSuccessMessage(successMessage) .setFailureMessage(failureMessage) .setUserAgent(userAgent) - .addTrackingParameter(EVENT_TOKEN, eventToken) - .addTrackingParameter(PACKAGE_NAME, packageName) + .addTrackingParameter(APP_TOKEN, appToken) .addTrackingParameter(MAC_SHORT, macShort) .addTrackingParameter(ANDROID_ID, androidId) + .addTrackingParameter(EVENT_TOKEN, eventToken) .addTrackingParameter(PARAMETERS, paramString) .build(); getRequestThread().track(event); @@ -149,7 +149,7 @@ public static void trackRevenue(float amountInCents, String eventToken, Map` with the App Token that you can find in your dashboard at [adjust.io][]. ```java import com.adeven.adjustio.AdjustIo; // ... -AdjustIo.appDidLaunch(getApplication()); +AdjustIo.appDidLaunch("", getApplication()); ``` ![][activity] From f1759af4e9555188d1ec90ff8fac75421c41657f Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Mon, 6 May 2013 11:33:32 +0200 Subject: [PATCH 04/37] Rename eventId to eventToken, adeven/adjust#340 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9691994ab..b74d76f10 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Build and run your Android app. In your LogCat viewer you can set the filter `ta Once you have integrated the AdjustIo SDK into you project, you can take advantage of the following features wherever you see fit. ### Add tracking of custom events. -You can tell AdjustIo about every event you consider to be of your interest. Suppose you want to track every tap on a button. Currently you would have to ask us for an eventId and we would give you one, like `abc123`. In your button's onClick method you could then add the following code to track the click: +You can tell AdjustIo about every event you consider to be of your interest. Suppose you want to track every tap on a button. Currently you would have to ask us for an event token and we would give you one, like `abc123`. In your button's onClick method you could then add the following code to track the click: AdjustIo.trackEvent("abc123"); @@ -74,7 +74,7 @@ If your users can generate revenue by clicking on advertisements you can track t AdjustIo.trackRevenue(1.0f); -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 eventIds for each kind. Again, you need to ask us for eventIds that you can then use. In that case you would make a call like this: +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 ask us for event tokens that you can then use. In that case you would make a call like this: AdjustIo.trackRevenue(1.0f, "abc123"); From 4906491a7960bff489a30fa1b7ebc310b58de50a Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Mon, 6 May 2013 18:21:57 +0200 Subject: [PATCH 05/37] Use java highlighted code blocks in readme --- README.md | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b74d76f10..2ecb4a35d 100644 --- a/README.md +++ b/README.md @@ -33,16 +33,20 @@ In the left pane select `Android`. In the bottom right group `Library` click the 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. - - +```java + + +``` ![][permissions] In the Package Explorer open the launch activity of your Android App. Add the `import` statement to the top of the source file. In the `onCreate` method of your activity call the method `appDidLaunch`. This tells AdjustIo about the launch of your Application. - import com.adeven.adjustio.AdjustIo; - // ... - AdjustIo.appDidLaunch(getApplication()); +```java +import com.adeven.adjustio.AdjustIo; +// ... +AdjustIo.appDidLaunch(getApplication()); +``` ![][activity] @@ -58,7 +62,9 @@ Once you have integrated the AdjustIo SDK into you project, you can take advanta ### Add tracking of custom events. You can tell AdjustIo about every event you consider to be of your interest. Suppose you want to track every tap on a button. Currently you would have to ask us for an event token and we would give you one, like `abc123`. In your button's onClick method you could then add the following code to track the click: - AdjustIo.trackEvent("abc123"); +```java +AdjustIo.trackEvent("abc123"); +``` You can also register a callback URL for that event and we will send a request to that URL whenever the event happens. Additianally you can put some key-value-pairs in a Map and pass it to the trackEvent method. In that case we will forward these named parameters to your callback URL. Suppose you registered the URL `http://www.adeven.com/callback` for your event and execute the following lines: @@ -72,18 +78,24 @@ In that case we would track the event and send a request to `http://www.adeven.c ### Add tracking of revenue If your users can generate revenue by clicking on advertisements you can track those revenues. If the click is worth one Cent, you could make the following call to track that revenue: - AdjustIo.trackRevenue(1.0f); +```java +AdjustIo.trackRevenue(1.0f); +``` 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 ask us for event tokens that you can then use. In that case you would make a call like this: - AdjustIo.trackRevenue(1.0f, "abc123"); +```java +AdjustIo.trackRevenue(1.0f, "abc123"); +``` You can also register a callback URL again and provide a map of named parameters, just like it worked with normal events. - Map parameters = new HashMap(); - parameters.put("key", "value"); - parameters.put("foo", "bar"); - AdjustIo.trackRevenue(1.0f, "abc123", parameters); +```java +Map parameters = new HashMap(); +parameters.put("key", "value"); +parameters.put("foo", "bar"); +AdjustIo.trackRevenue(1.0f, "abc123", parameters); +``` In any case, don't forget to import AdjustIo. Again, there is no point in sending parameters if you haven't registered a callback URL for that revenue event. From fc68f22a15e2e09860ce4978c3d482f37ecfe64f Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Mon, 6 May 2013 18:24:24 +0200 Subject: [PATCH 06/37] Add appToken, remove request parameter app_id adeven/adjust_backend#148 --- .../src/com/adeven/adjustio/AdjustIo.java | 26 +++++++++---------- README.md | 4 +-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java index 4d2c5fdac..bb6c86583 100644 --- a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java +++ b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java @@ -34,19 +34,19 @@ public class AdjustIo { * Generally obtained by calling getApplication() */ - public static void appDidLaunch(Context context) { + public static void appDidLaunch(String appToken, Context context) { if (!Util.checkPermissions(context)) { return; } String macAddress = Util.getMacAddress(context); - packageName = context.getPackageName(); - macSha1 = Util.sha1(macAddress); - macShort = macAddress.replaceAll(":", ""); - userAgent = Util.getUserAgent(context); - androidId = Util.getAndroidId(context); - attributionId = Util.getAttributionId(context); + AdjustIo.appToken = appToken; + AdjustIo.macSha1 = Util.sha1(macAddress); + AdjustIo.macShort = macAddress.replaceAll(":", ""); + AdjustIo.userAgent = Util.getUserAgent(context); + AdjustIo.androidId = Util.getAndroidId(context); + AdjustIo.attributionId = Util.getAttributionId(context); trackSessionStart(); } @@ -89,10 +89,10 @@ public static void trackEvent(String eventToken, Map parameters) .setSuccessMessage(successMessage) .setFailureMessage(failureMessage) .setUserAgent(userAgent) - .addTrackingParameter(EVENT_TOKEN, eventToken) - .addTrackingParameter(PACKAGE_NAME, packageName) + .addTrackingParameter(APP_TOKEN, appToken) .addTrackingParameter(MAC_SHORT, macShort) .addTrackingParameter(ANDROID_ID, androidId) + .addTrackingParameter(EVENT_TOKEN, eventToken) .addTrackingParameter(PARAMETERS, paramString) .build(); getRequestThread().track(event); @@ -149,7 +149,7 @@ public static void trackRevenue(float amountInCents, String eventToken, Map` with the App Token that you can find in your dashboard at [adjust.io][]. ```java import com.adeven.adjustio.AdjustIo; // ... -AdjustIo.appDidLaunch(getApplication()); +AdjustIo.appDidLaunch("", getApplication()); ``` ![][activity] From 06ef0fc7605f011fce74ebb73c3ec712c584751a Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Tue, 25 Jun 2013 10:43:34 +0200 Subject: [PATCH 07/37] Add multithreading --- .gitignore | 4 + .../src/com/adeven/adjustio/AdjustIo.java | 139 ++----- AdjustIo/src/com/adeven/adjustio/Logger.java | 2 + .../src/com/adeven/adjustio/QueueThread.java | 189 ++++++++++ .../com/adeven/adjustio/RequestThread.java | 110 +++--- .../src/com/adeven/adjustio/SessionState.java | 5 + .../com/adeven/adjustio/SessionThread.java | 354 ++++++++++++++++++ .../com/adeven/adjustio/TrackingPackage.java | 40 +- AdjustIo/src/com/adeven/adjustio/Util.java | 22 +- 9 files changed, 682 insertions(+), 183 deletions(-) create mode 100644 AdjustIo/src/com/adeven/adjustio/QueueThread.java create mode 100644 AdjustIo/src/com/adeven/adjustio/SessionState.java create mode 100644 AdjustIo/src/com/adeven/adjustio/SessionThread.java diff --git a/.gitignore b/.gitignore index 7d85933f8..e5b66406c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,7 @@ gen/ local.properties *.remote + +AdjustIo/.classpath +AdjustIo/.project +AdjustIo/.settings diff --git a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java index bb6c86583..d6d158250 100644 --- a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java +++ b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java @@ -11,7 +11,7 @@ import java.util.Map; -import android.content.Context; +import android.app.Activity; /** * The main interface to AdjustIo. @@ -24,6 +24,10 @@ */ public class AdjustIo { + // forwards everything to sessionThread + private static SessionThread sessionThread; + + // TODO: update all comments /** * Tell AdjustIo that the application did launch. * @@ -34,23 +38,20 @@ public class AdjustIo { * Generally obtained by calling getApplication() */ - public static void appDidLaunch(String appToken, Context context) { - if (!Util.checkPermissions(context)) { - return; + public static void onResume(String appToken, Activity activity) { + if (sessionThread == null) { + sessionThread = new SessionThread(appToken, activity.getApplication()); } - - String macAddress = Util.getMacAddress(context); - - AdjustIo.appToken = appToken; - AdjustIo.macSha1 = Util.sha1(macAddress); - AdjustIo.macShort = macAddress.replaceAll(":", ""); - AdjustIo.userAgent = Util.getUserAgent(context); - AdjustIo.androidId = Util.getAndroidId(context); - AdjustIo.attributionId = Util.getAttributionId(context); - - trackSessionStart(); + sessionThread.trackSubsessionStart(); } + public static void onPause() { + try { + sessionThread.trackSubsessionEnd(); + } catch (NullPointerException e) { + // TODO: log + } + } /** * Track any kind of event. @@ -71,31 +72,11 @@ public static void trackEvent(String eventToken) { } public static void trackEvent(String eventToken, Map parameters) { - if (eventToken.length() != 6) { - Logger.error( - "Event tracking only works with proper event tokens. " + - "Find them in your dashboard at http://www.adjust.io " + - "or contact support@adjust.io" - ); - return; + try { + sessionThread.trackEvent(eventToken, parameters); + } catch (NullPointerException e) { + // TODO: log } - - String paramString = Util.getBase64EncodedParameters(parameters); - String successMessage = "Tracked event: '" + eventToken + "'"; - String failureMessage = "Failed to track event: '" + eventToken + "'"; - - TrackingPackage event = new TrackingPackage.Builder() - .setPath("/event") - .setSuccessMessage(successMessage) - .setFailureMessage(failureMessage) - .setUserAgent(userAgent) - .addTrackingParameter(APP_TOKEN, appToken) - .addTrackingParameter(MAC_SHORT, macShort) - .addTrackingParameter(ANDROID_ID, androidId) - .addTrackingParameter(EVENT_TOKEN, eventToken) - .addTrackingParameter(PARAMETERS, paramString) - .build(); - getRequestThread().track(event); } @@ -122,41 +103,11 @@ public static void trackRevenue(float amountInCents, String eventToken) { } public static void trackRevenue(float amountInCents, String eventToken, Map parameters) { - if (eventToken != null && eventToken.length() != 6) { - Logger.error( - "Specific revenue tracking only works with proper event tokens. " + - "Find them in your dashboard at http://www.adjust.io " + - "or contact support@adjust.io" - ); - return; - } - - int amountInMillis = Math.round(10 * amountInCents); - amountInCents = amountInMillis/10.0f; // now rounded to one decimal point - String amount = Integer.toString(amountInMillis); - String paramString = Util.getBase64EncodedParameters(parameters); - String successMessage = "Tracked revenue: " + amountInCents + " Cent"; - String failureMessage = "Failed to track revenue: " + amountInCents + " Cent"; - - if (eventToken != null) { - String eventString = " (event token: '" + eventToken + "')"; - successMessage += eventString; - failureMessage += eventString; + try { + sessionThread.trackRevenue(amountInCents, eventToken, parameters); + } catch (NullPointerException e) { + // TODO: log } - - TrackingPackage revenue = new TrackingPackage.Builder() - .setPath("/revenue") - .setSuccessMessage(successMessage) - .setFailureMessage(failureMessage) - .setUserAgent(userAgent) - .addTrackingParameter(APP_TOKEN, appToken) - .addTrackingParameter(MAC_SHORT, macShort) - .addTrackingParameter(ANDROID_ID, androidId) - .addTrackingParameter(AMOUNT, amount) - .addTrackingParameter(EVENT_TOKEN, eventToken) - .addTrackingParameter(PARAMETERS, paramString) - .build(); - getRequestThread().track(revenue); } @@ -176,46 +127,4 @@ public static void trackRevenue(float amountInCents, String eventToken, Map packages; + + private static final String QUEUE_FILENAME = "testqueue"; + private static final int MESSAGE_ARG_ADD = 72500; // TODO: change numbers? + private static final int MESSAGE_ARG_REMOVE = 72510; + private static final int MESSAGE_ARG_READ = 72520; + private static final int MESSAGE_ARG_TRACK = 72530; + + // tracking loop: + // - q.addPackage + // - q.addInternal // or q.tryTrack + // - q.trackInternal (lock) // exception (unlock) + // - r.trackPackage + // - r.requestFinished + // - q.trackNext // or q.rejectFirst (unlock) + // - q.removeInternal (unlock) + // - q.trackInternal (repeat) + + // TODO: on session end: stop current tracking loop + // add an attribute that gets read before trackInternal starts + + public QueueThread(Context context) { + super(Logger.LOGTAG, MIN_PRIORITY); + setDaemon(true); + start(); + this.queueHandler = new PackageHandler(getLooper(), this); + + this.context = context; + this.isTracking = new AtomicBoolean(); + this.requestThread = new RequestThread(this); + + readPackages(); + } + + public void addPackage(TrackingPackage pack) { + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_ADD; + message.obj = pack; + queueHandler.sendMessage(message); + } + + public void tryTrackFirstPackage() { + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_TRACK; + queueHandler.sendMessage(message); + } + + public void trackNextPackage() { + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_REMOVE; + queueHandler.sendMessage(message); + } + + public void rejectFirstPackage() { + isTracking.set(false); + } + + private void readPackages() { + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_READ; + queueHandler.sendMessage(message); + } + + private static final class PackageHandler extends Handler { + private final WeakReference queueThreadReference; + + public PackageHandler(Looper looper, QueueThread queueThread) { + super(looper); + this.queueThreadReference = new WeakReference(queueThread); + } + + @Override + public void handleMessage(Message message) { + super.handleMessage(message); + + QueueThread queueThread = queueThreadReference.get(); + if (queueThread == null) { + return; + } else if (message.arg1 == MESSAGE_ARG_ADD) { + queueThread.addInternal((TrackingPackage) message.obj); + } else if (message.arg1 == MESSAGE_ARG_REMOVE) { + queueThread.removeInternal(); + } else if (message.arg1 == MESSAGE_ARG_READ) { + queueThread.readInternal(); + } else if (message.arg1 == MESSAGE_ARG_TRACK) { + queueThread.trackInternal(); + } + } + } + + // internal methods run in own thread + + // TODO: lock needed? test with sleep + private void addInternal(TrackingPackage pack) { + packages.add(pack); + writeInternal(); + + trackInternal(); + } + + // TODO: check package? + private void removeInternal() { + packages.remove(0); + writeInternal(); + isTracking.set(false); + + trackInternal(); + } + + // TODO: add lock + private void trackInternal() { + if (isTracking.getAndSet(true)) { + Logger.error("locked"); + return; + } + try { + TrackingPackage firstPackage = packages.get(0); + requestThread.trackPackage(firstPackage); + } catch (IndexOutOfBoundsException e) { + isTracking.set(false); + } + } + + @SuppressWarnings("unchecked") + private void readInternal() { + try { + FileInputStream inputStream = context.openFileInput(QUEUE_FILENAME); + BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); + ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); + try { + packages = (List)objectStream.readObject(); + Logger.error("packages " + packages.size()); + } finally { + objectStream.close(); + } + } catch (FileNotFoundException e) { + packages = new ArrayList(); + } catch (ClassNotFoundException e) { + Logger.error("class not found"); + } catch (IOException e) { + Logger.error("failed to read object"); + } + } + + private void writeInternal() { + try { Thread.sleep(100); } catch (Exception e) {} + + try { + FileOutputStream outputStream = context.openFileOutput(QUEUE_FILENAME, Context.MODE_PRIVATE); + BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); + ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream); + try { + objectStream.writeObject(packages); + } finally { + objectStream.close(); + } + } catch (IOException e) { + Logger.error("failed to write package"); // TODO: improve log + } + } +} diff --git a/AdjustIo/src/com/adeven/adjustio/RequestThread.java b/AdjustIo/src/com/adeven/adjustio/RequestThread.java index 4298acdf4..2f3f6c61f 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestThread.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestThread.java @@ -13,12 +13,15 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.ref.WeakReference; -import java.net.SocketException; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; +import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.entity.StringEntity; +import org.apache.http.protocol.HTTP; import android.os.Handler; import android.os.HandlerThread; @@ -26,7 +29,7 @@ import android.os.Message; /** - * Used to send tracking information. + * Used to send tracking packages. * * @author keyboardsurfer * @since 17.4.13 @@ -35,51 +38,85 @@ public class RequestThread extends HandlerThread { private static final int MESSAGE_ARG_TRACK = 72400; private Handler trackingHandler; + private QueueThread queueThread; - public RequestThread() { + public RequestThread(QueueThread queueThread) { super(Logger.LOGTAG, MIN_PRIORITY); setDaemon(true); start(); - trackingHandler = new RequestHandler(getLooper(), this); + + this.trackingHandler = new RequestHandler(getLooper(), this); + this.queueThread = queueThread; } - void track(TrackingPackage information) { + public void trackPackage(TrackingPackage pack) { Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_TRACK; - message.obj = information; + message.obj = pack; trackingHandler.sendMessage(message); } - private void trackInternal(TrackingPackage trackingInformation) { - HttpClient httpClient = Util.getHttpClient(trackingInformation.userAgent); - HttpPost request = Util.getPostRequest(trackingInformation.path); + private static final class RequestHandler extends Handler { + private final WeakReference requestThreadReference; + + public RequestHandler(Looper looper, RequestThread requestThread) { + super(looper); + this.requestThreadReference = new WeakReference(requestThread); + } + + public void handleMessage(Message message) { + super.handleMessage(message); + + RequestThread requestThread = requestThreadReference.get(); + if (requestThread == null) { + return; + } else if (message.arg1 == MESSAGE_ARG_TRACK) { + requestThread.trackInternal((TrackingPackage) message.obj); + } + } + } + + private void trackInternal(TrackingPackage trackingPackage) { + HttpClient httpClient = Util.getHttpClient(trackingPackage.userAgent); + HttpPost request = Util.getPostRequest(trackingPackage.path); + // TODO: test all paths! + // TODO: reject if unsure (because rejected packages will always be retried) try { - request.setEntity(Util.getEntityEncodedParameters(trackingInformation.parameters)); + StringEntity entity = new StringEntity(trackingPackage.parameters, HTTP.UTF_8); + entity.setContentType(URLEncodedUtils.CONTENT_TYPE); + request.setEntity(entity); HttpResponse response = httpClient.execute(request); - Logger.info(getLogString(response, trackingInformation)); - } catch (SocketException e) { - Logger.error("This SDK requires the INTERNET permission. You might need to adjust your manifest. See the README for details."); + requestFinished(response, trackingPackage); } catch (UnsupportedEncodingException e) { - Logger.error("Failed to encode parameters."); + Logger.error("failed to encode parameters"); + queueThread.trackNextPackage(); + } catch (ClientProtocolException e) { + Logger.error("client protocol error"); + queueThread.rejectFirstPackage(); } catch (IOException e) { - Logger.error("Unexpected IOException", e); + Logger.error("connection failed (" + e.getLocalizedMessage() + ")"); + queueThread.rejectFirstPackage(); } } - private String getLogString(HttpResponse response, TrackingPackage trackingInformation) { - if (response == null) { - return trackingInformation.failureMessage + " (Request failed)"; - } else { - int statusCode = response.getStatusLine().getStatusCode(); - String responseString = parseResponse(response); + private void requestFinished(HttpResponse response, TrackingPackage trackingPackage) { + if (response == null) { // TODO: test + Logger.debug(trackingPackage.failureMessage + " (Request failed)"); // TODO: "will retry later" like on ios + queueThread.rejectFirstPackage(); + return; + } - if (statusCode == HttpStatus.SC_OK) { - return trackingInformation.successMessage; - } else { - return trackingInformation.failureMessage + " (" + responseString + ")"; - } + int statusCode = response.getStatusLine().getStatusCode(); + String responseString = parseResponse(response); + + if (statusCode == HttpStatus.SC_OK) { + Logger.info(trackingPackage.successMessage); + } else { + Logger.warn(trackingPackage.failureMessage + " (" + responseString + ")"); } + + queueThread.trackNextPackage(); } private String parseResponse(HttpResponse response) { @@ -94,25 +131,4 @@ private String parseResponse(HttpResponse response) { return "Failed parsing response"; } } - - private static final class RequestHandler extends Handler { - private final WeakReference requestThreadReference; - - public RequestHandler(Looper looper, RequestThread requestThread) { - super(looper); - this.requestThreadReference = new WeakReference(requestThread); - } - - @Override - public void handleMessage(Message message) { - super.handleMessage(message); - RequestThread requestThread = requestThreadReference.get(); - if (requestThread == null) { - return; - } - if (message.arg1 == MESSAGE_ARG_TRACK) { - requestThread.trackInternal((TrackingPackage) message.obj); - } - } - } } diff --git a/AdjustIo/src/com/adeven/adjustio/SessionState.java b/AdjustIo/src/com/adeven/adjustio/SessionState.java new file mode 100644 index 000000000..28312ce05 --- /dev/null +++ b/AdjustIo/src/com/adeven/adjustio/SessionState.java @@ -0,0 +1,5 @@ +package com.adeven.adjustio; + +public class SessionState { + +} diff --git a/AdjustIo/src/com/adeven/adjustio/SessionThread.java b/AdjustIo/src/com/adeven/adjustio/SessionThread.java new file mode 100644 index 000000000..dc0c8c307 --- /dev/null +++ b/AdjustIo/src/com/adeven/adjustio/SessionThread.java @@ -0,0 +1,354 @@ +package com.adeven.adjustio; + +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.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.ref.WeakReference; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; + +public class SessionThread extends HandlerThread { + private String appToken; + private Context context; + + private String macSha1; + private String macShort; + private String userAgent; + private String androidId; + private String attributionId; + + private Handler sessionHandler; + private SessionState sessionState; + private QueueThread queueThread; + private static ScheduledExecutorService executor; + + // TODO: move to TrackingPackage? + private static final String APP_TOKEN = "app_token"; + private static final String MAC_SHA1 = "mac_sha1"; + private static final String MAC_SHORT = "mac"; + private static final String ANDROID_ID = "android_id"; + private static final String ATTRIBUTION_ID = "fb_id"; + private static final String EVENT_TOKEN = "event_id"; + private static final String PARAMETERS = "params"; + private static final String AMOUNT = "amount"; + + private static final String SESSION_FILENAME = "sessionstate"; + private static final int MESSAGE_ARG_UPDATE = 72610; + private static final int MESSAGE_ARG_READ = 72620; + private static final int MESSAGE_ARG_INIT = 72630; + private static final int MESSAGE_ARG_START = 72640; + private static final int MESSAGE_ARG_END = 72650; + private static final int MESSAGE_ARG_EVENT= 72660; + private static final int MESSAGE_ARG_REVENUE= 72670; + + public SessionThread(String appToken, Context context) { + super(Logger.LOGTAG, MIN_PRIORITY); + setDaemon(true); + start(); + this.sessionHandler = new SessionHandler(getLooper(), this); + + this.appToken = appToken; + this.context = context; + + init(); + readSessionState(); + } + + public void trackSubsessionStart() { + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_START; + sessionHandler.sendMessage(message); + } + + public void trackSubsessionEnd() { + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_END; + sessionHandler.sendMessage(message); + } + + public void trackEvent(String eventToken, Map parameters) { + TrackingPackage.Builder builder = new TrackingPackage.Builder(); + builder.eventToken = eventToken; + builder.parameters = parameters; + + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_EVENT; + message.obj = builder; + sessionHandler.sendMessage(message); + } + + public void trackRevenue(float amountInCents, String eventToken, Map parameters) { + TrackingPackage.Builder builder = new TrackingPackage.Builder(); + builder.amountInCents = amountInCents; + builder.eventToken = eventToken; + builder.parameters = parameters; + + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_REVENUE; + message.obj = builder; + sessionHandler.sendMessage(message); + } + + public void updateLastActivity() { + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_UPDATE; + sessionHandler.sendMessage(message); + } + + private void init() { + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_INIT; + sessionHandler.sendMessage(message); + } + + private void readSessionState() { + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_READ; + sessionHandler.sendMessage(message); + } + + private static final class SessionHandler extends Handler { + private final WeakReference sessionThreadReference; + + public SessionHandler(Looper looper, SessionThread sessionThread) { + super(looper); + this.sessionThreadReference = new WeakReference(sessionThread); + } + + @Override + public void handleMessage(Message message) { + super.handleMessage(message); + + SessionThread sessionThread = sessionThreadReference.get(); + if (sessionThread == null) { + return; + } else if (message.arg1 == MESSAGE_ARG_UPDATE) { + sessionThread.updateInternal(); + } else if (message.arg1 == MESSAGE_ARG_READ) { + sessionThread.readInternal(); + } else if (message.arg1 == MESSAGE_ARG_INIT) { + sessionThread.initInternal(); + } else if (message.arg1 == MESSAGE_ARG_START) { + sessionThread.startInternal(); + } else if (message.arg1 == MESSAGE_ARG_END) { + sessionThread.endInternal(); + } else if (message.arg1 == MESSAGE_ARG_EVENT) { + TrackingPackage.Builder builder = (TrackingPackage.Builder)message.obj; + sessionThread.eventInternal(builder); + } else if (message.arg1 == MESSAGE_ARG_REVENUE) { + TrackingPackage.Builder builder = (TrackingPackage.Builder)message.obj; + sessionThread.revenueInternal(builder); + } + } + } + + private void updateInternal() { + Logger.info("update"); + } + + private void initInternal() { + if (!Util.checkPermissions(context)) return; + if (!checkAppToken(appToken)) return; + if (!checkContext(context)) return; + + String macAddress = Util.getMacAddress(context); + + macSha1 = Util.sha1(macAddress); + macShort = macAddress.replaceAll(":", ""); + userAgent = Util.getUserAgent(context); + androidId = Util.getAndroidId(context); + attributionId = Util.getAttributionId(context); + + queueThread = new QueueThread(context); + } + + private void startInternal() { + startExecutor(); + + TrackingPackage sessionStart = new TrackingPackage.Builder() + .setPath("/startup") + .setSuccessMessage("Tracked session start.") + .setFailureMessage("Failed to track session start.") + .setUserAgent(userAgent) + .addTrackingParameter(APP_TOKEN, appToken) + .addTrackingParameter(MAC_SHORT, macShort) + .addTrackingParameter(MAC_SHA1, macSha1) + .addTrackingParameter(ANDROID_ID, androidId) + .addTrackingParameter(ATTRIBUTION_ID, attributionId) + .build(); + + queueThread.addPackage(sessionStart); + } + + private void endInternal() { + stopExecutor(); + // TODO: update last activity + } + + private void eventInternal(TrackingPackage.Builder builder) { + // TODO: clean up the builder stuff: reuse builder or even use its eventToken and parameters + if (!checkEventTokenNotNull(builder.eventToken)) return; + if (!checkEventTokenLength(builder.eventToken)) return; + + String paramString = Util.getBase64EncodedParameters(builder.parameters); + String successMessage = "Tracked event: '" + builder.eventToken + "'"; + String failureMessage = "Failed to track event: '" + builder.eventToken + "'"; + + TrackingPackage eventPackage = new TrackingPackage.Builder() + .setPath("/event") + .setSuccessMessage(successMessage) + .setFailureMessage(failureMessage) + .setUserAgent(userAgent) + .addTrackingParameter(APP_TOKEN, appToken) + .addTrackingParameter(MAC_SHORT, macShort) + .addTrackingParameter(ANDROID_ID, androidId) + .addTrackingParameter(EVENT_TOKEN, builder.eventToken) + .addTrackingParameter(PARAMETERS, paramString) + .build(); + + queueThread.addPackage(eventPackage); + } + + private void revenueInternal(TrackingPackage.Builder builder) { + if (!checkEventTokenLength(builder.eventToken)) return; + + Logger.info("revenueInternal"); + // TODO: clean up and extract general event stuff? + + + int amountInMillis = Math.round(10 * builder.amountInCents); + float amountInCents = amountInMillis/10.0f; // now rounded to one decimal point + String amount = Integer.toString(amountInMillis); + String paramString = Util.getBase64EncodedParameters(builder.parameters); + String successMessage = "Tracked revenue: " + amountInCents + " Cent"; + String failureMessage = "Failed to track revenue: " + amountInCents + " Cent"; + + if (builder.eventToken != null) { + String eventString = " (event token: '" + builder.eventToken + "')"; + successMessage += eventString; + failureMessage += eventString; + } + + TrackingPackage revenuePackage = new TrackingPackage.Builder() + .setPath("/revenue") + .setSuccessMessage(successMessage) + .setFailureMessage(failureMessage) + .setUserAgent(userAgent) + .addTrackingParameter(APP_TOKEN, appToken) + .addTrackingParameter(MAC_SHORT, macShort) + .addTrackingParameter(ANDROID_ID, androidId) + .addTrackingParameter(AMOUNT, amount) + .addTrackingParameter(EVENT_TOKEN, builder.eventToken) + .addTrackingParameter(PARAMETERS, paramString) + .build(); + //getRequestThread().track(revenue); + queueThread.addPackage(revenuePackage); + } + + private void readInternal() { + try { + FileInputStream inputStream = context.openFileInput(SESSION_FILENAME); + BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); + ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); + try { + sessionState = (SessionState)objectStream.readObject(); + Logger.error("state"); + } finally { + objectStream.close(); + } + } catch (FileNotFoundException e) { + sessionState = new SessionState(); + } catch (ClassNotFoundException e) { + Logger.error("class not found"); + } catch (IOException e) { + Logger.error("failed to read object"); + } + } + + private void writeInternal() { + try { Thread.sleep(100); } catch (Exception e) {} + + try { + FileOutputStream outputStream = context.openFileOutput(SESSION_FILENAME, Context.MODE_PRIVATE); + BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); + ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream); + try { + objectStream.writeObject(sessionState); + } finally { + objectStream.close(); + } + } catch (IOException e) { + Logger.error("failed to write package"); // TODO: improve log + } + } + + private void startExecutor() { + stopExecutor(); + executor = Executors.newSingleThreadScheduledExecutor(); + executor.scheduleWithFixedDelay(new Runnable() { + public void run() { + Logger.info("timer"); + updateInternal(); + queueThread.tryTrackFirstPackage(); + } + }, 5, 5, TimeUnit.SECONDS); // TODO: one minute, extract constants + } + + private void stopExecutor() { + try { + executor.shutdown(); + } catch (NullPointerException e) { + // TODO: log? + } + } + + private static boolean checkAppToken(String appToken) { + if (appToken == null) { + Logger.error("Missing App Token."); + return false; + } else if (appToken.length() != 12) { + Logger.error("Malformed App Token " + appToken); + return false; + } + return true; + } + + private static boolean checkEventTokenNotNull(String eventToken) { + if (eventToken == null) { + Logger.error("Missing Event Token"); + return false; + } + return true; + } + + private static boolean checkEventTokenLength(String eventToken) { + if (eventToken == null) return true; + + if (eventToken.length() != 6) { + Logger.error("Malformed Event Token"); + return false; + } + return true; + } + + private static boolean checkContext(Context context) { + if (context == null) { + Logger.error("Missing context."); + return false; + } + return true; + } +} diff --git a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java index bb7849583..a6fb3a64c 100644 --- a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java @@ -9,11 +9,8 @@ package com.adeven.adjustio; -import java.util.ArrayList; -import java.util.List; - -import org.apache.http.NameValuePair; -import org.apache.http.message.BasicNameValuePair; +import java.io.Serializable; +import java.util.Map; /** * Holds information of one tracking package. @@ -21,14 +18,18 @@ * @author keyboardsurfer * @since 17.4.13 */ -public class TrackingPackage { +public class TrackingPackage implements Serializable { + /** + * + */ + private static final long serialVersionUID = -5435782033488813179L; final String path; final String successMessage; final String failureMessage; final String userAgent; - final List parameters; + final String parameters; - public TrackingPackage(String path, String successMessage, String failureMessage, String userAgent, List parameters) { + public TrackingPackage(String path, String successMessage, String failureMessage, String userAgent, String parameters) { this.path = path; this.successMessage = successMessage; this.failureMessage = failureMessage; @@ -37,18 +38,18 @@ public TrackingPackage(String path, String successMessage, String failureMessage } /** - * A builder to enable chained building of a RequestThread. + * A builder to enable chained building of a TrackingPackage. */ static class Builder { + public float amountInCents; + public String eventToken; + public Map parameters; + private String path; private String successMessage; private String failureMessage; private String userAgent; - private List parameters; - - Builder() { - parameters = new ArrayList(); - } + private String parameterString; Builder setPath(String path) { this.path = path; @@ -78,13 +79,18 @@ Builder addTrackingParameter(String key, String value) { return this; } - parameters.add(new BasicNameValuePair(key, value)); - Logger.verbose(path, key, value); + if (parameterString == null || parameterString == "") { + parameterString = key + "=" + value; + } else { + parameterString += "&" + key + "=" + value; + } + + Logger.verbose(path, key, value); // TODO: remove these logs here? return this; } TrackingPackage build() { - TrackingPackage trackingPackage = new TrackingPackage(path, successMessage, failureMessage, userAgent, parameters); + TrackingPackage trackingPackage = new TrackingPackage(path, successMessage, failureMessage, userAgent, parameterString); return trackingPackage; } } diff --git a/AdjustIo/src/com/adeven/adjustio/Util.java b/AdjustIo/src/com/adeven/adjustio/Util.java index 9834cf8ff..21e659df2 100644 --- a/AdjustIo/src/com/adeven/adjustio/Util.java +++ b/AdjustIo/src/com/adeven/adjustio/Util.java @@ -12,6 +12,8 @@ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.util.List; @@ -22,7 +24,6 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.CoreProtocolPNames; import org.apache.http.params.HttpParams; @@ -51,7 +52,7 @@ * @since 11.10.12 */ public class Util { - private static final String BASEURL = "https://app.adjust.io"; + private static final String BASEURL = "http://192.168.178.117:8509"; private static final String CLIENTSDK = "android1.6"; private static final String UNKNOWN = "unknown"; @@ -93,8 +94,21 @@ public static String getBase64EncodedParameters(Map parameters) return encoded; } - public static StringEntity getEntityEncodedParameters(List parameters) throws UnsupportedEncodingException { - StringEntity entity = new UrlEncodedFormEntity(parameters); + public static UrlEncodedFormEntity getEntityEncodedParameters(List parameters) throws UnsupportedEncodingException { + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters); + + try { + InputStream stream = entity.getContent(); + InputStreamReader reader = new InputStreamReader(stream); + BufferedReader buffer = new BufferedReader(reader); + String line; + while ((line = buffer.readLine()) != null) { + Logger.error("entity line: " + line); + } + } catch (IOException e) { + Logger.error("failed to read entity"); + } + return entity; } From ef56c794c6606e59a4a0cd93d32b4387b73ae264 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Tue, 25 Jun 2013 15:12:34 +0200 Subject: [PATCH 08/37] Extract PackageBuilder, begin session aggregation --- .../com/adeven/adjustio/PackageBuilder.java | 85 +++++++ .../src/com/adeven/adjustio/SessionState.java | 34 ++- .../com/adeven/adjustio/SessionThread.java | 228 +++++++++++------- .../com/adeven/adjustio/TrackingPackage.java | 64 +---- 4 files changed, 255 insertions(+), 156 deletions(-) create mode 100644 AdjustIo/src/com/adeven/adjustio/PackageBuilder.java diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java new file mode 100644 index 000000000..ff9fc7a09 --- /dev/null +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -0,0 +1,85 @@ +package com.adeven.adjustio; + +import java.util.Map; + +public class PackageBuilder { + // TODO: move somewhere else? + public float amountInCents; + public String eventToken; + public Map parameters; + + private String path; + private String successMessage; + private String failureMessage; + private String userAgent; + private String parameterString; + + PackageBuilder setPath(String path) { + this.path = path; + return this; + } + + PackageBuilder setUserAgent(String userAgent) { + this.userAgent = userAgent; + Logger.verbose(path, "userAgent", userAgent); + return this; + } + + PackageBuilder setSuccessMessage(String successMessage) { + this.successMessage = successMessage; + Logger.verbose(path, "successMessage", successMessage); + return this; + } + + PackageBuilder setFailureMessage(String failureMessage) { + this.failureMessage = failureMessage; + Logger.verbose(path, "failureMessage", failureMessage); + return this; + } + + PackageBuilder addTrackingParameter(String key, String value) { + if (value == null || value == "") { + return this; + } + + if (parameterString == null || parameterString == "") { + parameterString = key + "=" + value; + } else { + parameterString += "&" + key + "=" + value; + } + + Logger.verbose(path, key, value); // TODO: remove these logs here? + return this; + } + + TrackingPackage build() { + TrackingPackage trackingPackage = new TrackingPackage(path, + successMessage, failureMessage, userAgent, parameterString); + return trackingPackage; + } + + public void setSessionCount(int sessionCount) { + // TODO Auto-generated method stub + + } + + public void setSubsessionCount(int subsessionCount) { + // TODO Auto-generated method stub + + } + + public void setSessionLength(double sessionLength) { + // TODO Auto-generated method stub + + } + + public void setCreatedAt(long createdAt) { + // TODO Auto-generated method stub + + } + + public void setTimeSpent(double timeSpent) { + // TODO Auto-generated method stub + + } +} diff --git a/AdjustIo/src/com/adeven/adjustio/SessionState.java b/AdjustIo/src/com/adeven/adjustio/SessionState.java index 28312ce05..b0b1fbb97 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionState.java +++ b/AdjustIo/src/com/adeven/adjustio/SessionState.java @@ -1,5 +1,37 @@ package com.adeven.adjustio; -public class SessionState { +import java.io.Serializable; +public class SessionState implements Serializable { + private static final long serialVersionUID = 9039439291143138148L; + + // TODO: make attributes private? + + // global counters + public int eventCount; + public int sessionCount; + + // session attributes + public int subsessionCount; + public double sessionLength; + public double timeSpent; + public long createdAt; // all times in milliseconds since 1970 + public long lastSubsessionStart; + public long lastActivity; + + + public PackageBuilder getPackageBuilder() { + PackageBuilder builder = new PackageBuilder(); + + builder.setPath("/startup"); + builder.setSuccessMessage("Tracked session start."); + builder.setFailureMessage("Failed to track session start."); + builder.setSessionCount(sessionCount); + builder.setSubsessionCount(subsessionCount); + builder.setSessionLength(sessionLength); + builder.setTimeSpent(timeSpent); + builder.setCreatedAt(createdAt); + + return builder; + } } diff --git a/AdjustIo/src/com/adeven/adjustio/SessionThread.java b/AdjustIo/src/com/adeven/adjustio/SessionThread.java index dc0c8c307..8a6399634 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionThread.java +++ b/AdjustIo/src/com/adeven/adjustio/SessionThread.java @@ -9,6 +9,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.ref.WeakReference; +import java.util.Date; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -47,12 +48,13 @@ public class SessionThread extends HandlerThread { private static final String SESSION_FILENAME = "sessionstate"; private static final int MESSAGE_ARG_UPDATE = 72610; - private static final int MESSAGE_ARG_READ = 72620; private static final int MESSAGE_ARG_INIT = 72630; private static final int MESSAGE_ARG_START = 72640; private static final int MESSAGE_ARG_END = 72650; - private static final int MESSAGE_ARG_EVENT= 72660; - private static final int MESSAGE_ARG_REVENUE= 72670; + private static final int MESSAGE_ARG_EVENT = 72660; + private static final int MESSAGE_ARG_REVENUE = 72670; + + private static final long SESSION_INTERVAL = 1000 * 1; // 1 second, TODO: 30 minutes public SessionThread(String appToken, Context context) { super(Logger.LOGTAG, MIN_PRIORITY); @@ -63,8 +65,9 @@ public SessionThread(String appToken, Context context) { this.appToken = appToken; this.context = context; - init(); - readSessionState(); + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_INIT; + sessionHandler.sendMessage(message); } public void trackSubsessionStart() { @@ -80,7 +83,7 @@ public void trackSubsessionEnd() { } public void trackEvent(String eventToken, Map parameters) { - TrackingPackage.Builder builder = new TrackingPackage.Builder(); + PackageBuilder builder = new PackageBuilder(); builder.eventToken = eventToken; builder.parameters = parameters; @@ -90,8 +93,9 @@ public void trackEvent(String eventToken, Map parameters) { sessionHandler.sendMessage(message); } - public void trackRevenue(float amountInCents, String eventToken, Map parameters) { - TrackingPackage.Builder builder = new TrackingPackage.Builder(); + public void trackRevenue(float amountInCents, String eventToken, + Map parameters) { + PackageBuilder builder = new PackageBuilder(); builder.amountInCents = amountInCents; builder.eventToken = eventToken; builder.parameters = parameters; @@ -108,27 +112,15 @@ public void updateLastActivity() { sessionHandler.sendMessage(message); } - private void init() { - Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_INIT; - sessionHandler.sendMessage(message); - } - - private void readSessionState() { - Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_READ; - sessionHandler.sendMessage(message); - } - private static final class SessionHandler extends Handler { private final WeakReference sessionThreadReference; public SessionHandler(Looper looper, SessionThread sessionThread) { super(looper); - this.sessionThreadReference = new WeakReference(sessionThread); + this.sessionThreadReference = new WeakReference( + sessionThread); } - @Override public void handleMessage(Message message) { super.handleMessage(message); @@ -137,8 +129,6 @@ public void handleMessage(Message message) { return; } else if (message.arg1 == MESSAGE_ARG_UPDATE) { sessionThread.updateInternal(); - } else if (message.arg1 == MESSAGE_ARG_READ) { - sessionThread.readInternal(); } else if (message.arg1 == MESSAGE_ARG_INIT) { sessionThread.initInternal(); } else if (message.arg1 == MESSAGE_ARG_START) { @@ -146,23 +136,24 @@ public void handleMessage(Message message) { } else if (message.arg1 == MESSAGE_ARG_END) { sessionThread.endInternal(); } else if (message.arg1 == MESSAGE_ARG_EVENT) { - TrackingPackage.Builder builder = (TrackingPackage.Builder)message.obj; + PackageBuilder builder = (PackageBuilder) message.obj; sessionThread.eventInternal(builder); } else if (message.arg1 == MESSAGE_ARG_REVENUE) { - TrackingPackage.Builder builder = (TrackingPackage.Builder)message.obj; + PackageBuilder builder = (PackageBuilder) message.obj; sessionThread.revenueInternal(builder); } } } - private void updateInternal() { - Logger.info("update"); - } + // TODO: rename internal methods private void initInternal() { - if (!Util.checkPermissions(context)) return; - if (!checkAppToken(appToken)) return; - if (!checkContext(context)) return; + if (!Util.checkPermissions(context)) + return; + if (!checkAppToken(appToken)) + return; + if (!checkContext(context)) + return; String macAddress = Util.getMacAddress(context); @@ -173,24 +164,63 @@ private void initInternal() { attributionId = Util.getAttributionId(context); queueThread = new QueueThread(context); + readSessionStateInternal(); + } + + private void updateInternal() { + Logger.info("update"); } private void startInternal() { - startExecutor(); - - TrackingPackage sessionStart = new TrackingPackage.Builder() - .setPath("/startup") - .setSuccessMessage("Tracked session start.") - .setFailureMessage("Failed to track session start.") - .setUserAgent(userAgent) - .addTrackingParameter(APP_TOKEN, appToken) - .addTrackingParameter(MAC_SHORT, macShort) - .addTrackingParameter(MAC_SHA1, macSha1) - .addTrackingParameter(ANDROID_ID, androidId) - .addTrackingParameter(ATTRIBUTION_ID, attributionId) - .build(); + // startExecutor(); // TODO: enable + + long now = new Date().getTime(); + + // very first session on this device + if (sessionState.lastActivity == 0) { + sessionState.eventCount = 0; // no events yet + sessionState.sessionCount = 1; // the first session just started + sessionState.subsessionCount = -1; // we don't know how many subssessions this first session will have + sessionState.sessionLength = -1; // same for session length and time spent + sessionState.timeSpent = -1; // this information will be collected and attached to the next session + sessionState.createdAt = now; + + writeSessionStateInternal(); + enqueueSessionInternal(); + return; + } + + // new session + if (now - sessionState.lastActivity > SESSION_INTERVAL) { + sessionState.sessionCount++; + + enqueueSessionInternal(); + } + } + + private void enqueueSessionInternal() { + PackageBuilder builder = sessionState.getPackageBuilder(); + // TODO: extract? + builder.setUserAgent(userAgent); + builder.addTrackingParameter(APP_TOKEN, appToken); + builder.addTrackingParameter(MAC_SHORT, macShort); + builder.addTrackingParameter(MAC_SHA1, macSha1); + builder.addTrackingParameter(ANDROID_ID, androidId); + builder.addTrackingParameter(ATTRIBUTION_ID, attributionId); + + TrackingPackage sessionStart = builder.build(); queueThread.addPackage(sessionStart); + + long now = new Date().getTime(); + + sessionState.sessionCount++; // the next session just started + sessionState.subsessionCount = 1; // first subsession + sessionState.sessionLength = 0; // no session length yet + sessionState.timeSpent = 0; // no time spent yet + sessionState.createdAt = now; + sessionState.lastSubsessionStart = now; + sessionState.lastActivity = now; } private void endInternal() { @@ -198,43 +228,47 @@ private void endInternal() { // TODO: update last activity } - private void eventInternal(TrackingPackage.Builder builder) { - // TODO: clean up the builder stuff: reuse builder or even use its eventToken and parameters - if (!checkEventTokenNotNull(builder.eventToken)) return; - if (!checkEventTokenLength(builder.eventToken)) return; + private void eventInternal(PackageBuilder builder) { + // TODO: clean up the builder stuff: reuse builder or even use its + // eventToken and parameters + if (!checkEventTokenNotNull(builder.eventToken)) + return; + if (!checkEventTokenLength(builder.eventToken)) + return; - String paramString = Util.getBase64EncodedParameters(builder.parameters); + String paramString = Util + .getBase64EncodedParameters(builder.parameters); String successMessage = "Tracked event: '" + builder.eventToken + "'"; - String failureMessage = "Failed to track event: '" + builder.eventToken + "'"; - - TrackingPackage eventPackage = new TrackingPackage.Builder() - .setPath("/event") - .setSuccessMessage(successMessage) - .setFailureMessage(failureMessage) - .setUserAgent(userAgent) - .addTrackingParameter(APP_TOKEN, appToken) - .addTrackingParameter(MAC_SHORT, macShort) - .addTrackingParameter(ANDROID_ID, androidId) - .addTrackingParameter(EVENT_TOKEN, builder.eventToken) - .addTrackingParameter(PARAMETERS, paramString) - .build(); + String failureMessage = "Failed to track event: '" + builder.eventToken + + "'"; + + TrackingPackage eventPackage = new PackageBuilder() + .setPath("/event").setSuccessMessage(successMessage) + .setFailureMessage(failureMessage).setUserAgent(userAgent) + .addTrackingParameter(APP_TOKEN, appToken) + .addTrackingParameter(MAC_SHORT, macShort) + .addTrackingParameter(ANDROID_ID, androidId) + .addTrackingParameter(EVENT_TOKEN, builder.eventToken) + .addTrackingParameter(PARAMETERS, paramString).build(); queueThread.addPackage(eventPackage); } - private void revenueInternal(TrackingPackage.Builder builder) { - if (!checkEventTokenLength(builder.eventToken)) return; + private void revenueInternal(PackageBuilder builder) { + if (!checkEventTokenLength(builder.eventToken)) + return; Logger.info("revenueInternal"); // TODO: clean up and extract general event stuff? - int amountInMillis = Math.round(10 * builder.amountInCents); - float amountInCents = amountInMillis/10.0f; // now rounded to one decimal point + float amountInCents = amountInMillis / 10.0f; // now rounded to one decimal point String amount = Integer.toString(amountInMillis); - String paramString = Util.getBase64EncodedParameters(builder.parameters); + String paramString = Util + .getBase64EncodedParameters(builder.parameters); String successMessage = "Tracked revenue: " + amountInCents + " Cent"; - String failureMessage = "Failed to track revenue: " + amountInCents + " Cent"; + String failureMessage = "Failed to track revenue: " + amountInCents + + " Cent"; if (builder.eventToken != null) { String eventString = " (event token: '" + builder.eventToken + "')"; @@ -242,35 +276,36 @@ private void revenueInternal(TrackingPackage.Builder builder) { failureMessage += eventString; } - TrackingPackage revenuePackage = new TrackingPackage.Builder() - .setPath("/revenue") - .setSuccessMessage(successMessage) - .setFailureMessage(failureMessage) - .setUserAgent(userAgent) - .addTrackingParameter(APP_TOKEN, appToken) - .addTrackingParameter(MAC_SHORT, macShort) - .addTrackingParameter(ANDROID_ID, androidId) - .addTrackingParameter(AMOUNT, amount) - .addTrackingParameter(EVENT_TOKEN, builder.eventToken) - .addTrackingParameter(PARAMETERS, paramString) - .build(); - //getRequestThread().track(revenue); + TrackingPackage revenuePackage = new PackageBuilder() + .setPath("/revenue").setSuccessMessage(successMessage) + .setFailureMessage(failureMessage).setUserAgent(userAgent) + .addTrackingParameter(APP_TOKEN, appToken) + .addTrackingParameter(MAC_SHORT, macShort) + .addTrackingParameter(ANDROID_ID, androidId) + .addTrackingParameter(AMOUNT, amount) + .addTrackingParameter(EVENT_TOKEN, builder.eventToken) + .addTrackingParameter(PARAMETERS, paramString).build(); + // getRequestThread().track(revenue); queueThread.addPackage(revenuePackage); } - private void readInternal() { + private void readSessionStateInternal() { try { - FileInputStream inputStream = context.openFileInput(SESSION_FILENAME); - BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); - ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); + FileInputStream inputStream = context + .openFileInput(SESSION_FILENAME); + BufferedInputStream bufferedStream = new BufferedInputStream( + inputStream); + ObjectInputStream objectStream = new ObjectInputStream( + bufferedStream); try { - sessionState = (SessionState)objectStream.readObject(); + sessionState = (SessionState) objectStream.readObject(); Logger.error("state"); } finally { objectStream.close(); } } catch (FileNotFoundException e) { sessionState = new SessionState(); + writeSessionStateInternal(); } catch (ClassNotFoundException e) { Logger.error("class not found"); } catch (IOException e) { @@ -278,13 +313,19 @@ private void readInternal() { } } - private void writeInternal() { - try { Thread.sleep(100); } catch (Exception e) {} + private void writeSessionStateInternal() { + try { + Thread.sleep(100); + } catch (Exception e) { + } try { - FileOutputStream outputStream = context.openFileOutput(SESSION_FILENAME, Context.MODE_PRIVATE); - BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); - ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream); + FileOutputStream outputStream = context.openFileOutput( + SESSION_FILENAME, Context.MODE_PRIVATE); + BufferedOutputStream bufferedStream = new BufferedOutputStream( + outputStream); + ObjectOutputStream objectStream = new ObjectOutputStream( + bufferedStream); try { objectStream.writeObject(sessionState); } finally { @@ -335,7 +376,8 @@ private static boolean checkEventTokenNotNull(String eventToken) { } private static boolean checkEventTokenLength(String eventToken) { - if (eventToken == null) return true; + if (eventToken == null) + return true; if (eventToken.length() != 6) { Logger.error("Malformed Event Token"); diff --git a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java index a6fb3a64c..f0ece2a34 100644 --- a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java @@ -10,8 +10,8 @@ package com.adeven.adjustio; import java.io.Serializable; -import java.util.Map; +// TODO: remove this comment? clean up comments! /** * Holds information of one tracking package. * @@ -19,10 +19,8 @@ * @since 17.4.13 */ public class TrackingPackage implements Serializable { - /** - * - */ private static final long serialVersionUID = -5435782033488813179L; + final String path; final String successMessage; final String failureMessage; @@ -36,62 +34,4 @@ public TrackingPackage(String path, String successMessage, String failureMessage this.userAgent = userAgent; this.parameters = parameters; } - - /** - * A builder to enable chained building of a TrackingPackage. - */ - static class Builder { - public float amountInCents; - public String eventToken; - public Map parameters; - - private String path; - private String successMessage; - private String failureMessage; - private String userAgent; - private String parameterString; - - Builder setPath(String path) { - this.path = path; - return this; - } - - Builder setUserAgent(String userAgent) { - this.userAgent = userAgent; - Logger.verbose(path, "userAgent", userAgent); - return this; - } - - Builder setSuccessMessage(String successMessage) { - this.successMessage = successMessage; - Logger.verbose(path, "successMessage", successMessage); - return this; - } - - Builder setFailureMessage(String failureMessage) { - this.failureMessage = failureMessage; - Logger.verbose(path, "failureMessage", failureMessage); - return this; - } - - Builder addTrackingParameter(String key, String value) { - if (value == null || value == "" ) { - return this; - } - - if (parameterString == null || parameterString == "") { - parameterString = key + "=" + value; - } else { - parameterString += "&" + key + "=" + value; - } - - Logger.verbose(path, key, value); // TODO: remove these logs here? - return this; - } - - TrackingPackage build() { - TrackingPackage trackingPackage = new TrackingPackage(path, successMessage, failureMessage, userAgent, parameterString); - return trackingPackage; - } - } } From 040326c98921b2366120d933a9fbb12fbcff42a9 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Wed, 26 Jun 2013 11:31:44 +0200 Subject: [PATCH 09/37] Expand PackageBuilder and SessionState --- .../com/adeven/adjustio/PackageBuilder.java | 182 ++++++++----- .../src/com/adeven/adjustio/QueueThread.java | 2 +- .../com/adeven/adjustio/RequestThread.java | 17 +- .../src/com/adeven/adjustio/SessionState.java | 54 +++- .../com/adeven/adjustio/SessionThread.java | 244 +++++++++--------- .../com/adeven/adjustio/TrackingPackage.java | 65 ++--- 6 files changed, 338 insertions(+), 226 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java index ff9fc7a09..0ac624b39 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -1,85 +1,147 @@ package com.adeven.adjustio; +import java.util.Date; +import java.util.HashMap; import java.util.Map; -public class PackageBuilder { - // TODO: move somewhere else? - public float amountInCents; - public String eventToken; - public Map parameters; - private String path; - private String successMessage; - private String failureMessage; - private String userAgent; - private String parameterString; - PackageBuilder setPath(String path) { - this.path = path; - return this; - } - - PackageBuilder setUserAgent(String userAgent) { - this.userAgent = userAgent; - Logger.verbose(path, "userAgent", userAgent); - return this; +public class PackageBuilder { + // general + public String appToken; + public String macSha1; + public String macShort; // TODO: md5! + public String androidId; + public String attributionId; + + // sessions + public int sessionCount; + public int subsessionCount; + public long createdAt; + public long sessionLength; + public long timeSpent; + public long lastInterval; + + // events + public String eventToken; + public float amountInCents; + public Map callbackParameters; + + // meta + public String path; + public String userAgent; + public String kind; + public String suffix; + public String successMessage; + public String failureMessage; + + public TrackingPackage buildSessionPackage() { + Map pairs = new HashMap(); + + addString(pairs, "app_token", appToken); + addString(pairs, "mac_sha1", macSha1); + addString(pairs, "mac", macShort); + addString(pairs, "android_id", androidId); + addString(pairs, "fb_id", attributionId); + + addInt(pairs, "session_id", sessionCount); // TODO: rename? + addInt(pairs, "subsession_count", subsessionCount); + addDate(pairs, "created_at", createdAt); + addDuration(pairs, "session_length", sessionLength); + addDuration(pairs, "time_spent", timeSpent); + addDuration(pairs, "last_interval", lastInterval); + + String parameterString = buildParameterString(pairs); + + path = "/startup"; + successMessage = "Tracked session start."; + failureMessage = "Failed to track session start."; + + TrackingPackage sessionPackage = new TrackingPackage(); + sessionPackage.kind = "session start"; + sessionPackage.path = "/startup"; + sessionPackage.successMessage = "Tracked session start."; // TODO: can these logs be improved? + sessionPackage.failureMessage = "Failed to track session start."; + sessionPackage.parameters = parameterString; + sessionPackage.userAgent = userAgent; + + Logger.info("session package: " + sessionPackage); + + return sessionPackage; + // similar for event and revenue, then extract common parts } - PackageBuilder setSuccessMessage(String successMessage) { - this.successMessage = successMessage; - Logger.verbose(path, "successMessage", successMessage); - return this; + public TrackingPackage buildEventPackage() { + return null; } - PackageBuilder setFailureMessage(String failureMessage) { - this.failureMessage = failureMessage; - Logger.verbose(path, "failureMessage", failureMessage); - return this; + public TrackingPackage buildRevenuePackage() { + return null; } - PackageBuilder addTrackingParameter(String key, String value) { - if (value == null || value == "") { - return this; - } - - if (parameterString == null || parameterString == "") { - parameterString = key + "=" + value; - } else { - parameterString += "&" + key + "=" + value; + private String buildParameterString(Map pairs) { + String parameterString = null; + for (Map.Entry entry : pairs.entrySet()) { + String pair = entry.getKey() + "=" + entry.getValue(); + if (parameterString == null) { + parameterString = pair; + } else { + parameterString += "&" + pair; + } } - - Logger.verbose(path, key, value); // TODO: remove these logs here? - return this; + return parameterString; } - TrackingPackage build() { - TrackingPackage trackingPackage = new TrackingPackage(path, - successMessage, failureMessage, userAgent, parameterString); - return trackingPackage; + private void getParameterString() { + String callbackParametersString = Util.getBase64EncodedParameters(callbackParameters); + int amountInMillis = Math.round(10 * amountInCents); + float amountInCents = amountInMillis / 10.0f; // now rounded to one decimal point + String amountString = Integer.toString(amountInMillis); + + Map pairs = new HashMap(); + + addString(pairs, "app_token", appToken); + addString(pairs, "mac_sha1", macSha1); + addString(pairs, "mac", macShort); + addString(pairs, "android_id", androidId); + addString(pairs, "fb_id", attributionId); + + addInt(pairs, "session_id", sessionCount); // TODO: rename? + addInt(pairs, "subsession_count", subsessionCount); + addDate(pairs, "session_length", sessionLength); + addDate(pairs, "time_spent", timeSpent); + addDate(pairs, "last_interval", lastInterval); + + addString(pairs, "event_id", eventToken); + addString(pairs, "params", callbackParametersString); + addString(pairs, "amount", amountString); } - public void setSessionCount(int sessionCount) { - // TODO Auto-generated method stub - - } - - public void setSubsessionCount(int subsessionCount) { - // TODO Auto-generated method stub - + private void addString(Map pairs, String key, String value) { + if (value == null || value == "") { + return; + } + pairs.put(key, value); } - public void setSessionLength(double sessionLength) { - // TODO Auto-generated method stub - + private void addInt(Map pairs, String key, long value) { + if (value == -1) { + return; + } + String valueString = Long.toString(value); + pairs.put(key, valueString); } - public void setCreatedAt(long createdAt) { - // TODO Auto-generated method stub - + private void addDate(Map pairs, String key, long value) { + if (value == -1) { + return; + } + Date date = new Date(value); + pairs.put(key, date.toString()); // TODO: format } - public void setTimeSpent(double timeSpent) { - // TODO Auto-generated method stub - + private void addDuration(Map pairs, String key, long durationInMilliSeconds) { + long durationInSeconds = durationInMilliSeconds / 1000; + addInt(pairs, key, durationInSeconds); } } diff --git a/AdjustIo/src/com/adeven/adjustio/QueueThread.java b/AdjustIo/src/com/adeven/adjustio/QueueThread.java index 5a8cd2686..f613a0a2c 100644 --- a/AdjustIo/src/com/adeven/adjustio/QueueThread.java +++ b/AdjustIo/src/com/adeven/adjustio/QueueThread.java @@ -157,7 +157,7 @@ private void readInternal() { ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); try { packages = (List)objectStream.readObject(); - Logger.error("packages " + packages.size()); + Logger.info("packages " + packages.size()); } finally { objectStream.close(); } diff --git a/AdjustIo/src/com/adeven/adjustio/RequestThread.java b/AdjustIo/src/com/adeven/adjustio/RequestThread.java index 2f3f6c61f..fe4ee932a 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestThread.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestThread.java @@ -19,9 +19,6 @@ import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.entity.StringEntity; -import org.apache.http.protocol.HTTP; import android.os.Handler; import android.os.HandlerThread; @@ -77,15 +74,14 @@ public void handleMessage(Message message) { } private void trackInternal(TrackingPackage trackingPackage) { + HttpClient httpClient = Util.getHttpClient(trackingPackage.userAgent); HttpPost request = Util.getPostRequest(trackingPackage.path); // TODO: test all paths! - // TODO: reject if unsure (because rejected packages will always be retried) + // TODO: track next if unsure (because rejected packages will always be retried) try { - StringEntity entity = new StringEntity(trackingPackage.parameters, HTTP.UTF_8); - entity.setContentType(URLEncodedUtils.CONTENT_TYPE); - request.setEntity(entity); + trackingPackage.injectEntity(request); HttpResponse response = httpClient.execute(request); requestFinished(response, trackingPackage); } catch (UnsupportedEncodingException e) { @@ -93,10 +89,13 @@ private void trackInternal(TrackingPackage trackingPackage) { queueThread.trackNextPackage(); } catch (ClientProtocolException e) { Logger.error("client protocol error"); - queueThread.rejectFirstPackage(); + queueThread.rejectFirstPackage(); // TODO: track next, right? } catch (IOException e) { - Logger.error("connection failed (" + e.getLocalizedMessage() + ")"); + Logger.error("connection failed (" + e.getLocalizedMessage() + ")"); // TODO: "will try again later" queueThread.rejectFirstPackage(); + } catch (IllegalArgumentException e) { + Logger.error("Failed to track package (" + e.getLocalizedMessage() + ")"); + queueThread.trackNextPackage(); } } diff --git a/AdjustIo/src/com/adeven/adjustio/SessionState.java b/AdjustIo/src/com/adeven/adjustio/SessionState.java index b0b1fbb97..c883905ac 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionState.java +++ b/AdjustIo/src/com/adeven/adjustio/SessionState.java @@ -1,6 +1,7 @@ package com.adeven.adjustio; import java.io.Serializable; +import java.util.Date; public class SessionState implements Serializable { private static final long serialVersionUID = 9039439291143138148L; @@ -13,25 +14,56 @@ public class SessionState implements Serializable { // session attributes public int subsessionCount; - public double sessionLength; - public double timeSpent; + public long sessionLength; // all durations in milliseconds + public long timeSpent; public long createdAt; // all times in milliseconds since 1970 - public long lastSubsessionStart; public long lastActivity; + public SessionState() { + eventCount = 0; // no events yet + sessionCount = 0; // the first session just started + subsessionCount = -1; // we don't know how many subssessions 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 + createdAt = -1; + lastActivity = -1; + } + + public void startNextSession(long now) { + sessionCount++; // the next session just started + subsessionCount = 1; // first subsession + sessionLength = 0; // no session length yet + timeSpent = 0; // no time spent yet + createdAt = now; + lastActivity = now; + } public PackageBuilder getPackageBuilder() { PackageBuilder builder = new PackageBuilder(); - builder.setPath("/startup"); - builder.setSuccessMessage("Tracked session start."); - builder.setFailureMessage("Failed to track session start."); - builder.setSessionCount(sessionCount); - builder.setSubsessionCount(subsessionCount); - builder.setSessionLength(sessionLength); - builder.setTimeSpent(timeSpent); - builder.setCreatedAt(createdAt); + builder.sessionCount = sessionCount; + builder.subsessionCount = subsessionCount; + builder.sessionLength = sessionLength; + builder.timeSpent = timeSpent; + builder.createdAt = createdAt; return builder; } + + public String toString() { + return "ec:" + eventCount + + " sc:" + sessionCount + + " ssc:" + subsessionCount + + " sl:" + sessionLength + + " ts:" + timeSpent + + " ca:" + stamp(createdAt) + + " la:" + stamp(lastActivity); + } + + private static String stamp(long date) { + Date d = new Date(date); + return "" + d.getHours() + + ":" + d.getMinutes() + + ":" + d.getSeconds(); + } } diff --git a/AdjustIo/src/com/adeven/adjustio/SessionThread.java b/AdjustIo/src/com/adeven/adjustio/SessionThread.java index 8a6399634..6711c3e5d 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionThread.java +++ b/AdjustIo/src/com/adeven/adjustio/SessionThread.java @@ -36,16 +36,6 @@ public class SessionThread extends HandlerThread { private QueueThread queueThread; private static ScheduledExecutorService executor; - // TODO: move to TrackingPackage? - private static final String APP_TOKEN = "app_token"; - private static final String MAC_SHA1 = "mac_sha1"; - private static final String MAC_SHORT = "mac"; - private static final String ANDROID_ID = "android_id"; - private static final String ATTRIBUTION_ID = "fb_id"; - private static final String EVENT_TOKEN = "event_id"; - private static final String PARAMETERS = "params"; - private static final String AMOUNT = "amount"; - private static final String SESSION_FILENAME = "sessionstate"; private static final int MESSAGE_ARG_UPDATE = 72610; private static final int MESSAGE_ARG_INIT = 72630; @@ -54,7 +44,8 @@ public class SessionThread extends HandlerThread { private static final int MESSAGE_ARG_EVENT = 72660; private static final int MESSAGE_ARG_REVENUE = 72670; - private static final long SESSION_INTERVAL = 1000 * 1; // 1 second, TODO: 30 minutes + private static final long SESSION_INTERVAL = 1000 * 1; // 1 second, TODO: 30 + // minutes public SessionThread(String appToken, Context context) { super(Logger.LOGTAG, MIN_PRIORITY); @@ -85,7 +76,7 @@ public void trackSubsessionEnd() { public void trackEvent(String eventToken, Map parameters) { PackageBuilder builder = new PackageBuilder(); builder.eventToken = eventToken; - builder.parameters = parameters; + builder.callbackParameters = parameters; Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_EVENT; @@ -93,12 +84,11 @@ public void trackEvent(String eventToken, Map parameters) { sessionHandler.sendMessage(message); } - public void trackRevenue(float amountInCents, String eventToken, - Map parameters) { + public void trackRevenue(float amountInCents, String eventToken, Map parameters) { PackageBuilder builder = new PackageBuilder(); builder.amountInCents = amountInCents; builder.eventToken = eventToken; - builder.parameters = parameters; + builder.callbackParameters = parameters; Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_REVENUE; @@ -117,8 +107,7 @@ private static final class SessionHandler extends Handler { public SessionHandler(Looper looper, SessionThread sessionThread) { super(looper); - this.sessionThreadReference = new WeakReference( - sessionThread); + this.sessionThreadReference = new WeakReference(sessionThread); } public void handleMessage(Message message) { @@ -127,20 +116,27 @@ public void handleMessage(Message message) { SessionThread sessionThread = sessionThreadReference.get(); if (sessionThread == null) { return; - } else if (message.arg1 == MESSAGE_ARG_UPDATE) { + } + + switch (message.arg1) { + case MESSAGE_ARG_UPDATE: sessionThread.updateInternal(); - } else if (message.arg1 == MESSAGE_ARG_INIT) { + break; + case MESSAGE_ARG_INIT: sessionThread.initInternal(); - } else if (message.arg1 == MESSAGE_ARG_START) { + break; + case MESSAGE_ARG_START: sessionThread.startInternal(); - } else if (message.arg1 == MESSAGE_ARG_END) { + break; + case MESSAGE_ARG_END: sessionThread.endInternal(); - } else if (message.arg1 == MESSAGE_ARG_EVENT) { - PackageBuilder builder = (PackageBuilder) message.obj; - sessionThread.eventInternal(builder); - } else if (message.arg1 == MESSAGE_ARG_REVENUE) { - PackageBuilder builder = (PackageBuilder) message.obj; - sessionThread.revenueInternal(builder); + break; + case MESSAGE_ARG_EVENT: + sessionThread.eventInternal((PackageBuilder) message.obj); + break; + case MESSAGE_ARG_REVENUE: + sessionThread.revenueInternal((PackageBuilder) message.obj); + break; } } } @@ -177,55 +173,83 @@ private void startInternal() { long now = new Date().getTime(); // very first session on this device - if (sessionState.lastActivity == 0) { - sessionState.eventCount = 0; // no events yet - sessionState.sessionCount = 1; // the first session just started - sessionState.subsessionCount = -1; // we don't know how many subssessions this first session will have - sessionState.sessionLength = -1; // same for session length and time spent - sessionState.timeSpent = -1; // this information will be collected and attached to the next session - sessionState.createdAt = now; + if (sessionState == null) { + Logger.info("first session"); + sessionState = new SessionState(); + sessionState.sessionCount = 1; // this is the first session + sessionState.createdAt = now; // starting now (that's all we know) + enqueueSessionInternal(-1); writeSessionStateInternal(); - enqueueSessionInternal(); return; } - // new session - if (now - sessionState.lastActivity > SESSION_INTERVAL) { - sessionState.sessionCount++; + long lastInterval = now - sessionState.lastActivity; + if (lastInterval < 0) { + Logger.info("time travel"); + // TODO: extract this check? + // should not happen, skip last interval and continue from here + sessionState.lastActivity = now; + return; + } - enqueueSessionInternal(); + // new session + if (lastInterval > SESSION_INTERVAL) { + Logger.info("new session"); + enqueueSessionInternal(lastInterval); + sessionState.startNextSession(now); + writeSessionStateInternal(); + return; } + + // new subsession start + sessionState.subsessionCount++; + sessionState.sessionLength += lastInterval; + sessionState.lastActivity = now; + Logger.info("new subsession " + sessionState.subsessionCount); } - private void enqueueSessionInternal() { - PackageBuilder builder = sessionState.getPackageBuilder(); + private void endInternal() { + stopExecutor(); - // TODO: extract? - builder.setUserAgent(userAgent); - builder.addTrackingParameter(APP_TOKEN, appToken); - builder.addTrackingParameter(MAC_SHORT, macShort); - builder.addTrackingParameter(MAC_SHA1, macSha1); - builder.addTrackingParameter(ANDROID_ID, androidId); - builder.addTrackingParameter(ATTRIBUTION_ID, attributionId); - - TrackingPackage sessionStart = builder.build(); - queueThread.addPackage(sessionStart); + if (sessionState == null) { + // ignore session ends before first session start + return; + } long now = new Date().getTime(); + long lastInterval = now - sessionState.lastActivity; + if (lastInterval < 0) { + // should not happen, skip last interval and continue from here + sessionState.lastActivity = now; + return; + } - sessionState.sessionCount++; // the next session just started - sessionState.subsessionCount = 1; // first subsession - sessionState.sessionLength = 0; // no session length yet - sessionState.timeSpent = 0; // no time spent yet - sessionState.createdAt = now; - sessionState.lastSubsessionStart = now; + if (lastInterval > SESSION_INTERVAL) { + // ignore late ends (should not happen because of activity timer) + return; + } + + // subsession end + sessionState.sessionLength += lastInterval; + sessionState.timeSpent += lastInterval; sessionState.lastActivity = now; } - private void endInternal() { - stopExecutor(); - // TODO: update last activity + private void enqueueSessionInternal(long lastInterval) { + PackageBuilder builder = sessionState.getPackageBuilder(); + builder.lastInterval = lastInterval; + + // TODO: extract? + builder.userAgent = userAgent; + builder.appToken = appToken; + builder.macShort = macShort; + builder.macSha1 = macSha1; + builder.androidId = androidId; + builder.attributionId = attributionId; + + TrackingPackage sessionStart = builder.buildSessionPackage(); + queueThread.addPackage(sessionStart); } private void eventInternal(PackageBuilder builder) { @@ -236,22 +260,20 @@ private void eventInternal(PackageBuilder builder) { if (!checkEventTokenLength(builder.eventToken)) return; - String paramString = Util - .getBase64EncodedParameters(builder.parameters); String successMessage = "Tracked event: '" + builder.eventToken + "'"; - String failureMessage = "Failed to track event: '" + builder.eventToken - + "'"; - - TrackingPackage eventPackage = new PackageBuilder() - .setPath("/event").setSuccessMessage(successMessage) - .setFailureMessage(failureMessage).setUserAgent(userAgent) - .addTrackingParameter(APP_TOKEN, appToken) - .addTrackingParameter(MAC_SHORT, macShort) - .addTrackingParameter(ANDROID_ID, androidId) - .addTrackingParameter(EVENT_TOKEN, builder.eventToken) - .addTrackingParameter(PARAMETERS, paramString).build(); - - queueThread.addPackage(eventPackage); + String failureMessage = "Failed to track event: '" + builder.eventToken + "'"; + + builder.path = "/event"; + builder.successMessage = successMessage; + builder.failureMessage = failureMessage; + builder.userAgent = userAgent; + builder.appToken = appToken; + builder.macShort = macShort; + builder.androidId = androidId; + builder.eventToken = builder.eventToken; +// TrackingPackage eventPackage = builder.build(); +// +// queueThread.addPackage(eventPackage); } private void revenueInternal(PackageBuilder builder) { @@ -261,55 +283,47 @@ private void revenueInternal(PackageBuilder builder) { Logger.info("revenueInternal"); // TODO: clean up and extract general event stuff? - int amountInMillis = Math.round(10 * builder.amountInCents); - float amountInCents = amountInMillis / 10.0f; // now rounded to one decimal point - String amount = Integer.toString(amountInMillis); - String paramString = Util - .getBase64EncodedParameters(builder.parameters); - String successMessage = "Tracked revenue: " + amountInCents + " Cent"; - String failureMessage = "Failed to track revenue: " + amountInCents - + " Cent"; - - if (builder.eventToken != null) { - String eventString = " (event token: '" + builder.eventToken + "')"; - successMessage += eventString; - failureMessage += eventString; - } - - TrackingPackage revenuePackage = new PackageBuilder() - .setPath("/revenue").setSuccessMessage(successMessage) - .setFailureMessage(failureMessage).setUserAgent(userAgent) - .addTrackingParameter(APP_TOKEN, appToken) - .addTrackingParameter(MAC_SHORT, macShort) - .addTrackingParameter(ANDROID_ID, androidId) - .addTrackingParameter(AMOUNT, amount) - .addTrackingParameter(EVENT_TOKEN, builder.eventToken) - .addTrackingParameter(PARAMETERS, paramString).build(); - // getRequestThread().track(revenue); - queueThread.addPackage(revenuePackage); +// String successMessage = "Tracked revenue: " + amountInCents + " Cent"; +// String failureMessage = "Failed to track revenue: " + amountInCents + " Cent"; +// +// if (builder.eventToken != null) { +// String eventString = " (event token: '" + builder.eventToken + "')"; +// successMessage += eventString; +// failureMessage += eventString; +// } +// +// builder.path = "/revenue"; +// builder.successMessage = successMessage; +// builder.failureMessage = failureMessage; +// builder.userAgent = userAgent; +// builder.appToken = appToken; +// builder.macShort = macShort; +// builder.androidId = androidId; +// builder.eventToken = builder.eventToken; +// TrackingPackage revenuePackage = builder.build(); +// // getRequestThread().track(revenue); +// queueThread.addPackage(revenuePackage); } private void readSessionStateInternal() { try { - FileInputStream inputStream = context - .openFileInput(SESSION_FILENAME); - BufferedInputStream bufferedStream = new BufferedInputStream( - inputStream); - ObjectInputStream objectStream = new ObjectInputStream( - bufferedStream); + FileInputStream inputStream = context.openFileInput(SESSION_FILENAME); + BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); + ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); try { sessionState = (SessionState) objectStream.readObject(); - Logger.error("state"); + Logger.info("readSessionState " + sessionState); } finally { objectStream.close(); } } catch (FileNotFoundException e) { - sessionState = new SessionState(); - writeSessionStateInternal(); + // first ever session start } catch (ClassNotFoundException e) { Logger.error("class not found"); } catch (IOException e) { Logger.error("failed to read object"); + } catch (IllegalArgumentException e) { + Logger.error("illegal argument"); } } @@ -320,13 +334,11 @@ private void writeSessionStateInternal() { } try { - FileOutputStream outputStream = context.openFileOutput( - SESSION_FILENAME, Context.MODE_PRIVATE); - BufferedOutputStream bufferedStream = new BufferedOutputStream( - outputStream); - ObjectOutputStream objectStream = new ObjectOutputStream( - bufferedStream); + FileOutputStream outputStream = context.openFileOutput(SESSION_FILENAME, Context.MODE_PRIVATE); + BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); + ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream); try { + Logger.info("writeSessionState " + sessionState); objectStream.writeObject(sessionState); } finally { objectStream.close(); diff --git a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java index f0ece2a34..8a5f04649 100644 --- a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java @@ -1,37 +1,44 @@ -// -// TrackingPackage.java -// AdjustIo -// -// Created by Benjamin Weiss on 17.4.13 -// Copyright (c) 2012 adeven. All rights reserved. -// See the file MIT-LICENSE for copying permission. -// +// TODO: add header comments package com.adeven.adjustio; import java.io.Serializable; +import java.io.UnsupportedEncodingException; + +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.entity.StringEntity; +import org.apache.http.protocol.HTTP; -// TODO: remove this comment? clean up comments! -/** - * Holds information of one tracking package. - * - * @author keyboardsurfer - * @since 17.4.13 - */ public class TrackingPackage implements Serializable { - private static final long serialVersionUID = -5435782033488813179L; - - final String path; - final String successMessage; - final String failureMessage; - final String userAgent; - final String parameters; - - public TrackingPackage(String path, String successMessage, String failureMessage, String userAgent, String parameters) { - this.path = path; - this.successMessage = successMessage; - this.failureMessage = failureMessage; - this.userAgent = userAgent; - this.parameters = parameters; + private static final long serialVersionUID = -35935556512024097L; + + // data + public String path; + public String parameters; + public String userAgent; + + // for logging + public String kind; + public String successMessage; + public String failureMessage; + + public String toString() { + return "k:" + kind + + " pt:" + path + + " pr:" + parameters + + " ua:" + userAgent + + " sm:" + successMessage + + " fm:" + failureMessage; + } + + public void injectEntity(HttpPost request) throws UnsupportedEncodingException { + if (parameters == null) { + return; + } + + StringEntity entity = new StringEntity(parameters, HTTP.UTF_8); + entity.setContentType(URLEncodedUtils.CONTENT_TYPE); + request.setEntity(entity); } } From 7242655c5bea319ee84cb8c4a27149ab4ce736f2 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Wed, 26 Jun 2013 12:19:08 +0200 Subject: [PATCH 10/37] Use HashMap for parameters --- .../com/adeven/adjustio/PackageBuilder.java | 87 +++++-------------- .../src/com/adeven/adjustio/QueueThread.java | 15 +++- .../com/adeven/adjustio/SessionThread.java | 2 +- .../com/adeven/adjustio/TrackingPackage.java | 25 ++++-- AdjustIo/src/com/adeven/adjustio/Util.java | 18 ---- 5 files changed, 55 insertions(+), 92 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java index 0ac624b39..90b9e5929 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -4,8 +4,6 @@ import java.util.HashMap; import java.util.Map; - - public class PackageBuilder { // general public String appToken; @@ -25,7 +23,7 @@ public class PackageBuilder { // events public String eventToken; public float amountInCents; - public Map callbackParameters; + public Map callbackParameters; // TODO: remove // meta public String path; @@ -36,22 +34,20 @@ public class PackageBuilder { public String failureMessage; public TrackingPackage buildSessionPackage() { - Map pairs = new HashMap(); + Map parameters = new HashMap(); - addString(pairs, "app_token", appToken); - addString(pairs, "mac_sha1", macSha1); - addString(pairs, "mac", macShort); - addString(pairs, "android_id", androidId); - addString(pairs, "fb_id", attributionId); + addString(parameters, "app_token", appToken); + addString(parameters, "mac_sha1", macSha1); + addString(parameters, "mac", macShort); + addString(parameters, "android_id", androidId); + addString(parameters, "fb_id", attributionId); - addInt(pairs, "session_id", sessionCount); // TODO: rename? - addInt(pairs, "subsession_count", subsessionCount); - addDate(pairs, "created_at", createdAt); - addDuration(pairs, "session_length", sessionLength); - addDuration(pairs, "time_spent", timeSpent); - addDuration(pairs, "last_interval", lastInterval); - - String parameterString = buildParameterString(pairs); + addInt(parameters, "session_id", sessionCount); // TODO: rename? + addInt(parameters, "subsession_count", subsessionCount); + addDate(parameters, "created_at", createdAt); + addDuration(parameters, "session_length", sessionLength); + addDuration(parameters, "time_spent", timeSpent); + addDuration(parameters, "last_interval", lastInterval); path = "/startup"; successMessage = "Tracked session start."; @@ -62,7 +58,7 @@ public TrackingPackage buildSessionPackage() { sessionPackage.path = "/startup"; sessionPackage.successMessage = "Tracked session start."; // TODO: can these logs be improved? sessionPackage.failureMessage = "Failed to track session start."; - sessionPackage.parameters = parameterString; + sessionPackage.parameters = parameters; sessionPackage.userAgent = userAgent; Logger.info("session package: " + sessionPackage); @@ -79,69 +75,32 @@ public TrackingPackage buildRevenuePackage() { return null; } - private String buildParameterString(Map pairs) { - String parameterString = null; - for (Map.Entry entry : pairs.entrySet()) { - String pair = entry.getKey() + "=" + entry.getValue(); - if (parameterString == null) { - parameterString = pair; - } else { - parameterString += "&" + pair; - } - } - return parameterString; - } - - private void getParameterString() { - String callbackParametersString = Util.getBase64EncodedParameters(callbackParameters); - int amountInMillis = Math.round(10 * amountInCents); - float amountInCents = amountInMillis / 10.0f; // now rounded to one decimal point - String amountString = Integer.toString(amountInMillis); - - Map pairs = new HashMap(); - - addString(pairs, "app_token", appToken); - addString(pairs, "mac_sha1", macSha1); - addString(pairs, "mac", macShort); - addString(pairs, "android_id", androidId); - addString(pairs, "fb_id", attributionId); - - addInt(pairs, "session_id", sessionCount); // TODO: rename? - addInt(pairs, "subsession_count", subsessionCount); - addDate(pairs, "session_length", sessionLength); - addDate(pairs, "time_spent", timeSpent); - addDate(pairs, "last_interval", lastInterval); - - addString(pairs, "event_id", eventToken); - addString(pairs, "params", callbackParametersString); - addString(pairs, "amount", amountString); - } - - private void addString(Map pairs, String key, String value) { + private void addString(Map parameters, String key, String value) { if (value == null || value == "") { return; } - pairs.put(key, value); + parameters.put(key, value); } - private void addInt(Map pairs, String key, long value) { + private void addInt(Map parameters, String key, long value) { if (value == -1) { return; } String valueString = Long.toString(value); - pairs.put(key, valueString); + addString(parameters, key, valueString); } - private void addDate(Map pairs, String key, long value) { + private void addDate(Map parameters, String key, long value) { if (value == -1) { return; } Date date = new Date(value); - pairs.put(key, date.toString()); // TODO: format + String dateString = date.toString(); // TODO: format + addString(parameters, key, dateString); } - private void addDuration(Map pairs, String key, long durationInMilliSeconds) { + private void addDuration(Map parameters, String key, long durationInMilliSeconds) { long durationInSeconds = durationInMilliSeconds / 1000; - addInt(pairs, key, durationInSeconds); + addInt(parameters, key, durationInSeconds); } } diff --git a/AdjustIo/src/com/adeven/adjustio/QueueThread.java b/AdjustIo/src/com/adeven/adjustio/QueueThread.java index f613a0a2c..70e220e62 100644 --- a/AdjustIo/src/com/adeven/adjustio/QueueThread.java +++ b/AdjustIo/src/com/adeven/adjustio/QueueThread.java @@ -7,6 +7,7 @@ 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.lang.ref.WeakReference; @@ -29,7 +30,7 @@ public class QueueThread extends HandlerThread { private List packages; private static final String QUEUE_FILENAME = "testqueue"; - private static final int MESSAGE_ARG_ADD = 72500; // TODO: change numbers? + private static final int MESSAGE_ARG_ADD = 72500; // TODO: change numbers? private static final int MESSAGE_ARG_REMOVE = 72510; private static final int MESSAGE_ARG_READ = 72520; private static final int MESSAGE_ARG_TRACK = 72530; @@ -156,7 +157,7 @@ private void readInternal() { BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); try { - packages = (List)objectStream.readObject(); + packages = (List) objectStream.readObject(); Logger.info("packages " + packages.size()); } finally { objectStream.close(); @@ -171,7 +172,10 @@ private void readInternal() { } private void writeInternal() { - try { Thread.sleep(100); } catch (Exception e) {} + try { + Thread.sleep(100); + } catch (Exception e) { + } try { FileOutputStream outputStream = context.openFileOutput(QUEUE_FILENAME, Context.MODE_PRIVATE); @@ -179,11 +183,14 @@ private void writeInternal() { ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream); try { objectStream.writeObject(packages); + } catch (NotSerializableException e) { + Logger.error("failed to serialize package"); } finally { objectStream.close(); } } catch (IOException e) { - Logger.error("failed to write package"); // TODO: improve log + Logger.error("failed to write packages (" + e.getLocalizedMessage() + ")"); // TODO: improve log + e.printStackTrace(); } } } diff --git a/AdjustIo/src/com/adeven/adjustio/SessionThread.java b/AdjustIo/src/com/adeven/adjustio/SessionThread.java index 6711c3e5d..d341382d2 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionThread.java +++ b/AdjustIo/src/com/adeven/adjustio/SessionThread.java @@ -344,7 +344,7 @@ private void writeSessionStateInternal() { objectStream.close(); } } catch (IOException e) { - Logger.error("failed to write package"); // TODO: improve log + Logger.error("failed to write package X (" + e.getLocalizedMessage() + ")"); // TODO: improve log } } diff --git a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java index 8a5f04649..acbffa9b2 100644 --- a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java @@ -2,21 +2,29 @@ package com.adeven.adjustio; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.Serializable; import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.entity.StringEntity; -import org.apache.http.protocol.HTTP; +import org.apache.http.message.BasicNameValuePair; public class TrackingPackage implements Serializable { private static final long serialVersionUID = -35935556512024097L; // data public String path; - public String parameters; public String userAgent; + Map parameters; // for logging public String kind; @@ -26,7 +34,7 @@ public class TrackingPackage implements Serializable { public String toString() { return "k:" + kind + " pt:" + path + - " pr:" + parameters + + " pr:" + parameters.toString() + // TODO: format? " ua:" + userAgent + " sm:" + successMessage + " fm:" + failureMessage; @@ -37,8 +45,15 @@ public void injectEntity(HttpPost request) throws UnsupportedEncodingException { return; } - StringEntity entity = new StringEntity(parameters, HTTP.UTF_8); + List pairs = new ArrayList(); + for (Map.Entry entity : parameters.entrySet()) { + NameValuePair pair = new BasicNameValuePair(entity.getKey(), entity.getValue()); + pairs.add(pair); + } + + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(pairs); entity.setContentType(URLEncodedUtils.CONTENT_TYPE); request.setEntity(entity); } + } diff --git a/AdjustIo/src/com/adeven/adjustio/Util.java b/AdjustIo/src/com/adeven/adjustio/Util.java index 21e659df2..6c3beceab 100644 --- a/AdjustIo/src/com/adeven/adjustio/Util.java +++ b/AdjustIo/src/com/adeven/adjustio/Util.java @@ -94,24 +94,6 @@ public static String getBase64EncodedParameters(Map parameters) return encoded; } - public static UrlEncodedFormEntity getEntityEncodedParameters(List parameters) throws UnsupportedEncodingException { - UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters); - - try { - InputStream stream = entity.getContent(); - InputStreamReader reader = new InputStreamReader(stream); - BufferedReader buffer = new BufferedReader(reader); - String line; - while ((line = buffer.readLine()) != null) { - Logger.error("entity line: " + line); - } - } catch (IOException e) { - Logger.error("failed to read entity"); - } - - return entity; - } - public static HttpClient getHttpClient(String userAgent) { HttpClient httpClient = new DefaultHttpClient(); HttpParams params = httpClient.getParams(); From 2bff3ddd53981012a6b56d347194c39b3801cdb3 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Fri, 28 Jun 2013 11:33:58 +0200 Subject: [PATCH 11/37] Restructure, refactor, clean up --- AdjustIo/src/com/adeven/adjustio/Logger.java | 1 + .../com/adeven/adjustio/PackageBuilder.java | 125 +++++++-- .../src/com/adeven/adjustio/QueueThread.java | 184 +++++++------ .../com/adeven/adjustio/RequestThread.java | 61 ++--- .../src/com/adeven/adjustio/SessionState.java | 17 +- .../com/adeven/adjustio/SessionThread.java | 255 ++++++++---------- .../com/adeven/adjustio/TrackingPackage.java | 31 ++- AdjustIo/src/com/adeven/adjustio/Util.java | 26 +- 8 files changed, 396 insertions(+), 304 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/Logger.java b/AdjustIo/src/com/adeven/adjustio/Logger.java index c38c14e4d..968fe5874 100644 --- a/AdjustIo/src/com/adeven/adjustio/Logger.java +++ b/AdjustIo/src/com/adeven/adjustio/Logger.java @@ -12,6 +12,7 @@ import android.util.Log; // TODO: check if java.util.logging.Logger is an alternative +// TODO: go through all log calls and set a proper log level /** * A Wrapper that allows easy toggles of Logging. diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java index 90b9e5929..2f5ff6814 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -4,6 +4,10 @@ import java.util.HashMap; import java.util.Map; +import org.json.JSONObject; + +import android.util.Base64; + public class PackageBuilder { // general public String appToken; @@ -21,58 +25,131 @@ public class PackageBuilder { public long lastInterval; // events + public int eventCount; public String eventToken; public float amountInCents; - public Map callbackParameters; // TODO: remove + public Map callbackParameters; // TODO: test! - // meta + // meta TODO: remove public String path; public String userAgent; public String kind; public String suffix; - public String successMessage; - public String failureMessage; public TrackingPackage buildSessionPackage() { Map parameters = new HashMap(); + // general + addDate(parameters, "created_at", createdAt); addString(parameters, "app_token", appToken); addString(parameters, "mac_sha1", macSha1); addString(parameters, "mac", macShort); addString(parameters, "android_id", androidId); addString(parameters, "fb_id", attributionId); + // session specific addInt(parameters, "session_id", sessionCount); // TODO: rename? addInt(parameters, "subsession_count", subsessionCount); - addDate(parameters, "created_at", createdAt); addDuration(parameters, "session_length", sessionLength); addDuration(parameters, "time_spent", timeSpent); addDuration(parameters, "last_interval", lastInterval); - path = "/startup"; - successMessage = "Tracked session start."; - failureMessage = "Failed to track session start."; - TrackingPackage sessionPackage = new TrackingPackage(); - sessionPackage.kind = "session start"; sessionPackage.path = "/startup"; - sessionPackage.successMessage = "Tracked session start."; // TODO: can these logs be improved? - sessionPackage.failureMessage = "Failed to track session start."; + sessionPackage.kind = "session start"; + sessionPackage.suffix = "."; sessionPackage.parameters = parameters; sessionPackage.userAgent = userAgent; - Logger.info("session package: " + sessionPackage); - return sessionPackage; - // similar for event and revenue, then extract common parts } public TrackingPackage buildEventPackage() { - return null; + Map parameters = new HashMap(); + + // general + addDate(parameters, "created_at", createdAt); + addString(parameters, "app_token", appToken); + addString(parameters, "mac_sha1", macSha1); + addString(parameters, "mac", macShort); + addString(parameters, "android_id", androidId); + addString(parameters, "fb_id", attributionId); + + // event specific + addInt(parameters, "event_count", eventCount); + addString(parameters, "event_id", eventToken); // TODO: rename + addMap(parameters, "params", callbackParameters); + + // session specific (current values at time of event) + addInt(parameters, "session_count", sessionCount); + addInt(parameters, "subsession_count", subsessionCount); + addDuration(parameters, "session_length", sessionLength); + addDuration(parameters, "time_spent", timeSpent); + addDuration(parameters, "last_interval", lastInterval); + + TrackingPackage eventPackage = new TrackingPackage(); + eventPackage.path = "/event"; + eventPackage.kind = "event"; + eventPackage.suffix = " '" + eventToken + "'."; + eventPackage.parameters = parameters; + eventPackage.userAgent = userAgent; + + return eventPackage; } public TrackingPackage buildRevenuePackage() { - return null; + + Map parameters = new HashMap(); + + // general + addDate(parameters, "created_at", createdAt); + addString(parameters, "app_token", appToken); + addString(parameters, "mac_sha1", macSha1); + addString(parameters, "mac", macShort); + addString(parameters, "android_id", androidId); + addString(parameters, "fb_id", attributionId); + + // event specific + addInt(parameters, "event_count", eventCount); + addString(parameters, "event_token", eventToken); + addString(parameters, "amount", getAmountString()); + addMap(parameters, "params", callbackParameters); + + // session specific (current values at time of event) + addInt(parameters, "event_count", eventCount); + addInt(parameters, "session_count", sessionCount); + addInt(parameters, "subsession_count", subsessionCount); + addDuration(parameters, "session_length", sessionLength); + addDuration(parameters, "time_spent", timeSpent); + addDuration(parameters, "last_interval", lastInterval); + + TrackingPackage revenuePackage = new TrackingPackage(); + revenuePackage.path = "/revenue"; + revenuePackage.kind = "revenue"; + revenuePackage.suffix = getRevenueSuffix(); + revenuePackage.parameters = parameters; + revenuePackage.userAgent = userAgent; + + return revenuePackage; + + } + + private String getAmountString() { + int amountInMillis = Math.round(10 * amountInCents); + amountInCents = amountInMillis / 10.0f; // now rounded to one decimal point + String amountString = Integer.toString(amountInMillis); + return amountString; + } + + // examples: " (12.5 cent)." + // " (12.5 cent, 'abc123')." + private String getRevenueSuffix() { + String suffix = " (" + amountInCents + " cent"; + if (eventToken != null) { + suffix += ", '" + eventToken + "'"; + } + suffix += ")."; + return suffix; } private void addString(Map parameters, String key, String value) { @@ -95,7 +172,7 @@ private void addDate(Map parameters, String key, long value) { return; } Date date = new Date(value); - String dateString = date.toString(); // TODO: format + String dateString = date.toString(); // TODO: format addString(parameters, key, dateString); } @@ -103,4 +180,16 @@ private void addDuration(Map parameters, String key, long durati long durationInSeconds = durationInMilliSeconds / 1000; addInt(parameters, key, durationInSeconds); } + + private void addMap(Map parameters, String key, Map map) { + if (map == null) { + return; + } + + JSONObject jsonObject = new JSONObject(map); + byte[] jsonBytes = jsonObject.toString().getBytes(); + String encodedMap = Base64.encodeToString(jsonBytes, Base64.NO_WRAP); + + addString(parameters, key, encodedMap); + } } diff --git a/AdjustIo/src/com/adeven/adjustio/QueueThread.java b/AdjustIo/src/com/adeven/adjustio/QueueThread.java index 70e220e62..0335f472a 100644 --- a/AdjustIo/src/com/adeven/adjustio/QueueThread.java +++ b/AdjustIo/src/com/adeven/adjustio/QueueThread.java @@ -10,6 +10,7 @@ import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.OptionalDataException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -23,27 +24,19 @@ // persistent public class QueueThread extends HandlerThread { + private static final String QUEUE_FILENAME = "testqueue3"; + + private static final int MESSAGE_ARG_ADD = 72500; // TODO: change constants! + private static final int MESSAGE_ARG_TRACK_NEXT = 72510; + private static final int MESSAGE_ARG_TRACK_FIRST = 72530; + private static final int MESSAGE_ARG_READ = 72520; + private Handler queueHandler; private Context context; private AtomicBoolean isTracking; private RequestThread requestThread; private List packages; - - private static final String QUEUE_FILENAME = "testqueue"; - private static final int MESSAGE_ARG_ADD = 72500; // TODO: change numbers? - private static final int MESSAGE_ARG_REMOVE = 72510; - private static final int MESSAGE_ARG_READ = 72520; - private static final int MESSAGE_ARG_TRACK = 72530; - - // tracking loop: - // - q.addPackage - // - q.addInternal // or q.tryTrack - // - q.trackInternal (lock) // exception (unlock) - // - r.trackPackage - // - r.requestFinished - // - q.trackNext // or q.rejectFirst (unlock) - // - q.removeInternal (unlock) - // - q.trackInternal (repeat) + private boolean paused; // TODO: on session end: stop current tracking loop // add an attribute that gets read before trackInternal starts @@ -58,9 +51,12 @@ public QueueThread(Context context) { this.isTracking = new AtomicBoolean(); this.requestThread = new RequestThread(this); - readPackages(); + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_READ; + queueHandler.sendMessage(message); } + // add a package to the queue, trigger tracking public void addPackage(TrackingPackage pack) { Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_ADD; @@ -68,26 +64,34 @@ public void addPackage(TrackingPackage pack) { queueHandler.sendMessage(message); } - public void tryTrackFirstPackage() { + // try to track the oldest package + public void trackFirstPackage() { Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_TRACK; + message.arg1 = MESSAGE_ARG_TRACK_FIRST; queueHandler.sendMessage(message); } + // remove oldest package and try to track the next one + // (after success or possibly permanent failure) public void trackNextPackage() { Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_REMOVE; + message.arg1 = MESSAGE_ARG_TRACK_NEXT; queueHandler.sendMessage(message); } - public void rejectFirstPackage() { + // close the package to retry in the future (after temporary failure) + public void closeFirstPackage() { isTracking.set(false); } - private void readPackages() { - Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_READ; - queueHandler.sendMessage(message); + // interrupt the tracking loop after the current request has finished + public void pauseTracking() { + paused = true; + } + + // allow tracking requests again + public void resumeTracking() { + paused = false; } private static final class PackageHandler extends Handler { @@ -98,98 +102,128 @@ public PackageHandler(Looper looper, QueueThread queueThread) { this.queueThreadReference = new WeakReference(queueThread); } - @Override public void handleMessage(Message message) { super.handleMessage(message); QueueThread queueThread = queueThreadReference.get(); - if (queueThread == null) { - return; - } else if (message.arg1 == MESSAGE_ARG_ADD) { - queueThread.addInternal((TrackingPackage) message.obj); - } else if (message.arg1 == MESSAGE_ARG_REMOVE) { - queueThread.removeInternal(); - } else if (message.arg1 == MESSAGE_ARG_READ) { - queueThread.readInternal(); - } else if (message.arg1 == MESSAGE_ARG_TRACK) { - queueThread.trackInternal(); + if (queueThread == null) return; + + switch (message.arg1) { + case MESSAGE_ARG_ADD: + TrackingPackage trackingPackage = (TrackingPackage) message.obj; + queueThread.addInternal(trackingPackage); + break; + case MESSAGE_ARG_TRACK_FIRST: + queueThread.trackFirstInternal(); + break; + case MESSAGE_ARG_TRACK_NEXT: + queueThread.trackNextInternal(); + break; + case MESSAGE_ARG_READ: + queueThread.readPackagesInternal(); + break; } } } - // internal methods run in own thread - - // TODO: lock needed? test with sleep - private void addInternal(TrackingPackage pack) { - packages.add(pack); - writeInternal(); - - trackInternal(); - } + // internal methods run in dedicated queue thread - // TODO: check package? - private void removeInternal() { - packages.remove(0); - writeInternal(); - isTracking.set(false); + private void addInternal(TrackingPackage newPackage) { + packages.add(newPackage); + Logger.info("added package " + packages.size() + " (" + newPackage + ")"); + Logger.verbose(newPackage.parameterString()); - trackInternal(); + writePackagesInternal(); + trackFirstInternal(); } - // TODO: add lock - private void trackInternal() { + private void trackFirstInternal() { + if (paused) { + Logger.debug("paused"); + return; + } if (isTracking.getAndSet(true)) { - Logger.error("locked"); + Logger.debug("locked"); return; } + try { TrackingPackage firstPackage = packages.get(0); requestThread.trackPackage(firstPackage); - } catch (IndexOutOfBoundsException e) { + } + catch (IndexOutOfBoundsException e) { isTracking.set(false); } } - @SuppressWarnings("unchecked") - private void readInternal() { + private void trackNextInternal() { + packages.remove(0); + writePackagesInternal(); + isTracking.set(false); + trackFirstInternal(); + } + + private void readPackagesInternal() { + // initialize with empty list; if any exception gets raised + // while reading the queue file this list will be used + packages = new ArrayList(); + try { FileInputStream inputStream = context.openFileInput(QUEUE_FILENAME); BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); + try { - packages = (List) objectStream.readObject(); - Logger.info("packages " + packages.size()); - } finally { + Object object = objectStream.readObject(); + @SuppressWarnings("unchecked") + List packages = (List) object; + Logger.debug("read " + packages.size() + " packages"); + this.packages = packages; + } + catch (ClassNotFoundException e) { + Logger.error("failed to find queue class"); + } + catch (OptionalDataException e) {} catch (IOException e) { + Logger.error("failed to read queue object"); + } + catch (ClassCastException e) { + Logger.error("failed to cast queue object"); + } + finally { objectStream.close(); } - } catch (FileNotFoundException e) { - packages = new ArrayList(); - } catch (ClassNotFoundException e) { - Logger.error("class not found"); - } catch (IOException e) { - Logger.error("failed to read object"); + } + catch (FileNotFoundException e) { + Logger.verbose("queue file not found"); + } + catch (IOException e) { + Logger.error("failed to read queue file"); } } - private void writeInternal() { - try { + private void writePackagesInternal() { + try { // TODO: remove! Thread.sleep(100); - } catch (Exception e) { - } + } catch (Exception e) {} try { FileOutputStream outputStream = context.openFileOutput(QUEUE_FILENAME, Context.MODE_PRIVATE); BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream); + try { objectStream.writeObject(packages); - } catch (NotSerializableException e) { - Logger.error("failed to serialize package"); - } finally { + Logger.debug("wrote " + packages.size() + " packages"); + } + catch (NotSerializableException e) { + Logger.error("failed to serialize packages"); + } + finally { objectStream.close(); } - } catch (IOException e) { - Logger.error("failed to write packages (" + e.getLocalizedMessage() + ")"); // TODO: improve log + } + catch (IOException e) { + Logger.error("failed to write packages (" + e.getLocalizedMessage() + ")"); e.printStackTrace(); } } diff --git a/AdjustIo/src/com/adeven/adjustio/RequestThread.java b/AdjustIo/src/com/adeven/adjustio/RequestThread.java index fe4ee932a..e40bb5fe7 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestThread.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestThread.java @@ -1,12 +1,3 @@ -// -// RequestThread.java -// AdjustIo -// -// Created by Benjamin Weiss on 17.4.13 -// Copyright (c) 2012 adeven. All rights reserved. -// See the file MIT-LICENSE for copying permission. -// - package com.adeven.adjustio; import java.io.ByteArrayOutputStream; @@ -25,15 +16,9 @@ import android.os.Looper; import android.os.Message; -/** - * Used to send tracking packages. - * - * @author keyboardsurfer - * @since 17.4.13 - */ public class RequestThread extends HandlerThread { - private static final int MESSAGE_ARG_TRACK = 72400; + private Handler trackingHandler; private QueueThread queueThread; @@ -61,39 +46,40 @@ public RequestHandler(Looper looper, RequestThread requestThread) { this.requestThreadReference = new WeakReference(requestThread); } - public void handleMessage(Message message) { + public void handleMessage(Message message) { super.handleMessage(message); RequestThread requestThread = requestThreadReference.get(); - if (requestThread == null) { - return; - } else if (message.arg1 == MESSAGE_ARG_TRACK) { + if (requestThread == null) return; + + if (message.arg1 == MESSAGE_ARG_TRACK) { requestThread.trackInternal((TrackingPackage) message.obj); } } } private void trackInternal(TrackingPackage trackingPackage) { - - HttpClient httpClient = Util.getHttpClient(trackingPackage.userAgent); - HttpPost request = Util.getPostRequest(trackingPackage.path); - // TODO: test all paths! - // TODO: track next if unsure (because rejected packages will always be retried) try { + HttpClient httpClient = Util.getHttpClient(trackingPackage.userAgent); + HttpPost request = Util.getPostRequest(trackingPackage.path); trackingPackage.injectEntity(request); HttpResponse response = httpClient.execute(request); requestFinished(response, trackingPackage); - } catch (UnsupportedEncodingException e) { + } + catch (UnsupportedEncodingException e) { Logger.error("failed to encode parameters"); queueThread.trackNextPackage(); - } catch (ClientProtocolException e) { + } + catch (ClientProtocolException e) { Logger.error("client protocol error"); - queueThread.rejectFirstPackage(); // TODO: track next, right? - } catch (IOException e) { - Logger.error("connection failed (" + e.getLocalizedMessage() + ")"); // TODO: "will try again later" - queueThread.rejectFirstPackage(); - } catch (IllegalArgumentException e) { + queueThread.closeFirstPackage(); + } + catch (IOException e) { + Logger.error(trackingPackage.getFailureMessage() + " Will retry later. (Request failed: " + e.getLocalizedMessage() + ")"); + queueThread.closeFirstPackage(); + } + catch (IllegalArgumentException e) { Logger.error("Failed to track package (" + e.getLocalizedMessage() + ")"); queueThread.trackNextPackage(); } @@ -101,8 +87,8 @@ private void trackInternal(TrackingPackage trackingPackage) { private void requestFinished(HttpResponse response, TrackingPackage trackingPackage) { if (response == null) { // TODO: test - Logger.debug(trackingPackage.failureMessage + " (Request failed)"); // TODO: "will retry later" like on ios - queueThread.rejectFirstPackage(); + Logger.debug(trackingPackage.getFailureMessage() + " (Request failed)"); // TODO: "will retry later" like on ios + queueThread.closeFirstPackage(); return; } @@ -110,9 +96,9 @@ private void requestFinished(HttpResponse response, TrackingPackage trackingPack String responseString = parseResponse(response); if (statusCode == HttpStatus.SC_OK) { - Logger.info(trackingPackage.successMessage); + Logger.info(trackingPackage.getSuccessMessage()); } else { - Logger.warn(trackingPackage.failureMessage + " (" + responseString + ")"); + Logger.warn(trackingPackage.getFailureMessage() + " (" + responseString + ")"); } queueThread.trackNextPackage(); @@ -125,7 +111,8 @@ private String parseResponse(HttpResponse response) { out.close(); String responseString = out.toString().trim(); return responseString; - } catch (Exception e) { + } + catch (Exception e) { Logger.error("error parsing response", e); return "Failed parsing response"; } diff --git a/AdjustIo/src/com/adeven/adjustio/SessionState.java b/AdjustIo/src/com/adeven/adjustio/SessionState.java index c883905ac..92cdd1be5 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionState.java +++ b/AdjustIo/src/com/adeven/adjustio/SessionState.java @@ -18,6 +18,7 @@ public class SessionState implements Serializable { public long timeSpent; public long createdAt; // all times in milliseconds since 1970 public long lastActivity; + public long lastInterval; public SessionState() { eventCount = 0; // no events yet @@ -27,6 +28,7 @@ public SessionState() { timeSpent = -1; // this information will be collected and attached to the next session createdAt = -1; lastActivity = -1; + lastInterval = -1; } public void startNextSession(long now) { @@ -36,18 +38,27 @@ public void startNextSession(long now) { timeSpent = 0; // no time spent yet createdAt = now; lastActivity = now; + lastInterval = 0; } - public PackageBuilder getPackageBuilder() { - PackageBuilder builder = new PackageBuilder(); + public void injectSessionAttributes(PackageBuilder builder) { + builder.sessionCount = sessionCount; + builder.subsessionCount = subsessionCount; + builder.sessionLength = sessionLength; + builder.timeSpent = timeSpent; + builder.createdAt = createdAt; + + builder.lastInterval = lastInterval; + } + public void injectEventAttributes(PackageBuilder builder) { builder.sessionCount = sessionCount; builder.subsessionCount = subsessionCount; builder.sessionLength = sessionLength; builder.timeSpent = timeSpent; builder.createdAt = createdAt; - return builder; + builder.eventCount = eventCount; } public String toString() { diff --git a/AdjustIo/src/com/adeven/adjustio/SessionThread.java b/AdjustIo/src/com/adeven/adjustio/SessionThread.java index d341382d2..848eae6eb 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionThread.java +++ b/AdjustIo/src/com/adeven/adjustio/SessionThread.java @@ -6,8 +6,10 @@ 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.Date; import java.util.Map; @@ -22,36 +24,37 @@ import android.os.Message; public class SessionThread extends HandlerThread { - private String appToken; - private Context context; - - private String macSha1; - private String macShort; - private String userAgent; - private String androidId; - private String attributionId; + private static final String SESSION_FILENAME = "sessionstate1"; - private Handler sessionHandler; - private SessionState sessionState; - private QueueThread queueThread; - private static ScheduledExecutorService executor; + private static final long UPDATE_INTERVAL = 1000 * 10; // 10 second, TODO: one minute + private static final long SESSION_INTERVAL = 1000 * 15; // 30 seconds, TODO: 30 minutes + private static final long SUBSESSION_INTERVAL = 1000 * 1; // one second - private static final String SESSION_FILENAME = "sessionstate"; - private static final int MESSAGE_ARG_UPDATE = 72610; private static final int MESSAGE_ARG_INIT = 72630; private static final int MESSAGE_ARG_START = 72640; private static final int MESSAGE_ARG_END = 72650; private static final int MESSAGE_ARG_EVENT = 72660; private static final int MESSAGE_ARG_REVENUE = 72670; - private static final long SESSION_INTERVAL = 1000 * 1; // 1 second, TODO: 30 - // minutes + private Handler sessionHandler; + private SessionState sessionState; + private QueueThread queueThread; + private static ScheduledExecutorService executor; + + private Context context; + + private String appToken; + private String macSha1; + private String macShort; + private String userAgent; // changes, should be updated periodically + private String androidId; // everything else here could be persisted + private String attributionId; public SessionThread(String appToken, Context context) { super(Logger.LOGTAG, MIN_PRIORITY); setDaemon(true); start(); - this.sessionHandler = new SessionHandler(getLooper(), this); + sessionHandler = new SessionHandler(getLooper(), this); this.appToken = appToken; this.context = context; @@ -96,12 +99,6 @@ public void trackRevenue(float amountInCents, String eventToken, Map sessionThreadReference; @@ -114,14 +111,9 @@ public void handleMessage(Message message) { super.handleMessage(message); SessionThread sessionThread = sessionThreadReference.get(); - if (sessionThread == null) { - return; - } + if (sessionThread == null) return; switch (message.arg1) { - case MESSAGE_ARG_UPDATE: - sessionThread.updateInternal(); - break; case MESSAGE_ARG_INIT: sessionThread.initInternal(); break; @@ -132,10 +124,12 @@ public void handleMessage(Message message) { sessionThread.endInternal(); break; case MESSAGE_ARG_EVENT: - sessionThread.eventInternal((PackageBuilder) message.obj); + PackageBuilder eventBuilder = (PackageBuilder) message.obj; + sessionThread.eventInternal(eventBuilder); break; case MESSAGE_ARG_REVENUE: - sessionThread.revenueInternal((PackageBuilder) message.obj); + PackageBuilder revenueBuilder = (PackageBuilder) message.obj; + sessionThread.revenueInternal(revenueBuilder); break; } } @@ -143,16 +137,12 @@ public void handleMessage(Message message) { // TODO: rename internal methods + // called from outside + private void initInternal() { - if (!Util.checkPermissions(context)) - return; - if (!checkAppToken(appToken)) - return; - if (!checkContext(context)) - return; + if (!checkState()) return; String macAddress = Util.getMacAddress(context); - macSha1 = Util.sha1(macAddress); macShort = macAddress.replaceAll(":", ""); userAgent = Util.getUserAgent(context); @@ -163,167 +153,150 @@ private void initInternal() { readSessionStateInternal(); } - private void updateInternal() { - Logger.info("update"); - } - private void startInternal() { - // startExecutor(); // TODO: enable + if (!checkState()) return; + queueThread.resumeTracking(); + startExecutor(); long now = new Date().getTime(); - // very first session on this device + // very first session if (sessionState == null) { Logger.info("first session"); sessionState = new SessionState(); sessionState.sessionCount = 1; // this is the first session - sessionState.createdAt = now; // starting now (that's all we know) + sessionState.createdAt = now; // starting now (that's all we know) - enqueueSessionInternal(-1); + enqueueSessionInternal(); writeSessionStateInternal(); return; } long lastInterval = now - sessionState.lastActivity; if (lastInterval < 0) { - Logger.info("time travel"); - // TODO: extract this check? - // should not happen, skip last interval and continue from here + Logger.error("time travel"); sessionState.lastActivity = now; return; } // new session if (lastInterval > SESSION_INTERVAL) { - Logger.info("new session"); - enqueueSessionInternal(lastInterval); + sessionState.lastInterval = lastInterval; + enqueueSessionInternal(); sessionState.startNextSession(now); writeSessionStateInternal(); return; } // new subsession start - sessionState.subsessionCount++; + if (lastInterval > SUBSESSION_INTERVAL) { + sessionState.subsessionCount++; + } sessionState.sessionLength += lastInterval; sessionState.lastActivity = now; - Logger.info("new subsession " + sessionState.subsessionCount); + + writeSessionStateInternal(); } private void endInternal() { + if (!checkState()) return; + queueThread.pauseTracking(); stopExecutor(); + updateInternal(); + } - if (sessionState == null) { - // ignore session ends before first session start - return; - } + private void eventInternal(PackageBuilder eventBuilder) { + if (!checkState()) return; + if (!checkEventTokenNotNull(eventBuilder.eventToken)) return; + if (!checkEventTokenLength(eventBuilder.eventToken)) return; + + sessionState.eventCount++; + updateInternal(); + injectGeneralAttributes(eventBuilder); + sessionState.injectEventAttributes(eventBuilder); + + TrackingPackage eventPackage = eventBuilder.buildEventPackage(); + queueThread.addPackage(eventPackage); + } + + private void revenueInternal(PackageBuilder revenueBuilder) { + if (!checkState()) return; + if (!checkEventTokenLength(revenueBuilder.eventToken)) return; + + sessionState.eventCount++; + updateInternal(); + injectGeneralAttributes(revenueBuilder); + sessionState.injectEventAttributes(revenueBuilder); + + TrackingPackage revenuePackage = revenueBuilder.buildRevenuePackage(); + queueThread.addPackage(revenuePackage); + } + + // called from inside + + private void updateInternal() { + if (sessionState == null) return; long now = new Date().getTime(); long lastInterval = now - sessionState.lastActivity; if (lastInterval < 0) { - // should not happen, skip last interval and continue from here + Logger.error("time travel"); sessionState.lastActivity = now; return; } - if (lastInterval > SESSION_INTERVAL) { - // ignore late ends (should not happen because of activity timer) - return; - } + if (lastInterval > SESSION_INTERVAL) return; - // subsession end sessionState.sessionLength += lastInterval; sessionState.timeSpent += lastInterval; sessionState.lastActivity = now; + + writeSessionStateInternal(); } - private void enqueueSessionInternal(long lastInterval) { - PackageBuilder builder = sessionState.getPackageBuilder(); - builder.lastInterval = lastInterval; + private void enqueueSessionInternal() { + PackageBuilder builder = new PackageBuilder(); + injectGeneralAttributes(builder); + sessionState.injectSessionAttributes(builder); + TrackingPackage sessionPackage = builder.buildSessionPackage(); + queueThread.addPackage(sessionPackage); + } - // TODO: extract? + private void injectGeneralAttributes(PackageBuilder builder) { builder.userAgent = userAgent; builder.appToken = appToken; builder.macShort = macShort; builder.macSha1 = macSha1; builder.androidId = androidId; builder.attributionId = attributionId; - - TrackingPackage sessionStart = builder.buildSessionPackage(); - queueThread.addPackage(sessionStart); - } - - private void eventInternal(PackageBuilder builder) { - // TODO: clean up the builder stuff: reuse builder or even use its - // eventToken and parameters - if (!checkEventTokenNotNull(builder.eventToken)) - return; - if (!checkEventTokenLength(builder.eventToken)) - return; - - String successMessage = "Tracked event: '" + builder.eventToken + "'"; - String failureMessage = "Failed to track event: '" + builder.eventToken + "'"; - - builder.path = "/event"; - builder.successMessage = successMessage; - builder.failureMessage = failureMessage; - builder.userAgent = userAgent; - builder.appToken = appToken; - builder.macShort = macShort; - builder.androidId = androidId; - builder.eventToken = builder.eventToken; -// TrackingPackage eventPackage = builder.build(); -// -// queueThread.addPackage(eventPackage); - } - - private void revenueInternal(PackageBuilder builder) { - if (!checkEventTokenLength(builder.eventToken)) - return; - - Logger.info("revenueInternal"); - // TODO: clean up and extract general event stuff? - -// String successMessage = "Tracked revenue: " + amountInCents + " Cent"; -// String failureMessage = "Failed to track revenue: " + amountInCents + " Cent"; -// -// if (builder.eventToken != null) { -// String eventString = " (event token: '" + builder.eventToken + "')"; -// successMessage += eventString; -// failureMessage += eventString; -// } -// -// builder.path = "/revenue"; -// builder.successMessage = successMessage; -// builder.failureMessage = failureMessage; -// builder.userAgent = userAgent; -// builder.appToken = appToken; -// builder.macShort = macShort; -// builder.androidId = androidId; -// builder.eventToken = builder.eventToken; -// TrackingPackage revenuePackage = builder.build(); -// // getRequestThread().track(revenue); -// queueThread.addPackage(revenuePackage); } private void readSessionStateInternal() { + // if any exception gets raised we start with a fresh sessionState + sessionState = null; + try { FileInputStream inputStream = context.openFileInput(SESSION_FILENAME); BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); + try { sessionState = (SessionState) objectStream.readObject(); - Logger.info("readSessionState " + sessionState); + Logger.debug("read session state: " + sessionState); + } catch (ClassNotFoundException e) { + Logger.error("failed to find session state class"); + } catch (OptionalDataException e) {} catch (IOException e) { + Logger.error("failed to read session states object"); + } catch (ClassCastException e) { + Logger.error("failed to cast session state object"); } finally { objectStream.close(); } + } catch (FileNotFoundException e) { - // first ever session start - } catch (ClassNotFoundException e) { - Logger.error("class not found"); + Logger.verbose("session state file not found"); } catch (IOException e) { - Logger.error("failed to read object"); - } catch (IllegalArgumentException e) { - Logger.error("illegal argument"); + Logger.error("failed to read session state file"); } } @@ -337,14 +310,18 @@ private void writeSessionStateInternal() { FileOutputStream outputStream = context.openFileOutput(SESSION_FILENAME, Context.MODE_PRIVATE); BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream); + try { - Logger.info("writeSessionState " + sessionState); objectStream.writeObject(sessionState); + Logger.debug("wrote session state: " + sessionState); + } catch (NotSerializableException e) { + Logger.error("failed to serialize session state"); } finally { objectStream.close(); } + } catch (IOException e) { - Logger.error("failed to write package X (" + e.getLocalizedMessage() + ")"); // TODO: improve log + Logger.error("failed to write session state (" + e.getLocalizedMessage() + ")"); // TODO: improve log } } @@ -353,11 +330,10 @@ private void startExecutor() { executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleWithFixedDelay(new Runnable() { public void run() { - Logger.info("timer"); updateInternal(); - queueThread.tryTrackFirstPackage(); + queueThread.trackFirstPackage(); } - }, 5, 5, TimeUnit.SECONDS); // TODO: one minute, extract constants + }, 1000, UPDATE_INTERVAL, TimeUnit.MILLISECONDS); } private void stopExecutor() { @@ -368,12 +344,19 @@ private void stopExecutor() { } } + private boolean checkState() { + if (!Util.checkPermissions(context)) return false; + if (!checkAppToken(appToken)) return false; + if (!checkContext(context)) return false; + return true; + } + private static boolean checkAppToken(String appToken) { if (appToken == null) { Logger.error("Missing App Token."); return false; } else if (appToken.length() != 12) { - Logger.error("Malformed App Token " + appToken); + Logger.error("Malformed App Token '" + appToken + "'"); return false; } return true; diff --git a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java index acbffa9b2..29c3200e6 100644 --- a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java @@ -2,10 +2,6 @@ package com.adeven.adjustio; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.util.ArrayList; @@ -26,18 +22,20 @@ public class TrackingPackage implements Serializable { public String userAgent; Map parameters; - // for logging + // logs public String kind; - public String successMessage; - public String failureMessage; + public String suffix; public String toString() { - return "k:" + kind + - " pt:" + path + - " pr:" + parameters.toString() + // TODO: format? - " ua:" + userAgent + - " sm:" + successMessage + - " fm:" + failureMessage; + return kind + suffix + " " + path; + } + + public String parameterString() { + String parameterString = "parameters:"; + for (Map.Entry entity : parameters.entrySet()) { + parameterString += String.format("\n\t%-16s %s", entity.getKey(), entity.getValue()); + } + return parameterString; } public void injectEntity(HttpPost request) throws UnsupportedEncodingException { @@ -56,4 +54,11 @@ public void injectEntity(HttpPost request) throws UnsupportedEncodingException { request.setEntity(entity); } + public String getSuccessMessage() { + return "Tracked " + kind + suffix; + } + + public String getFailureMessage() { + return "Failed to track " + kind + suffix; + } } diff --git a/AdjustIo/src/com/adeven/adjustio/Util.java b/AdjustIo/src/com/adeven/adjustio/Util.java index 6c3beceab..472f8e871 100644 --- a/AdjustIo/src/com/adeven/adjustio/Util.java +++ b/AdjustIo/src/com/adeven/adjustio/Util.java @@ -12,22 +12,15 @@ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import java.security.MessageDigest; -import java.util.List; import java.util.Locale; -import java.util.Map; -import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; -import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.BasicHttpParams; import org.apache.http.params.CoreProtocolPNames; import org.apache.http.params.HttpParams; -import org.json.JSONObject; import android.content.ContentResolver; import android.content.Context; @@ -42,7 +35,6 @@ import android.os.Build; import android.provider.Settings.Secure; import android.text.TextUtils; -import android.util.Base64; import android.util.DisplayMetrics; /** @@ -52,7 +44,7 @@ * @since 11.10.12 */ public class Util { - private static final String BASEURL = "http://192.168.178.117:8509"; + private static final String BASEURL = "http://192.168.178.117:8509"; // TODO: change! private static final String CLIENTSDK = "android1.6"; private static final String UNKNOWN = "unknown"; @@ -83,19 +75,9 @@ private static boolean checkPermission(Context context, String permission) { return granted; } - public static String getBase64EncodedParameters(Map parameters) { - if (parameters == null) { - return null; - } - - JSONObject jsonObject = new JSONObject(parameters); - byte[] bytes = jsonObject.toString().getBytes(); - String encoded = Base64.encodeToString(bytes, Base64.NO_WRAP); - return encoded; - } - public static HttpClient getHttpClient(String userAgent) { - HttpClient httpClient = new DefaultHttpClient(); + HttpParams httpParams = new BasicHttpParams(); + HttpClient httpClient = new DefaultHttpClient(httpParams); HttpParams params = httpClient.getParams(); params.setParameter(CoreProtocolPNames.USER_AGENT, userAgent); return httpClient; From cde1d435543ed79e4cfd91a87fe40b08595c1e2b Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Fri, 28 Jun 2013 16:36:08 +0200 Subject: [PATCH 12/37] Extract, refactor --- .../src/com/adeven/adjustio/AdjustIo.java | 2 + .../com/adeven/adjustio/PackageBuilder.java | 43 ++++------- .../src/com/adeven/adjustio/QueueThread.java | 8 +- .../com/adeven/adjustio/RequestThread.java | 73 +++++++++++++++---- .../com/adeven/adjustio/TrackingPackage.java | 47 +++++++----- AdjustIo/src/com/adeven/adjustio/Util.java | 30 +------- 6 files changed, 111 insertions(+), 92 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java index d6d158250..6a8f52b19 100644 --- a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java +++ b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java @@ -23,6 +23,8 @@ * @since 11.10.12 */ public class AdjustIo { + protected static final String BASE_URL = "http://192.168.178.117:8509"; // TODO: change! + protected static final String CLIENT_SDK = "android1.6"; // forwards everything to sessionThread private static SessionThread sessionThread; diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java index 2f5ff6814..c8c6fbd17 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -37,15 +37,7 @@ public class PackageBuilder { public String suffix; public TrackingPackage buildSessionPackage() { - Map parameters = new HashMap(); - - // general - addDate(parameters, "created_at", createdAt); - addString(parameters, "app_token", appToken); - addString(parameters, "mac_sha1", macSha1); - addString(parameters, "mac", macShort); - addString(parameters, "android_id", androidId); - addString(parameters, "fb_id", attributionId); + Map parameters = getDefaultParameters(); // session specific addInt(parameters, "session_id", sessionCount); // TODO: rename? @@ -65,15 +57,7 @@ public TrackingPackage buildSessionPackage() { } public TrackingPackage buildEventPackage() { - Map parameters = new HashMap(); - - // general - addDate(parameters, "created_at", createdAt); - addString(parameters, "app_token", appToken); - addString(parameters, "mac_sha1", macSha1); - addString(parameters, "mac", macShort); - addString(parameters, "android_id", androidId); - addString(parameters, "fb_id", attributionId); + Map parameters = getDefaultParameters(); // event specific addInt(parameters, "event_count", eventCount); @@ -98,16 +82,7 @@ public TrackingPackage buildEventPackage() { } public TrackingPackage buildRevenuePackage() { - - Map parameters = new HashMap(); - - // general - addDate(parameters, "created_at", createdAt); - addString(parameters, "app_token", appToken); - addString(parameters, "mac_sha1", macSha1); - addString(parameters, "mac", macShort); - addString(parameters, "android_id", androidId); - addString(parameters, "fb_id", attributionId); + Map parameters = getDefaultParameters(); // event specific addInt(parameters, "event_count", eventCount); @@ -131,7 +106,19 @@ public TrackingPackage buildRevenuePackage() { revenuePackage.userAgent = userAgent; return revenuePackage; + } + + private Map getDefaultParameters() { + Map parameters = new HashMap(); + + addDate(parameters, "created_at", createdAt); + addString(parameters, "app_token", appToken); + addString(parameters, "mac_sha1", macSha1); + addString(parameters, "mac", macShort); + addString(parameters, "android_id", androidId); + addString(parameters, "fb_id", attributionId); + return parameters; } private String getAmountString() { diff --git a/AdjustIo/src/com/adeven/adjustio/QueueThread.java b/AdjustIo/src/com/adeven/adjustio/QueueThread.java index 0335f472a..fc41753bc 100644 --- a/AdjustIo/src/com/adeven/adjustio/QueueThread.java +++ b/AdjustIo/src/com/adeven/adjustio/QueueThread.java @@ -32,9 +32,9 @@ public class QueueThread extends HandlerThread { private static final int MESSAGE_ARG_READ = 72520; private Handler queueHandler; + private RequestThread requestThread; private Context context; private AtomicBoolean isTracking; - private RequestThread requestThread; private List packages; private boolean paused; @@ -130,7 +130,7 @@ public void handleMessage(Message message) { private void addInternal(TrackingPackage newPackage) { packages.add(newPackage); - Logger.info("added package " + packages.size() + " (" + newPackage + ")"); + Logger.debug("added package " + packages.size() + " (" + newPackage + ")"); Logger.verbose(newPackage.parameterString()); writePackagesInternal(); @@ -177,7 +177,7 @@ private void readPackagesInternal() { Object object = objectStream.readObject(); @SuppressWarnings("unchecked") List packages = (List) object; - Logger.debug("read " + packages.size() + " packages"); + Logger.debug("queue thread read " + packages.size() + " packages"); this.packages = packages; } catch (ClassNotFoundException e) { @@ -213,7 +213,7 @@ private void writePackagesInternal() { try { objectStream.writeObject(packages); - Logger.debug("wrote " + packages.size() + " packages"); + Logger.verbose("queue thread wrote " + packages.size() + " packages"); } catch (NotSerializableException e) { Logger.error("failed to serialize packages"); diff --git a/AdjustIo/src/com/adeven/adjustio/RequestThread.java b/AdjustIo/src/com/adeven/adjustio/RequestThread.java index e40bb5fe7..bc23e1765 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestThread.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestThread.java @@ -4,12 +4,18 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.ref.WeakReference; +import java.net.SocketTimeoutException; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.CoreProtocolPNames; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; import android.os.Handler; import android.os.HandlerThread; @@ -17,10 +23,15 @@ import android.os.Message; public class RequestThread extends HandlerThread { + private static final int CONNECTION_TIMEOUT = 1000 * 5; // 5 seconds TODO: 1 minute? + private static final int SOCKET_TIMEOUT = 1000 * 5; // 5 seconds TODO: 1 minute? + + private static final int MESSAGE_ARG_INIT = 72401; private static final int MESSAGE_ARG_TRACK = 72400; private Handler trackingHandler; private QueueThread queueThread; + private HttpClient httpClient; public RequestThread(QueueThread queueThread) { super(Logger.LOGTAG, MIN_PRIORITY); @@ -29,6 +40,10 @@ public RequestThread(QueueThread queueThread) { this.trackingHandler = new RequestHandler(getLooper(), this); this.queueThread = queueThread; + + Message message = Message.obtain(); + message.arg1 = MESSAGE_ARG_INIT; + trackingHandler.sendMessage(message); } public void trackPackage(TrackingPackage pack) { @@ -52,18 +67,31 @@ public void handleMessage(Message message) { RequestThread requestThread = requestThreadReference.get(); if (requestThread == null) return; - if (message.arg1 == MESSAGE_ARG_TRACK) { - requestThread.trackInternal((TrackingPackage) message.obj); + switch (message.arg1) { + case MESSAGE_ARG_INIT: + requestThread.initInternal(); + break; + case MESSAGE_ARG_TRACK: + TrackingPackage trackingPackage = (TrackingPackage) message.obj; + requestThread.trackInternal(trackingPackage); + break; } } } + private void initInternal() { + HttpParams httpParams = new BasicHttpParams(); + HttpConnectionParams.setConnectionTimeout(httpParams, CONNECTION_TIMEOUT); + HttpConnectionParams.setSoTimeout(httpParams, SOCKET_TIMEOUT); + httpClient = new DefaultHttpClient(httpParams); + Logger.error("init httpclient " + httpClient); + } + private void trackInternal(TrackingPackage trackingPackage) { // TODO: test all paths! try { - HttpClient httpClient = Util.getHttpClient(trackingPackage.userAgent); - HttpPost request = Util.getPostRequest(trackingPackage.path); - trackingPackage.injectEntity(request); + setUserAgent(trackingPackage.userAgent); + HttpUriRequest request = trackingPackage.getRequest(); HttpResponse response = httpClient.execute(request); requestFinished(response, trackingPackage); } @@ -72,22 +100,34 @@ private void trackInternal(TrackingPackage trackingPackage) { queueThread.trackNextPackage(); } catch (ClientProtocolException e) { - Logger.error("client protocol error"); - queueThread.closeFirstPackage(); + closePackage(trackingPackage, "Client protocol error"); + } + catch (SocketTimeoutException e) { + closePackage(trackingPackage, "Request timed out"); } catch (IOException e) { - Logger.error(trackingPackage.getFailureMessage() + " Will retry later. (Request failed: " + e.getLocalizedMessage() + ")"); - queueThread.closeFirstPackage(); + closePackage(trackingPackage, "Request failed: " + e.getLocalizedMessage()); } - catch (IllegalArgumentException e) { - Logger.error("Failed to track package (" + e.getLocalizedMessage() + ")"); - queueThread.trackNextPackage(); + catch (RuntimeException e) { + trackNextPackage(trackingPackage, "Runtime exception: " + e.getClass() + ": " + e.getLocalizedMessage()); } } + private void closePackage(TrackingPackage trackingPackage, String message) { + String failureMessage = trackingPackage.getFailureMessage(); + Logger.error(failureMessage + " Will retry later. (" + message + ")"); + queueThread.closeFirstPackage(); + } + + private void trackNextPackage(TrackingPackage trackingPackage, String message) { + String failureMessage = trackingPackage.getFailureMessage(); + Logger.error(failureMessage + " (No parameters found)"); + queueThread.trackNextPackage(); + } + private void requestFinished(HttpResponse response, TrackingPackage trackingPackage) { if (response == null) { // TODO: test - Logger.debug(trackingPackage.getFailureMessage() + " (Request failed)"); // TODO: "will retry later" like on ios + Logger.debug(trackingPackage.getFailureMessage() + " (Missing response)"); // TODO: "will retry later" like on ios queueThread.closeFirstPackage(); return; } @@ -117,4 +157,9 @@ private String parseResponse(HttpResponse response) { return "Failed parsing response"; } } + + private void setUserAgent(String userAgent) { + HttpParams httpParams = httpClient.getParams(); + httpParams.setParameter(CoreProtocolPNames.USER_AGENT, userAgent); + } } diff --git a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java index 29c3200e6..f68be43b1 100644 --- a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java @@ -6,21 +6,24 @@ import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Map; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; +// TODO: change public to protected where possible public class TrackingPackage implements Serializable { private static final long serialVersionUID = -35935556512024097L; // data public String path; public String userAgent; - Map parameters; + public Map parameters; // logs public String kind; @@ -31,17 +34,33 @@ public String toString() { } public String parameterString() { - String parameterString = "parameters:"; - for (Map.Entry entity : parameters.entrySet()) { - parameterString += String.format("\n\t%-16s %s", entity.getKey(), entity.getValue()); + try { + String parameterString = "Parameters:"; + for (Map.Entry entity : parameters.entrySet()) { + parameterString += String.format("\n\t%-16s %s", entity.getKey(), entity.getValue()); + } + return parameterString; + } + catch (NullPointerException e) { + return "Parameters: null"; } - return parameterString; } - public void injectEntity(HttpPost request) throws UnsupportedEncodingException { - if (parameters == null) { - return; - } + public String getSuccessMessage() { + return "Tracked " + kind + suffix; + } + + public String getFailureMessage() { + return "Failed to track " + kind + suffix; + } + + public HttpUriRequest getRequest() throws UnsupportedEncodingException { + String url = AdjustIo.BASE_URL + path; + HttpPost request = new HttpPost(url); + + String language = Locale.getDefault().getLanguage(); + request.addHeader("Accept-Language", language); + request.addHeader("Client-SDK", AdjustIo.CLIENT_SDK); List pairs = new ArrayList(); for (Map.Entry entity : parameters.entrySet()) { @@ -52,13 +71,7 @@ public void injectEntity(HttpPost request) throws UnsupportedEncodingException { UrlEncodedFormEntity entity = new UrlEncodedFormEntity(pairs); entity.setContentType(URLEncodedUtils.CONTENT_TYPE); request.setEntity(entity); - } - public String getSuccessMessage() { - return "Tracked " + kind + suffix; - } - - public String getFailureMessage() { - return "Failed to track " + kind + suffix; + return request; } -} +} \ No newline at end of file diff --git a/AdjustIo/src/com/adeven/adjustio/Util.java b/AdjustIo/src/com/adeven/adjustio/Util.java index 472f8e871..6848a4e82 100644 --- a/AdjustIo/src/com/adeven/adjustio/Util.java +++ b/AdjustIo/src/com/adeven/adjustio/Util.java @@ -15,13 +15,6 @@ import java.security.MessageDigest; import java.util.Locale; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.CoreProtocolPNames; -import org.apache.http.params.HttpParams; - import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageInfo; @@ -37,6 +30,7 @@ import android.text.TextUtils; import android.util.DisplayMetrics; +// TODO: clean up, move functions to callers /** * Collects utility functions used by AdjustIo. * @@ -44,9 +38,6 @@ * @since 11.10.12 */ public class Util { - private static final String BASEURL = "http://192.168.178.117:8509"; // TODO: change! - private static final String CLIENTSDK = "android1.6"; - private static final String UNKNOWN = "unknown"; public static boolean checkPermissions(Context context) { @@ -75,25 +66,6 @@ private static boolean checkPermission(Context context, String permission) { return granted; } - public static HttpClient getHttpClient(String userAgent) { - HttpParams httpParams = new BasicHttpParams(); - HttpClient httpClient = new DefaultHttpClient(httpParams); - HttpParams params = httpClient.getParams(); - params.setParameter(CoreProtocolPNames.USER_AGENT, userAgent); - return httpClient; - } - - public static HttpPost getPostRequest(String path) { - String url = BASEURL + path; - HttpPost request = new HttpPost(url); - - String language = Locale.getDefault().getLanguage(); - request.addHeader("Accept-Language", language); - request.addHeader("Client-SDK", CLIENTSDK); - - return request; - } - protected static String getUserAgent(Context context) { Resources resources = context.getResources(); DisplayMetrics displayMetrics = resources.getDisplayMetrics(); From c1e22c8023b5a31f158f1d0bdffb22f496f4ef33 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Fri, 28 Jun 2013 17:38:36 +0200 Subject: [PATCH 13/37] Reduce visibility where possible from public to protected --- AdjustIo/src/com/adeven/adjustio/Logger.java | 30 ++++--------- .../com/adeven/adjustio/PackageBuilder.java | 44 +++++++++---------- .../src/com/adeven/adjustio/QueueThread.java | 16 +++---- .../com/adeven/adjustio/RequestThread.java | 40 +++++++---------- .../src/com/adeven/adjustio/SessionState.java | 24 +++++----- .../com/adeven/adjustio/SessionThread.java | 14 +++--- .../com/adeven/adjustio/TrackingPackage.java | 20 ++++----- AdjustIo/src/com/adeven/adjustio/Util.java | 10 ++--- 8 files changed, 89 insertions(+), 109 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/Logger.java b/AdjustIo/src/com/adeven/adjustio/Logger.java index 968fe5874..f0135e8d9 100644 --- a/AdjustIo/src/com/adeven/adjustio/Logger.java +++ b/AdjustIo/src/com/adeven/adjustio/Logger.java @@ -1,11 +1,3 @@ -// -// Logger.java -// AdjustIo -// -// Created by Benjamin Weiss on 17.4.13 -// Copyright (c) 2012 adeven. All rights reserved. -// See the file MIT-LICENSE for copying permission. -// package com.adeven.adjustio; @@ -14,56 +6,50 @@ // TODO: check if java.util.logging.Logger is an alternative // TODO: go through all log calls and set a proper log level -/** - * A Wrapper that allows easy toggles of Logging. - * - * @author keyboardsurfer - * @since 17.4.13 - */ public class Logger { protected static final String LOGTAG = "AdjustIo"; private static int logLevel = Log.INFO; - public static void setLogLevel(int logLevel) { + protected static void setLogLevel(int logLevel) { Logger.logLevel = logLevel; } - public static void verbose(String message) { + protected static void verbose(String message) { if (logLevel <= Log.VERBOSE) { Log.v(LOGTAG, message); } } - public static void verbose(String context, String name, String value) { + protected static void verbose(String context, String name, String value) { verbose("[" + context + "] " + name + ": '" + value + "'"); } - public static void debug(String message) { + protected static void debug(String message) { if (logLevel <= Log.DEBUG) { Log.d(LOGTAG, message); } } - public static void info(String message) { + protected static void info(String message) { if (logLevel <= Log.INFO) { Log.i(LOGTAG, message); } } - public static void warn(String message) { + protected static void warn(String message) { if (logLevel <= Log.WARN) { Log.w(LOGTAG, message); } } - public static void error(String message) { + protected static void error(String message) { if (logLevel <= Log.ERROR) { Log.e(LOGTAG, message); } } - public static void error(String message, Throwable throwable) { + protected static void error(String message, Throwable throwable) { if (logLevel <= Log.ERROR) { Log.e(LOGTAG, message, throwable); } diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java index c8c6fbd17..4a8ff206d 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -10,33 +10,33 @@ public class PackageBuilder { // general - public String appToken; - public String macSha1; - public String macShort; // TODO: md5! - public String androidId; - public String attributionId; + protected String appToken; + protected String macSha1; + protected String macShort; // TODO: md5! + protected String androidId; + protected String attributionId; // sessions - public int sessionCount; - public int subsessionCount; - public long createdAt; - public long sessionLength; - public long timeSpent; - public long lastInterval; + protected int sessionCount; + protected int subsessionCount; + protected long createdAt; + protected long sessionLength; + protected long timeSpent; + protected long lastInterval; // events - public int eventCount; - public String eventToken; - public float amountInCents; - public Map callbackParameters; // TODO: test! + protected int eventCount; + protected String eventToken; + protected float amountInCents; + protected Map callbackParameters; // TODO: test! // meta TODO: remove - public String path; - public String userAgent; - public String kind; - public String suffix; + protected String path; + protected String userAgent; + protected String kind; + protected String suffix; - public TrackingPackage buildSessionPackage() { + protected TrackingPackage buildSessionPackage() { Map parameters = getDefaultParameters(); // session specific @@ -56,7 +56,7 @@ public TrackingPackage buildSessionPackage() { return sessionPackage; } - public TrackingPackage buildEventPackage() { + protected TrackingPackage buildEventPackage() { Map parameters = getDefaultParameters(); // event specific @@ -81,7 +81,7 @@ public TrackingPackage buildEventPackage() { return eventPackage; } - public TrackingPackage buildRevenuePackage() { + protected TrackingPackage buildRevenuePackage() { Map parameters = getDefaultParameters(); // event specific diff --git a/AdjustIo/src/com/adeven/adjustio/QueueThread.java b/AdjustIo/src/com/adeven/adjustio/QueueThread.java index fc41753bc..16eae5fa1 100644 --- a/AdjustIo/src/com/adeven/adjustio/QueueThread.java +++ b/AdjustIo/src/com/adeven/adjustio/QueueThread.java @@ -41,7 +41,7 @@ public class QueueThread extends HandlerThread { // TODO: on session end: stop current tracking loop // add an attribute that gets read before trackInternal starts - public QueueThread(Context context) { + protected QueueThread(Context context) { super(Logger.LOGTAG, MIN_PRIORITY); setDaemon(true); start(); @@ -57,7 +57,7 @@ public QueueThread(Context context) { } // add a package to the queue, trigger tracking - public void addPackage(TrackingPackage pack) { + protected void addPackage(TrackingPackage pack) { Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_ADD; message.obj = pack; @@ -65,7 +65,7 @@ public void addPackage(TrackingPackage pack) { } // try to track the oldest package - public void trackFirstPackage() { + protected void trackFirstPackage() { Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_TRACK_FIRST; queueHandler.sendMessage(message); @@ -73,31 +73,31 @@ public void trackFirstPackage() { // remove oldest package and try to track the next one // (after success or possibly permanent failure) - public void trackNextPackage() { + protected void trackNextPackage() { Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_TRACK_NEXT; queueHandler.sendMessage(message); } // close the package to retry in the future (after temporary failure) - public void closeFirstPackage() { + protected void closeFirstPackage() { isTracking.set(false); } // interrupt the tracking loop after the current request has finished - public void pauseTracking() { + protected void pauseTracking() { paused = true; } // allow tracking requests again - public void resumeTracking() { + protected void resumeTracking() { paused = false; } private static final class PackageHandler extends Handler { private final WeakReference queueThreadReference; - public PackageHandler(Looper looper, QueueThread queueThread) { + protected PackageHandler(Looper looper, QueueThread queueThread) { super(looper); this.queueThreadReference = new WeakReference(queueThread); } diff --git a/AdjustIo/src/com/adeven/adjustio/RequestThread.java b/AdjustIo/src/com/adeven/adjustio/RequestThread.java index bc23e1765..5a70e4218 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestThread.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestThread.java @@ -33,7 +33,7 @@ public class RequestThread extends HandlerThread { private QueueThread queueThread; private HttpClient httpClient; - public RequestThread(QueueThread queueThread) { + protected RequestThread(QueueThread queueThread) { super(Logger.LOGTAG, MIN_PRIORITY); setDaemon(true); start(); @@ -46,7 +46,7 @@ public RequestThread(QueueThread queueThread) { trackingHandler.sendMessage(message); } - public void trackPackage(TrackingPackage pack) { + protected void trackPackage(TrackingPackage pack) { Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_TRACK; message.obj = pack; @@ -56,7 +56,7 @@ public void trackPackage(TrackingPackage pack) { private static final class RequestHandler extends Handler { private final WeakReference requestThreadReference; - public RequestHandler(Looper looper, RequestThread requestThread) { + protected RequestHandler(Looper looper, RequestThread requestThread) { super(looper); this.requestThreadReference = new WeakReference(requestThread); } @@ -108,30 +108,12 @@ private void trackInternal(TrackingPackage trackingPackage) { catch (IOException e) { closePackage(trackingPackage, "Request failed: " + e.getLocalizedMessage()); } - catch (RuntimeException e) { - trackNextPackage(trackingPackage, "Runtime exception: " + e.getClass() + ": " + e.getLocalizedMessage()); + catch (Exception e) { + trackNextPackage(trackingPackage, e.getClass().toString()); } } - private void closePackage(TrackingPackage trackingPackage, String message) { - String failureMessage = trackingPackage.getFailureMessage(); - Logger.error(failureMessage + " Will retry later. (" + message + ")"); - queueThread.closeFirstPackage(); - } - - private void trackNextPackage(TrackingPackage trackingPackage, String message) { - String failureMessage = trackingPackage.getFailureMessage(); - Logger.error(failureMessage + " (No parameters found)"); - queueThread.trackNextPackage(); - } - private void requestFinished(HttpResponse response, TrackingPackage trackingPackage) { - if (response == null) { // TODO: test - Logger.debug(trackingPackage.getFailureMessage() + " (Missing response)"); // TODO: "will retry later" like on ios - queueThread.closeFirstPackage(); - return; - } - int statusCode = response.getStatusLine().getStatusCode(); String responseString = parseResponse(response); @@ -158,6 +140,18 @@ private String parseResponse(HttpResponse response) { } } + private void closePackage(TrackingPackage trackingPackage, String message) { + String failureMessage = trackingPackage.getFailureMessage(); + Logger.error(failureMessage + " Will retry later. (" + message + ")"); + queueThread.closeFirstPackage(); + } + + private void trackNextPackage(TrackingPackage trackingPackage, String message) { + String failureMessage = trackingPackage.getFailureMessage(); + Logger.error(failureMessage + " (" + message + ")"); + queueThread.trackNextPackage(); + } + private void setUserAgent(String userAgent) { HttpParams httpParams = httpClient.getParams(); httpParams.setParameter(CoreProtocolPNames.USER_AGENT, userAgent); diff --git a/AdjustIo/src/com/adeven/adjustio/SessionState.java b/AdjustIo/src/com/adeven/adjustio/SessionState.java index 92cdd1be5..08854196f 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionState.java +++ b/AdjustIo/src/com/adeven/adjustio/SessionState.java @@ -9,18 +9,18 @@ public class SessionState implements Serializable { // TODO: make attributes private? // global counters - public int eventCount; - public int sessionCount; + protected int eventCount; + protected int sessionCount; // session attributes - public int subsessionCount; - public long sessionLength; // all durations in milliseconds - public long timeSpent; - public long createdAt; // all times in milliseconds since 1970 - public long lastActivity; - public long lastInterval; + protected int subsessionCount; + protected long sessionLength; // all durations in milliseconds + protected long timeSpent; + protected long createdAt; // all times in milliseconds since 1970 + protected long lastActivity; + protected long lastInterval; - public SessionState() { + protected SessionState() { eventCount = 0; // no events yet sessionCount = 0; // the first session just started subsessionCount = -1; // we don't know how many subssessions this first session will have @@ -31,7 +31,7 @@ public SessionState() { lastInterval = -1; } - public void startNextSession(long now) { + protected void startNextSession(long now) { sessionCount++; // the next session just started subsessionCount = 1; // first subsession sessionLength = 0; // no session length yet @@ -41,7 +41,7 @@ public void startNextSession(long now) { lastInterval = 0; } - public void injectSessionAttributes(PackageBuilder builder) { + protected void injectSessionAttributes(PackageBuilder builder) { builder.sessionCount = sessionCount; builder.subsessionCount = subsessionCount; builder.sessionLength = sessionLength; @@ -51,7 +51,7 @@ public void injectSessionAttributes(PackageBuilder builder) { builder.lastInterval = lastInterval; } - public void injectEventAttributes(PackageBuilder builder) { + protected void injectEventAttributes(PackageBuilder builder) { builder.sessionCount = sessionCount; builder.subsessionCount = subsessionCount; builder.sessionLength = sessionLength; diff --git a/AdjustIo/src/com/adeven/adjustio/SessionThread.java b/AdjustIo/src/com/adeven/adjustio/SessionThread.java index 848eae6eb..872b65aad 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionThread.java +++ b/AdjustIo/src/com/adeven/adjustio/SessionThread.java @@ -50,7 +50,7 @@ public class SessionThread extends HandlerThread { private String androidId; // everything else here could be persisted private String attributionId; - public SessionThread(String appToken, Context context) { + protected SessionThread(String appToken, Context context) { super(Logger.LOGTAG, MIN_PRIORITY); setDaemon(true); start(); @@ -64,19 +64,19 @@ public SessionThread(String appToken, Context context) { sessionHandler.sendMessage(message); } - public void trackSubsessionStart() { + protected void trackSubsessionStart() { Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_START; sessionHandler.sendMessage(message); } - public void trackSubsessionEnd() { + protected void trackSubsessionEnd() { Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_END; sessionHandler.sendMessage(message); } - public void trackEvent(String eventToken, Map parameters) { + protected void trackEvent(String eventToken, Map parameters) { PackageBuilder builder = new PackageBuilder(); builder.eventToken = eventToken; builder.callbackParameters = parameters; @@ -87,7 +87,7 @@ public void trackEvent(String eventToken, Map parameters) { sessionHandler.sendMessage(message); } - public void trackRevenue(float amountInCents, String eventToken, Map parameters) { + protected void trackRevenue(float amountInCents, String eventToken, Map parameters) { PackageBuilder builder = new PackageBuilder(); builder.amountInCents = amountInCents; builder.eventToken = eventToken; @@ -102,7 +102,7 @@ public void trackRevenue(float amountInCents, String eventToken, Map sessionThreadReference; - public SessionHandler(Looper looper, SessionThread sessionThread) { + protected SessionHandler(Looper looper, SessionThread sessionThread) { super(looper); this.sessionThreadReference = new WeakReference(sessionThread); } @@ -144,7 +144,7 @@ private void initInternal() { String macAddress = Util.getMacAddress(context); macSha1 = Util.sha1(macAddress); - macShort = macAddress.replaceAll(":", ""); + macShort = macAddress.replaceAll(":", ""); // TODO: macMd5!!! userAgent = Util.getUserAgent(context); androidId = Util.getAndroidId(context); attributionId = Util.getAttributionId(context); diff --git a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java index f68be43b1..364da59c0 100644 --- a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java @@ -21,19 +21,19 @@ public class TrackingPackage implements Serializable { private static final long serialVersionUID = -35935556512024097L; // data - public String path; - public String userAgent; - public Map parameters; + protected String path; + protected String userAgent; + protected Map parameters; // logs - public String kind; - public String suffix; + protected String kind; + protected String suffix; public String toString() { return kind + suffix + " " + path; } - public String parameterString() { + protected String parameterString() { try { String parameterString = "Parameters:"; for (Map.Entry entity : parameters.entrySet()) { @@ -46,15 +46,15 @@ public String parameterString() { } } - public String getSuccessMessage() { + protected String getSuccessMessage() { return "Tracked " + kind + suffix; } - public String getFailureMessage() { + protected String getFailureMessage() { return "Failed to track " + kind + suffix; } - public HttpUriRequest getRequest() throws UnsupportedEncodingException { + protected HttpUriRequest getRequest() throws UnsupportedEncodingException { String url = AdjustIo.BASE_URL + path; HttpPost request = new HttpPost(url); @@ -74,4 +74,4 @@ public HttpUriRequest getRequest() throws UnsupportedEncodingException { return request; } -} \ No newline at end of file +} diff --git a/AdjustIo/src/com/adeven/adjustio/Util.java b/AdjustIo/src/com/adeven/adjustio/Util.java index 6848a4e82..872208178 100644 --- a/AdjustIo/src/com/adeven/adjustio/Util.java +++ b/AdjustIo/src/com/adeven/adjustio/Util.java @@ -40,7 +40,7 @@ public class Util { private static final String UNKNOWN = "unknown"; - public static boolean checkPermissions(Context context) { + protected static boolean checkPermissions(Context context) { boolean result = true; if (!checkPermission(context, android.Manifest.permission.INTERNET)) { @@ -267,7 +267,7 @@ private static String sanitizeString(String string, String defaultString) { return result; } - public static String loadAddress(String interfaceName) { + protected static String loadAddress(String interfaceName) { try { String filePath = "/sys/class/net/" + interfaceName + "/address"; StringBuffer fileData = new StringBuffer(1000); @@ -289,11 +289,11 @@ public static String loadAddress(String interfaceName) { } } - public static String getAndroidId(Context context) { + protected static String getAndroidId(Context context) { return Secure.getString(context.getContentResolver(), Secure.ANDROID_ID); } - public static String getAttributionId(Context context) { + protected static String getAttributionId(Context context) { try { ContentResolver contentResolver = context.getContentResolver(); Uri uri = Uri.parse("content://com.facebook.katana.provider.AttributionIdProvider"); @@ -317,7 +317,7 @@ public static String getAttributionId(Context context) { } } - public static String sha1(String text) { + protected static String sha1(String text) { try { MessageDigest mesd = MessageDigest.getInstance("SHA-1"); byte[] bytes = text.getBytes("iso-8859-1"); From 885cf158648871a0afaec15db4dc6da57fc8f7a0 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Fri, 28 Jun 2013 19:04:23 +0200 Subject: [PATCH 14/37] Move checkPermissions to SessionThread --- .../src/com/adeven/adjustio/AdjustIo.java | 6 ++-- AdjustIo/src/com/adeven/adjustio/Logger.java | 10 ------ .../com/adeven/adjustio/RequestThread.java | 33 ++++++++++++------- .../src/com/adeven/adjustio/SessionState.java | 24 +++++++------- .../com/adeven/adjustio/SessionThread.java | 31 +++++++++++++++-- .../com/adeven/adjustio/TrackingPackage.java | 1 - AdjustIo/src/com/adeven/adjustio/Util.java | 26 --------------- 7 files changed, 66 insertions(+), 65 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java index 6a8f52b19..4176e8ceb 100644 --- a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java +++ b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java @@ -23,10 +23,10 @@ * @since 11.10.12 */ public class AdjustIo { - protected static final String BASE_URL = "http://192.168.178.117:8509"; // TODO: change! + protected static final String BASE_URL = "http://192.168.178.117:8509"; // TODO: change url! protected static final String CLIENT_SDK = "android1.6"; - // forwards everything to sessionThread + // every call gets forwarded to sessionThread private static SessionThread sessionThread; // TODO: update all comments @@ -42,7 +42,7 @@ public class AdjustIo { public static void onResume(String appToken, Activity activity) { if (sessionThread == null) { - sessionThread = new SessionThread(appToken, activity.getApplication()); + sessionThread = new SessionThread(appToken, activity); } sessionThread.trackSubsessionStart(); } diff --git a/AdjustIo/src/com/adeven/adjustio/Logger.java b/AdjustIo/src/com/adeven/adjustio/Logger.java index f0135e8d9..fea05dd82 100644 --- a/AdjustIo/src/com/adeven/adjustio/Logger.java +++ b/AdjustIo/src/com/adeven/adjustio/Logger.java @@ -21,10 +21,6 @@ protected static void verbose(String message) { } } - protected static void verbose(String context, String name, String value) { - verbose("[" + context + "] " + name + ": '" + value + "'"); - } - protected static void debug(String message) { if (logLevel <= Log.DEBUG) { Log.d(LOGTAG, message); @@ -48,10 +44,4 @@ protected static void error(String message) { Log.e(LOGTAG, message); } } - - protected static void error(String message, Throwable throwable) { - if (logLevel <= Log.ERROR) { - Log.e(LOGTAG, message, throwable); - } - } } diff --git a/AdjustIo/src/com/adeven/adjustio/RequestThread.java b/AdjustIo/src/com/adeven/adjustio/RequestThread.java index 5a70e4218..9cab30aa4 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestThread.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestThread.java @@ -96,20 +96,19 @@ private void trackInternal(TrackingPackage trackingPackage) { requestFinished(response, trackingPackage); } catch (UnsupportedEncodingException e) { - Logger.error("failed to encode parameters"); - queueThread.trackNextPackage(); + trackNextPackage(trackingPackage, "Failed to encode parameters", e); } catch (ClientProtocolException e) { - closePackage(trackingPackage, "Client protocol error"); + closePackage(trackingPackage, "Client protocol error", e); } catch (SocketTimeoutException e) { - closePackage(trackingPackage, "Request timed out"); + closePackage(trackingPackage, "Request timed out", e); } catch (IOException e) { - closePackage(trackingPackage, "Request failed: " + e.getLocalizedMessage()); + closePackage(trackingPackage, "Request failed", e); } catch (Exception e) { - trackNextPackage(trackingPackage, e.getClass().toString()); + trackNextPackage(trackingPackage, "Runtime exeption", e); } } @@ -135,20 +134,32 @@ private String parseResponse(HttpResponse response) { return responseString; } catch (Exception e) { - Logger.error("error parsing response", e); + Logger.error("Error parsing response: " + e); return "Failed parsing response"; } } - private void closePackage(TrackingPackage trackingPackage, String message) { + private void closePackage(TrackingPackage trackingPackage, String message, Throwable e) { String failureMessage = trackingPackage.getFailureMessage(); - Logger.error(failureMessage + " Will retry later. (" + message + ")"); + String logMessage = failureMessage + " Will retry later. (" + message; + if (e != null) { + logMessage += ": " + e; + } + logMessage += ")"; + Logger.error(logMessage); + queueThread.closeFirstPackage(); } - private void trackNextPackage(TrackingPackage trackingPackage, String message) { + private void trackNextPackage(TrackingPackage trackingPackage, String message, Throwable e) { String failureMessage = trackingPackage.getFailureMessage(); - Logger.error(failureMessage + " (" + message + ")"); + String logMessage = failureMessage + " (" + message; + if (e != null) { + logMessage += ": " + e; + } + logMessage += ")"; + Logger.error(logMessage); + queueThread.trackNextPackage(); } diff --git a/AdjustIo/src/com/adeven/adjustio/SessionState.java b/AdjustIo/src/com/adeven/adjustio/SessionState.java index 08854196f..6b13b1508 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionState.java +++ b/AdjustIo/src/com/adeven/adjustio/SessionState.java @@ -2,6 +2,7 @@ import java.io.Serializable; import java.util.Date; +import java.util.Locale; public class SessionState implements Serializable { private static final long serialVersionUID = 9039439291143138148L; @@ -62,19 +63,18 @@ protected void injectEventAttributes(PackageBuilder builder) { } public String toString() { - return "ec:" + eventCount + - " sc:" + sessionCount + - " ssc:" + subsessionCount + - " sl:" + sessionLength + - " ts:" + timeSpent + - " ca:" + stamp(createdAt) + - " la:" + stamp(lastActivity); + return String.format(Locale.US, + "ec:%d sc:%d ssc:%d sl:%d ts:%d ca:%s la:%s", + eventCount, sessionCount, subsessionCount, sessionLength, + timeSpent, stamp(createdAt), stamp(lastActivity)); } - private static String stamp(long date) { - Date d = new Date(date); - return "" + d.getHours() + - ":" + d.getMinutes() + - ":" + d.getSeconds(); + private static String stamp(long dateMillis) { + Date date = new Date(dateMillis); + return String.format(Locale.US, + "%2d:%2d:%2d", + date.getHours(), + date.getMinutes(), + date.getSeconds()); } } diff --git a/AdjustIo/src/com/adeven/adjustio/SessionThread.java b/AdjustIo/src/com/adeven/adjustio/SessionThread.java index 872b65aad..9eff62c48 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionThread.java +++ b/AdjustIo/src/com/adeven/adjustio/SessionThread.java @@ -18,6 +18,7 @@ import java.util.concurrent.TimeUnit; import android.content.Context; +import android.content.pm.PackageManager; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -345,12 +346,38 @@ private void stopExecutor() { } private boolean checkState() { - if (!Util.checkPermissions(context)) return false; - if (!checkAppToken(appToken)) return false; if (!checkContext(context)) return false; + if (!checkAppToken(appToken)) return false; + if (!checkPermissions(context)) return false; return true; } + private static boolean checkPermissions(Context context) { + boolean result = true; + + if (!checkPermission(context, android.Manifest.permission.INTERNET)) { + Logger.error( + "This SDK requires the INTERNET permission. " + + "See the README for details." + ); + result = false; + } + if (!checkPermission(context, android.Manifest.permission.ACCESS_WIFI_STATE)) { + Logger.warn( + "You can improve your tracking results by adding the " + + "ACCESS_WIFI_STATE permission. See the README for details." + ); + } + + return result; + } + + private static boolean checkPermission(Context context, String permission) { + int result = context.checkCallingOrSelfPermission(permission); + boolean granted = (result == PackageManager.PERMISSION_GRANTED); + return granted; + } + private static boolean checkAppToken(String appToken) { if (appToken == null) { Logger.error("Missing App Token."); diff --git a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java index 364da59c0..38baa6970 100644 --- a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java @@ -16,7 +16,6 @@ import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; -// TODO: change public to protected where possible public class TrackingPackage implements Serializable { private static final long serialVersionUID = -35935556512024097L; diff --git a/AdjustIo/src/com/adeven/adjustio/Util.java b/AdjustIo/src/com/adeven/adjustio/Util.java index 872208178..eb8a54486 100644 --- a/AdjustIo/src/com/adeven/adjustio/Util.java +++ b/AdjustIo/src/com/adeven/adjustio/Util.java @@ -40,32 +40,6 @@ public class Util { private static final String UNKNOWN = "unknown"; - protected static boolean checkPermissions(Context context) { - boolean result = true; - - if (!checkPermission(context, android.Manifest.permission.INTERNET)) { - Logger.error( - "This SDK requires the INTERNET permission. " + - "See the README for details." - ); - result = false; - } - if (!checkPermission(context, android.Manifest.permission.ACCESS_WIFI_STATE)) { - Logger.warn( - "You can improve your tracking results by adding the " + - "ACCESS_WIFI_STATE permission. See the README for details." - ); - } - - return result; - } - - private static boolean checkPermission(Context context, String permission) { - int result = context.checkCallingOrSelfPermission(permission); - boolean granted = (result == PackageManager.PERMISSION_GRANTED); - return granted; - } - protected static String getUserAgent(Context context) { Resources resources = context.getResources(); DisplayMetrics displayMetrics = resources.getDisplayMetrics(); From 91693c03b9f1974b639f442034895092ed6cafe4 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Mon, 1 Jul 2013 11:16:39 +0200 Subject: [PATCH 15/37] Update and close some TODOs --- .../src/com/adeven/adjustio/AdjustIo.java | 6 ++--- AdjustIo/src/com/adeven/adjustio/Logger.java | 3 ++- .../com/adeven/adjustio/PackageBuilder.java | 15 ++++------- .../src/com/adeven/adjustio/QueueThread.java | 8 ++---- .../com/adeven/adjustio/RequestThread.java | 3 ++- .../com/adeven/adjustio/SessionThread.java | 26 +++++++++---------- 6 files changed, 27 insertions(+), 34 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java index 4176e8ceb..4f43b2a00 100644 --- a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java +++ b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java @@ -51,7 +51,7 @@ public static void onPause() { try { sessionThread.trackSubsessionEnd(); } catch (NullPointerException e) { - // TODO: log + Logger.error("No session thread found"); } } @@ -77,7 +77,7 @@ public static void trackEvent(String eventToken, Map parameters) try { sessionThread.trackEvent(eventToken, parameters); } catch (NullPointerException e) { - // TODO: log + Logger.error("No session thread found"); } } @@ -108,7 +108,7 @@ public static void trackRevenue(float amountInCents, String eventToken, Map callbackParameters; // TODO: test! - - // meta TODO: remove - protected String path; - protected String userAgent; - protected String kind; - protected String suffix; + protected Map callbackParameters; protected TrackingPackage buildSessionPackage() { Map parameters = getDefaultParameters(); // session specific - addInt(parameters, "session_id", sessionCount); // TODO: rename? + addInt(parameters, "session_id", sessionCount); // TODO: rename parameters addInt(parameters, "subsession_count", subsessionCount); addDuration(parameters, "session_length", sessionLength); addDuration(parameters, "time_spent", timeSpent); @@ -61,7 +56,7 @@ protected TrackingPackage buildEventPackage() { // event specific addInt(parameters, "event_count", eventCount); - addString(parameters, "event_id", eventToken); // TODO: rename + addString(parameters, "event_id", eventToken); // TODO: rename parameters addMap(parameters, "params", callbackParameters); // session specific (current values at time of event) @@ -159,7 +154,7 @@ private void addDate(Map parameters, String key, long value) { return; } Date date = new Date(value); - String dateString = date.toString(); // TODO: format + String dateString = date.toString(); // TODO: format with DateFormat addString(parameters, key, dateString); } diff --git a/AdjustIo/src/com/adeven/adjustio/QueueThread.java b/AdjustIo/src/com/adeven/adjustio/QueueThread.java index 16eae5fa1..44bf11a3c 100644 --- a/AdjustIo/src/com/adeven/adjustio/QueueThread.java +++ b/AdjustIo/src/com/adeven/adjustio/QueueThread.java @@ -1,4 +1,3 @@ -// TODO: add comments package com.adeven.adjustio; import java.io.BufferedInputStream; @@ -24,7 +23,7 @@ // persistent public class QueueThread extends HandlerThread { - private static final String QUEUE_FILENAME = "testqueue3"; + private static final String QUEUE_FILENAME = "testqueue3"; // TODO: change filename private static final int MESSAGE_ARG_ADD = 72500; // TODO: change constants! private static final int MESSAGE_ARG_TRACK_NEXT = 72510; @@ -38,9 +37,6 @@ public class QueueThread extends HandlerThread { private List packages; private boolean paused; - // TODO: on session end: stop current tracking loop - // add an attribute that gets read before trackInternal starts - protected QueueThread(Context context) { super(Logger.LOGTAG, MIN_PRIORITY); setDaemon(true); @@ -202,7 +198,7 @@ private void readPackagesInternal() { } private void writePackagesInternal() { - try { // TODO: remove! + try { // TODO: remove sleeps Thread.sleep(100); } catch (Exception e) {} diff --git a/AdjustIo/src/com/adeven/adjustio/RequestThread.java b/AdjustIo/src/com/adeven/adjustio/RequestThread.java index 9cab30aa4..830eecabe 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestThread.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestThread.java @@ -79,6 +79,8 @@ public void handleMessage(Message message) { } } + // TODO: use SSLSessionCache? + // http://candrews.integralblue.com/2011/09/best-way-to-use-httpclient-in-android/ private void initInternal() { HttpParams httpParams = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParams, CONNECTION_TIMEOUT); @@ -88,7 +90,6 @@ private void initInternal() { } private void trackInternal(TrackingPackage trackingPackage) { - // TODO: test all paths! try { setUserAgent(trackingPackage.userAgent); HttpUriRequest request = trackingPackage.getRequest(); diff --git a/AdjustIo/src/com/adeven/adjustio/SessionThread.java b/AdjustIo/src/com/adeven/adjustio/SessionThread.java index 9eff62c48..e8c795aaa 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionThread.java +++ b/AdjustIo/src/com/adeven/adjustio/SessionThread.java @@ -25,7 +25,7 @@ import android.os.Message; public class SessionThread extends HandlerThread { - private static final String SESSION_FILENAME = "sessionstate1"; + private static final String SESSION_FILENAME = "sessionstate1"; // TODO: change filename private static final long UPDATE_INTERVAL = 1000 * 10; // 10 second, TODO: one minute private static final long SESSION_INTERVAL = 1000 * 15; // 30 seconds, TODO: 30 minutes @@ -136,7 +136,7 @@ public void handleMessage(Message message) { } } - // TODO: rename internal methods + // TODO: rename internal methods? // called from outside @@ -283,26 +283,26 @@ private void readSessionStateInternal() { try { sessionState = (SessionState) objectStream.readObject(); - Logger.debug("read session state: " + sessionState); + Logger.debug("Read session state: " + sessionState); } catch (ClassNotFoundException e) { - Logger.error("failed to find session state class"); + Logger.error("Failed to find session state class"); } catch (OptionalDataException e) {} catch (IOException e) { - Logger.error("failed to read session states object"); + Logger.error("Failed to read session states object"); } catch (ClassCastException e) { - Logger.error("failed to cast session state object"); + Logger.error("Failed to cast session state object"); } finally { objectStream.close(); } } catch (FileNotFoundException e) { - Logger.verbose("session state file not found"); + Logger.verbose("Session state file not found"); } catch (IOException e) { - Logger.error("failed to read session state file"); + Logger.error("Failed to read session state file"); } } private void writeSessionStateInternal() { - try { + try { // TODO: remove sleeps! Thread.sleep(100); } catch (Exception e) { } @@ -314,15 +314,15 @@ private void writeSessionStateInternal() { try { objectStream.writeObject(sessionState); - Logger.debug("wrote session state: " + sessionState); + Logger.debug("Wrote session state: " + sessionState); } catch (NotSerializableException e) { - Logger.error("failed to serialize session state"); + Logger.error("Failed to serialize session state"); } finally { objectStream.close(); } } catch (IOException e) { - Logger.error("failed to write session state (" + e.getLocalizedMessage() + ")"); // TODO: improve log + Logger.error("Failed to write session state (" + e + ")"); } } @@ -341,7 +341,7 @@ private void stopExecutor() { try { executor.shutdown(); } catch (NullPointerException e) { - // TODO: log? + Logger.error("No executor found"); } } From 8fb6968531ca79934ffbb6d3661cdcc589dd6d80 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Wed, 3 Jul 2013 16:19:41 +0200 Subject: [PATCH 16/37] Refactor, restructure, rename threads to handlers --- ...essionThread.java => ActivityHandler.java} | 355 +++++++++++------- ...ckingPackage.java => ActivityPackage.java} | 2 +- .../src/com/adeven/adjustio/AdjustIo.java | 22 +- AdjustIo/src/com/adeven/adjustio/Logger.java | 1 - .../com/adeven/adjustio/PackageBuilder.java | 100 +++-- .../{QueueThread.java => QueueHandler.java} | 110 +++--- ...RequestThread.java => RequestHandler.java} | 80 ++-- 7 files changed, 359 insertions(+), 311 deletions(-) rename AdjustIo/src/com/adeven/adjustio/{SessionThread.java => ActivityHandler.java} (55%) rename AdjustIo/src/com/adeven/adjustio/{TrackingPackage.java => ActivityPackage.java} (97%) rename AdjustIo/src/com/adeven/adjustio/{QueueThread.java => QueueHandler.java} (63%) rename AdjustIo/src/com/adeven/adjustio/{RequestThread.java => RequestHandler.java} (61%) diff --git a/AdjustIo/src/com/adeven/adjustio/SessionThread.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java similarity index 55% rename from AdjustIo/src/com/adeven/adjustio/SessionThread.java rename to AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index e8c795aaa..bfe5bd5e6 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionThread.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -24,57 +24,57 @@ import android.os.Looper; import android.os.Message; -public class SessionThread extends HandlerThread { - private static final String SESSION_FILENAME = "sessionstate1"; // TODO: change filename +public class ActivityHandler extends HandlerThread { + private static final String SESSION_STATE_FILENAME = "sessionstate1"; // TODO: change filename - private static final long UPDATE_INTERVAL = 1000 * 10; // 10 second, TODO: one minute - private static final long SESSION_INTERVAL = 1000 * 15; // 30 seconds, TODO: 30 minutes - private static final long SUBSESSION_INTERVAL = 1000 * 1; // one second + private static final long TIMER_INTERVAL = 1000 * 10; // 10 second, TODO: one minute + private static final long SESSION_INTERVAL = 1000 * 15; // 15 seconds, TODO: 30 minutes + private static final long SUBSESSION_INTERVAL = 1000 * 1; // one second - private static final int MESSAGE_ARG_INIT = 72630; - private static final int MESSAGE_ARG_START = 72640; - private static final int MESSAGE_ARG_END = 72650; - private static final int MESSAGE_ARG_EVENT = 72660; + private static final int MESSAGE_ARG_INIT = 72630; + private static final int MESSAGE_ARG_START = 72640; + private static final int MESSAGE_ARG_END = 72650; + private static final int MESSAGE_ARG_EVENT = 72660; private static final int MESSAGE_ARG_REVENUE = 72670; - private Handler sessionHandler; + private InternalHandler internalHandler; private SessionState sessionState; - private QueueThread queueThread; - private static ScheduledExecutorService executor; + private QueueHandler queueHandler; + private static ScheduledExecutorService timer; // TODO: rename to timer private Context context; private String appToken; private String macSha1; - private String macShort; - private String userAgent; // changes, should be updated periodically - private String androidId; // everything else here could be persisted - private String attributionId; + private String macShort; // TODO: md5!!! + private String androidId; // everything else here could be persisted + private String fbAttributionId; + private String userAgent; // changes, should be updated periodically - protected SessionThread(String appToken, Context context) { + protected ActivityHandler(String appToken, Context context) { super(Logger.LOGTAG, MIN_PRIORITY); setDaemon(true); start(); - sessionHandler = new SessionHandler(getLooper(), this); + internalHandler = new InternalHandler(getLooper(), this); - this.appToken = appToken; this.context = context; Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_INIT; - sessionHandler.sendMessage(message); + message.obj = appToken; + internalHandler.sendMessage(message); } protected void trackSubsessionStart() { Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_START; - sessionHandler.sendMessage(message); + internalHandler.sendMessage(message); } protected void trackSubsessionEnd() { Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_END; - sessionHandler.sendMessage(message); + internalHandler.sendMessage(message); } protected void trackEvent(String eventToken, Map parameters) { @@ -85,7 +85,7 @@ protected void trackEvent(String eventToken, Map parameters) { Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_EVENT; message.obj = builder; - sessionHandler.sendMessage(message); + internalHandler.sendMessage(message); } protected void trackRevenue(float amountInCents, String eventToken, Map parameters) { @@ -97,259 +97,298 @@ protected void trackRevenue(float amountInCents, String eventToken, Map sessionThreadReference; + private static final class InternalHandler extends Handler { + private final WeakReference sessionHandlerReference; - protected SessionHandler(Looper looper, SessionThread sessionThread) { + protected InternalHandler(Looper looper, ActivityHandler sessionHandler) { super(looper); - this.sessionThreadReference = new WeakReference(sessionThread); + this.sessionHandlerReference = new WeakReference(sessionHandler); } public void handleMessage(Message message) { super.handleMessage(message); - SessionThread sessionThread = sessionThreadReference.get(); - if (sessionThread == null) return; + ActivityHandler sessionHandler = sessionHandlerReference.get(); + if (sessionHandler == null) return; switch (message.arg1) { case MESSAGE_ARG_INIT: - sessionThread.initInternal(); + String appToken = (String) message.obj; + sessionHandler.initInternal(appToken); break; case MESSAGE_ARG_START: - sessionThread.startInternal(); + sessionHandler.startInternal(); break; case MESSAGE_ARG_END: - sessionThread.endInternal(); + sessionHandler.endInternal(); break; case MESSAGE_ARG_EVENT: PackageBuilder eventBuilder = (PackageBuilder) message.obj; - sessionThread.eventInternal(eventBuilder); + sessionHandler.eventInternal(eventBuilder); break; case MESSAGE_ARG_REVENUE: PackageBuilder revenueBuilder = (PackageBuilder) message.obj; - sessionThread.revenueInternal(revenueBuilder); + sessionHandler.revenueInternal(revenueBuilder); break; } } } // TODO: rename internal methods? + // TODO: remove internal for all methods without external part + // TODO: move internal methods up to the public interface? + // like foo(), fooInternal(), bar(), barInternal(), etc. // called from outside - private void initInternal() { - if (!checkState()) return; + private void initInternal(String token) { + if (!checkAppTokenNotNull(token)) return; + if (!checkAppTokenLength(token)) return; + if (!checkContext(context)) return; + if (!checkPermissions(context)) return; String macAddress = Util.getMacAddress(context); + + appToken = token; macSha1 = Util.sha1(macAddress); - macShort = macAddress.replaceAll(":", ""); // TODO: macMd5!!! + macShort = macAddress.replaceAll(":", ""); // TODO: macMd5!!! userAgent = Util.getUserAgent(context); androidId = Util.getAndroidId(context); - attributionId = Util.getAttributionId(context); + fbAttributionId = Util.getAttributionId(context); - queueThread = new QueueThread(context); - readSessionStateInternal(); + queueHandler = new QueueHandler(context); + readSessionState(); } private void startInternal() { - if (!checkState()) return; - queueThread.resumeTracking(); - startExecutor(); + if (!checkAppTokenNotNull(appToken)) return; + + queueHandler.resumeSending(); + startTimer(); long now = new Date().getTime(); // very first session if (sessionState == null) { - Logger.info("first session"); + Logger.info("First session"); sessionState = new SessionState(); sessionState.sessionCount = 1; // this is the first session - sessionState.createdAt = now; // starting now (that's all we know) + sessionState.createdAt = now; // starting now - enqueueSessionInternal(); - writeSessionStateInternal(); + enqueueSessionPackage(); + writeSessionState(); return; } long lastInterval = now - sessionState.lastActivity; if (lastInterval < 0) { - Logger.error("time travel"); + Logger.error("Time travel!"); sessionState.lastActivity = now; + writeSessionState(); return; } // new session if (lastInterval > SESSION_INTERVAL) { sessionState.lastInterval = lastInterval; - enqueueSessionInternal(); + enqueueSessionPackage(); sessionState.startNextSession(now); - writeSessionStateInternal(); + writeSessionState(); return; } - // new subsession start + // new subsession if (lastInterval > SUBSESSION_INTERVAL) { sessionState.subsessionCount++; } sessionState.sessionLength += lastInterval; sessionState.lastActivity = now; - - writeSessionStateInternal(); + writeSessionState(); } private void endInternal() { - if (!checkState()) return; - queueThread.pauseTracking(); - stopExecutor(); - updateInternal(); + if (!checkAppTokenNotNull(appToken)) return; + + queueHandler.pauseSending(); + stopTimer(); + updateSessionState(); + writeSessionState(); } private void eventInternal(PackageBuilder eventBuilder) { - if (!checkState()) return; + Logger.error("event start"); + if (!checkAppTokenNotNull(appToken)) return; + if (!checkSessionState(sessionState)) return; if (!checkEventTokenNotNull(eventBuilder.eventToken)) return; if (!checkEventTokenLength(eventBuilder.eventToken)) return; sessionState.eventCount++; - updateInternal(); + updateSessionState(); injectGeneralAttributes(eventBuilder); sessionState.injectEventAttributes(eventBuilder); - TrackingPackage eventPackage = eventBuilder.buildEventPackage(); - queueThread.addPackage(eventPackage); + ActivityPackage eventPackage = eventBuilder.buildEventPackage(); + queueHandler.addPackage(eventPackage); + + writeSessionState(); + try { Thread.sleep(500); } catch(Exception e) {} + Logger.error("event end"); } private void revenueInternal(PackageBuilder revenueBuilder) { - if (!checkState()) return; + Logger.error("revenue start"); + if (!checkAppTokenNotNull(appToken)) return; + if (!checkSessionState(sessionState)) return; + if (!checkAmount(revenueBuilder.amountInCents)) return; if (!checkEventTokenLength(revenueBuilder.eventToken)) return; sessionState.eventCount++; - updateInternal(); + updateSessionState(); injectGeneralAttributes(revenueBuilder); sessionState.injectEventAttributes(revenueBuilder); - TrackingPackage revenuePackage = revenueBuilder.buildRevenuePackage(); - queueThread.addPackage(revenuePackage); - } - - // called from inside - - private void updateInternal() { - if (sessionState == null) return; - - long now = new Date().getTime(); - long lastInterval = now - sessionState.lastActivity; - if (lastInterval < 0) { - Logger.error("time travel"); - sessionState.lastActivity = now; - return; - } - - if (lastInterval > SESSION_INTERVAL) return; + ActivityPackage revenuePackage = revenueBuilder.buildRevenuePackage(); + queueHandler.addPackage(revenuePackage); - sessionState.sessionLength += lastInterval; - sessionState.timeSpent += lastInterval; - sessionState.lastActivity = now; - - writeSessionStateInternal(); - } - - private void enqueueSessionInternal() { - PackageBuilder builder = new PackageBuilder(); - injectGeneralAttributes(builder); - sessionState.injectSessionAttributes(builder); - TrackingPackage sessionPackage = builder.buildSessionPackage(); - queueThread.addPackage(sessionPackage); + writeSessionState(); + try { Thread.sleep(500); } catch(Exception e) {} + Logger.error("revenue end"); } - private void injectGeneralAttributes(PackageBuilder builder) { - builder.userAgent = userAgent; - builder.appToken = appToken; - builder.macShort = macShort; - builder.macSha1 = macSha1; - builder.androidId = androidId; - builder.attributionId = attributionId; - } + // called from inside - private void readSessionStateInternal() { + private void readSessionState() { // if any exception gets raised we start with a fresh sessionState sessionState = null; try { - FileInputStream inputStream = context.openFileInput(SESSION_FILENAME); + FileInputStream inputStream = context.openFileInput(SESSION_STATE_FILENAME); BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); try { sessionState = (SessionState) objectStream.readObject(); Logger.debug("Read session state: " + sessionState); - } catch (ClassNotFoundException e) { + } + catch (ClassNotFoundException e) { Logger.error("Failed to find session state class"); - } catch (OptionalDataException e) {} catch (IOException e) { + } + catch (OptionalDataException e) {} + catch (IOException e) { Logger.error("Failed to read session states object"); - } catch (ClassCastException e) { + } + catch (ClassCastException e) { Logger.error("Failed to cast session state object"); - } finally { + } + finally { objectStream.close(); } - } catch (FileNotFoundException e) { + } + catch (FileNotFoundException e) { Logger.verbose("Session state file not found"); - } catch (IOException e) { + } + catch (IOException e) { Logger.error("Failed to read session state file"); } } - private void writeSessionStateInternal() { + // TODO: move to sessionState? + private void writeSessionState() { try { // TODO: remove sleeps! Thread.sleep(100); - } catch (Exception e) { } + catch (Exception e) {} try { - FileOutputStream outputStream = context.openFileOutput(SESSION_FILENAME, Context.MODE_PRIVATE); + FileOutputStream outputStream = context.openFileOutput(SESSION_STATE_FILENAME, Context.MODE_PRIVATE); BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream); try { objectStream.writeObject(sessionState); Logger.debug("Wrote session state: " + sessionState); - } catch (NotSerializableException e) { + } + catch (NotSerializableException e) { Logger.error("Failed to serialize session state"); - } finally { + } + finally { objectStream.close(); } - } catch (IOException e) { + } + catch (IOException e) { Logger.error("Failed to write session state (" + e + ")"); } } - private void startExecutor() { - stopExecutor(); - executor = Executors.newSingleThreadScheduledExecutor(); - executor.scheduleWithFixedDelay(new Runnable() { + private void enqueueSessionPackage() { + PackageBuilder builder = new PackageBuilder(); + injectGeneralAttributes(builder); + sessionState.injectSessionAttributes(builder); + ActivityPackage sessionPackage = builder.buildSessionPackage(); + queueHandler.addPackage(sessionPackage); + } + + // called from inside + + private void updateSessionState() { + if (!checkSessionState(sessionState)) return; + + long now = new Date().getTime(); + long lastInterval = now - sessionState.lastActivity; + if (lastInterval < 0) { + Logger.error("Time travel"); + sessionState.lastActivity = now; + return; + } + + // ignore late updates + if (lastInterval > SESSION_INTERVAL) return; + + sessionState.sessionLength += lastInterval; + sessionState.timeSpent += lastInterval; + sessionState.lastActivity = now; + } + + private void injectGeneralAttributes(PackageBuilder builder) { + builder.userAgent = userAgent; + builder.appToken = appToken; + builder.macShort = macShort; + builder.macSha1 = macSha1; + builder.androidId = androidId; + builder.attributionId = fbAttributionId; + } + + private void startTimer() { + if (timer != null) { + stopTimer(); + } + timer = Executors.newSingleThreadScheduledExecutor(); + timer.scheduleWithFixedDelay(new Runnable() { public void run() { - updateInternal(); - queueThread.trackFirstPackage(); + timerFired(); } - }, 1000, UPDATE_INTERVAL, TimeUnit.MILLISECONDS); + }, 1000, TIMER_INTERVAL, TimeUnit.MILLISECONDS); } - private void stopExecutor() { + private void stopTimer() { try { - executor.shutdown(); - } catch (NullPointerException e) { - Logger.error("No executor found"); + timer.shutdown(); + } + catch (NullPointerException e) { + Logger.error("No timer found"); } } - private boolean checkState() { - if (!checkContext(context)) return false; - if (!checkAppToken(appToken)) return false; - if (!checkPermissions(context)) return false; - return true; + private void timerFired() { + queueHandler.sendFirstPackage(); + + updateSessionState(); + writeSessionState(); } private static boolean checkPermissions(Context context) { @@ -357,32 +396,53 @@ private static boolean checkPermissions(Context context) { if (!checkPermission(context, android.Manifest.permission.INTERNET)) { Logger.error( - "This SDK requires the INTERNET permission. " + - "See the README for details." - ); + "This SDK requires the INTERNET permission. " + + "See the README for details." + ); result = false; } if (!checkPermission(context, android.Manifest.permission.ACCESS_WIFI_STATE)) { Logger.warn( - "You can improve your tracking results by adding the " + - "ACCESS_WIFI_STATE permission. See the README for details." - ); + "You can improve your tracking results by adding the " + + "ACCESS_WIFI_STATE permission. See the README for details." + ); } return result; } + private static boolean checkContext(Context context) { + if (context == null) { + Logger.error("Missing context."); + return false; + } + return true; + } + private static boolean checkPermission(Context context, String permission) { int result = context.checkCallingOrSelfPermission(permission); boolean granted = (result == PackageManager.PERMISSION_GRANTED); return granted; } - private static boolean checkAppToken(String appToken) { + private static boolean checkSessionState(SessionState sessionState) { + if (sessionState == null) { + Logger.error("Missing session state."); + return false; + } + return true; + } + + private static boolean checkAppTokenNotNull(String appToken) { if (appToken == null) { Logger.error("Missing App Token."); return false; - } else if (appToken.length() != 12) { + } + return true; + } + + private static boolean checkAppTokenLength(String appToken) { + if (appToken.length() != 12) { Logger.error("Malformed App Token '" + appToken + "'"); return false; } @@ -402,17 +462,22 @@ private static boolean checkEventTokenLength(String eventToken) { return true; if (eventToken.length() != 6) { - Logger.error("Malformed Event Token"); + Logger.error("Malformed Event Token '" + eventToken + "'"); return false; } return true; } - private static boolean checkContext(Context context) { - if (context == null) { - Logger.error("Missing context."); + private static boolean checkAmount(float amount) { + if (amount <= 0.0f) { + Logger.error("Invalid amount " + amount); return false; } return true; } } + + + + +// TODO: remove trailing lines diff --git a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java b/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java similarity index 97% rename from AdjustIo/src/com/adeven/adjustio/TrackingPackage.java rename to AdjustIo/src/com/adeven/adjustio/ActivityPackage.java index 38baa6970..0875f2480 100644 --- a/AdjustIo/src/com/adeven/adjustio/TrackingPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java @@ -16,7 +16,7 @@ import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; -public class TrackingPackage implements Serializable { +public class ActivityPackage implements Serializable { private static final long serialVersionUID = -35935556512024097L; // data diff --git a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java index 4f43b2a00..9407c7827 100644 --- a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java +++ b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java @@ -26,8 +26,8 @@ public class AdjustIo { protected static final String BASE_URL = "http://192.168.178.117:8509"; // TODO: change url! protected static final String CLIENT_SDK = "android1.6"; - // every call gets forwarded to sessionThread - private static SessionThread sessionThread; + // every call gets forwarded to activityHandler + private static ActivityHandler activityHandler; // TODO: update all comments /** @@ -41,17 +41,17 @@ public class AdjustIo { */ public static void onResume(String appToken, Activity activity) { - if (sessionThread == null) { - sessionThread = new SessionThread(appToken, activity); + if (activityHandler == null) { + activityHandler = new ActivityHandler(appToken, activity); } - sessionThread.trackSubsessionStart(); + activityHandler.trackSubsessionStart(); } public static void onPause() { try { - sessionThread.trackSubsessionEnd(); + activityHandler.trackSubsessionEnd(); } catch (NullPointerException e) { - Logger.error("No session thread found"); + Logger.error("No activity handler found"); } } @@ -75,9 +75,9 @@ public static void trackEvent(String eventToken) { public static void trackEvent(String eventToken, Map parameters) { try { - sessionThread.trackEvent(eventToken, parameters); + activityHandler.trackEvent(eventToken, parameters); } catch (NullPointerException e) { - Logger.error("No session thread found"); + Logger.error("No activity handler found"); } } @@ -106,9 +106,9 @@ public static void trackRevenue(float amountInCents, String eventToken) { public static void trackRevenue(float amountInCents, String eventToken, Map parameters) { try { - sessionThread.trackRevenue(amountInCents, eventToken, parameters); + activityHandler.trackRevenue(amountInCents, eventToken, parameters); } catch (NullPointerException e) { - Logger.error("No session thread found"); + Logger.error("No activity handler found"); } } diff --git a/AdjustIo/src/com/adeven/adjustio/Logger.java b/AdjustIo/src/com/adeven/adjustio/Logger.java index a0d7c6907..185515a4d 100644 --- a/AdjustIo/src/com/adeven/adjustio/Logger.java +++ b/AdjustIo/src/com/adeven/adjustio/Logger.java @@ -1,4 +1,3 @@ - package com.adeven.adjustio; import android.util.Log; diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java index 7a6a3cdc9..24f5e221b 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -2,6 +2,7 @@ import java.util.Date; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import org.json.JSONObject; @@ -31,17 +32,10 @@ public class PackageBuilder { protected float amountInCents; protected Map callbackParameters; - protected TrackingPackage buildSessionPackage() { + protected ActivityPackage buildSessionPackage() { Map parameters = getDefaultParameters(); - // session specific - addInt(parameters, "session_id", sessionCount); // TODO: rename parameters - addInt(parameters, "subsession_count", subsessionCount); - addDuration(parameters, "session_length", sessionLength); - addDuration(parameters, "time_spent", timeSpent); - addDuration(parameters, "last_interval", lastInterval); - - TrackingPackage sessionPackage = new TrackingPackage(); + ActivityPackage sessionPackage = new ActivityPackage(); sessionPackage.path = "/startup"; sessionPackage.kind = "session start"; sessionPackage.suffix = "."; @@ -51,49 +45,26 @@ protected TrackingPackage buildSessionPackage() { return sessionPackage; } - protected TrackingPackage buildEventPackage() { + protected ActivityPackage buildEventPackage() { Map parameters = getDefaultParameters(); + injectEventParameters(parameters); - // event specific - addInt(parameters, "event_count", eventCount); - addString(parameters, "event_id", eventToken); // TODO: rename parameters - addMap(parameters, "params", callbackParameters); - - // session specific (current values at time of event) - addInt(parameters, "session_count", sessionCount); - addInt(parameters, "subsession_count", subsessionCount); - addDuration(parameters, "session_length", sessionLength); - addDuration(parameters, "time_spent", timeSpent); - addDuration(parameters, "last_interval", lastInterval); - - TrackingPackage eventPackage = new TrackingPackage(); + ActivityPackage eventPackage = new ActivityPackage(); eventPackage.path = "/event"; eventPackage.kind = "event"; - eventPackage.suffix = " '" + eventToken + "'."; + eventPackage.suffix = getEventSuffix(); eventPackage.parameters = parameters; eventPackage.userAgent = userAgent; return eventPackage; } - protected TrackingPackage buildRevenuePackage() { + protected ActivityPackage buildRevenuePackage() { Map parameters = getDefaultParameters(); - - // event specific - addInt(parameters, "event_count", eventCount); - addString(parameters, "event_token", eventToken); + injectEventParameters(parameters); addString(parameters, "amount", getAmountString()); - addMap(parameters, "params", callbackParameters); - // session specific (current values at time of event) - addInt(parameters, "event_count", eventCount); - addInt(parameters, "session_count", sessionCount); - addInt(parameters, "subsession_count", subsessionCount); - addDuration(parameters, "session_length", sessionLength); - addDuration(parameters, "time_spent", timeSpent); - addDuration(parameters, "last_interval", lastInterval); - - TrackingPackage revenuePackage = new TrackingPackage(); + ActivityPackage revenuePackage = new ActivityPackage(); revenuePackage.path = "/revenue"; revenuePackage.kind = "revenue"; revenuePackage.suffix = getRevenueSuffix(); @@ -106,6 +77,7 @@ protected TrackingPackage buildRevenuePackage() { private Map getDefaultParameters() { Map parameters = new HashMap(); + // general addDate(parameters, "created_at", createdAt); addString(parameters, "app_token", appToken); addString(parameters, "mac_sha1", macSha1); @@ -113,9 +85,22 @@ private Map getDefaultParameters() { addString(parameters, "android_id", androidId); addString(parameters, "fb_id", attributionId); + // session related (used for events as well) + addInt(parameters, "session_id", sessionCount); // TODO: rename parameters + addInt(parameters, "subsession_count", subsessionCount); + addDuration(parameters, "session_length", sessionLength); + addDuration(parameters, "time_spent", timeSpent); + addDuration(parameters, "last_interval", lastInterval); + return parameters; } + private void injectEventParameters(Map parameters) { + addInt(parameters, "event_count", eventCount); + addString(parameters, "event_id", eventToken); // TODO: rename parameters + addMap(parameters, "params", callbackParameters); + } + private String getAmountString() { int amountInMillis = Math.round(10 * amountInCents); amountInCents = amountInMillis / 10.0f; // now rounded to one decimal point @@ -123,50 +108,49 @@ private String getAmountString() { return amountString; } - // examples: " (12.5 cent)." - // " (12.5 cent, 'abc123')." + private String getEventSuffix() { + return String.format(" '%s'", eventToken); + } + private String getRevenueSuffix() { - String suffix = " (" + amountInCents + " cent"; if (eventToken != null) { - suffix += ", '" + eventToken + "'"; + return String.format(Locale.US, " (%.1f cent, '%s')", amountInCents, eventToken); + } else { + return String.format(Locale.US, " (%.1f cent)", amountInCents); } - suffix += ")."; - return suffix; } private void addString(Map parameters, String key, String value) { - if (value == null || value == "") { - return; - } + if (value == null || value == "") return; + parameters.put(key, value); } private void addInt(Map parameters, String key, long value) { - if (value == -1) { - return; - } + if (value < 0) return; + String valueString = Long.toString(value); addString(parameters, key, valueString); } private void addDate(Map parameters, String key, long value) { - if (value == -1) { - return; - } + if (value < 0) return; + Date date = new Date(value); String dateString = date.toString(); // TODO: format with DateFormat addString(parameters, key, dateString); } private void addDuration(Map parameters, String key, long durationInMilliSeconds) { - long durationInSeconds = durationInMilliSeconds / 1000; + if (durationInMilliSeconds < 0) return; + + // TODO: test rounding + long durationInSeconds = (durationInMilliSeconds + 500) / 1000; addInt(parameters, key, durationInSeconds); } private void addMap(Map parameters, String key, Map map) { - if (map == null) { - return; - } + if (map == null) return; JSONObject jsonObject = new JSONObject(map); byte[] jsonBytes = jsonObject.toString().getBytes(); diff --git a/AdjustIo/src/com/adeven/adjustio/QueueThread.java b/AdjustIo/src/com/adeven/adjustio/QueueHandler.java similarity index 63% rename from AdjustIo/src/com/adeven/adjustio/QueueThread.java rename to AdjustIo/src/com/adeven/adjustio/QueueHandler.java index 44bf11a3c..00e741857 100644 --- a/AdjustIo/src/com/adeven/adjustio/QueueThread.java +++ b/AdjustIo/src/com/adeven/adjustio/QueueHandler.java @@ -22,101 +22,101 @@ import android.os.Message; // persistent -public class QueueThread extends HandlerThread { +public class QueueHandler extends HandlerThread { private static final String QUEUE_FILENAME = "testqueue3"; // TODO: change filename private static final int MESSAGE_ARG_ADD = 72500; // TODO: change constants! - private static final int MESSAGE_ARG_TRACK_NEXT = 72510; - private static final int MESSAGE_ARG_TRACK_FIRST = 72530; + private static final int MESSAGE_ARG_SEND_NEXT = 72510; + private static final int MESSAGE_ARG_SEND_FIRST = 72530; private static final int MESSAGE_ARG_READ = 72520; - private Handler queueHandler; - private RequestThread requestThread; + private InternalHandler internalHandler; + private RequestHandler requestHandler; private Context context; - private AtomicBoolean isTracking; - private List packages; + private AtomicBoolean isSending; + private List packages; private boolean paused; - protected QueueThread(Context context) { + protected QueueHandler(Context context) { super(Logger.LOGTAG, MIN_PRIORITY); setDaemon(true); start(); - this.queueHandler = new PackageHandler(getLooper(), this); + this.internalHandler = new InternalHandler(getLooper(), this); this.context = context; - this.isTracking = new AtomicBoolean(); - this.requestThread = new RequestThread(this); + this.isSending = new AtomicBoolean(); + this.requestHandler = new RequestHandler(this); Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_READ; - queueHandler.sendMessage(message); + internalHandler.sendMessage(message); } - // add a package to the queue, trigger tracking - protected void addPackage(TrackingPackage pack) { + // add a package to the queue, trigger sending + protected void addPackage(ActivityPackage pack) { Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_ADD; message.obj = pack; - queueHandler.sendMessage(message); + internalHandler.sendMessage(message); } - // try to track the oldest package - protected void trackFirstPackage() { + // try to send the oldest package + protected void sendFirstPackage() { Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_TRACK_FIRST; - queueHandler.sendMessage(message); + message.arg1 = MESSAGE_ARG_SEND_FIRST; + internalHandler.sendMessage(message); } - // remove oldest package and try to track the next one + // remove oldest package and try to send the next one // (after success or possibly permanent failure) - protected void trackNextPackage() { + protected void sendNextPackage() { Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_TRACK_NEXT; - queueHandler.sendMessage(message); + message.arg1 = MESSAGE_ARG_SEND_NEXT; + internalHandler.sendMessage(message); } // close the package to retry in the future (after temporary failure) protected void closeFirstPackage() { - isTracking.set(false); + isSending.set(false); } - // interrupt the tracking loop after the current request has finished - protected void pauseTracking() { + // interrupt the sending loop after the current request has finished + protected void pauseSending() { paused = true; } - // allow tracking requests again - protected void resumeTracking() { + // allow sending requests again + protected void resumeSending() { paused = false; } - private static final class PackageHandler extends Handler { - private final WeakReference queueThreadReference; + private static final class InternalHandler extends Handler { + private final WeakReference queueHandlerReference; - protected PackageHandler(Looper looper, QueueThread queueThread) { + protected InternalHandler(Looper looper, QueueHandler queueHandler) { super(looper); - this.queueThreadReference = new WeakReference(queueThread); + this.queueHandlerReference = new WeakReference(queueHandler); } public void handleMessage(Message message) { super.handleMessage(message); - QueueThread queueThread = queueThreadReference.get(); - if (queueThread == null) return; + QueueHandler queueHandler = queueHandlerReference.get(); + if (queueHandler == null) return; switch (message.arg1) { case MESSAGE_ARG_ADD: - TrackingPackage trackingPackage = (TrackingPackage) message.obj; - queueThread.addInternal(trackingPackage); + ActivityPackage activityPackage = (ActivityPackage) message.obj; + queueHandler.addInternal(activityPackage); break; - case MESSAGE_ARG_TRACK_FIRST: - queueThread.trackFirstInternal(); + case MESSAGE_ARG_SEND_FIRST: + queueHandler.sendFirstInternal(); break; - case MESSAGE_ARG_TRACK_NEXT: - queueThread.trackNextInternal(); + case MESSAGE_ARG_SEND_NEXT: + queueHandler.sendNextInternal(); break; case MESSAGE_ARG_READ: - queueThread.readPackagesInternal(); + queueHandler.readPackagesInternal(); break; } } @@ -124,45 +124,45 @@ public void handleMessage(Message message) { // internal methods run in dedicated queue thread - private void addInternal(TrackingPackage newPackage) { + private void addInternal(ActivityPackage newPackage) { packages.add(newPackage); Logger.debug("added package " + packages.size() + " (" + newPackage + ")"); Logger.verbose(newPackage.parameterString()); writePackagesInternal(); - trackFirstInternal(); + sendFirstInternal(); } - private void trackFirstInternal() { + private void sendFirstInternal() { if (paused) { Logger.debug("paused"); return; } - if (isTracking.getAndSet(true)) { + if (isSending.getAndSet(true)) { Logger.debug("locked"); return; } try { - TrackingPackage firstPackage = packages.get(0); - requestThread.trackPackage(firstPackage); + ActivityPackage firstPackage = packages.get(0); + requestHandler.sendPackage(firstPackage); } catch (IndexOutOfBoundsException e) { - isTracking.set(false); + isSending.set(false); } } - private void trackNextInternal() { + private void sendNextInternal() { packages.remove(0); writePackagesInternal(); - isTracking.set(false); - trackFirstInternal(); + isSending.set(false); + sendFirstInternal(); } private void readPackagesInternal() { // initialize with empty list; if any exception gets raised // while reading the queue file this list will be used - packages = new ArrayList(); + packages = new ArrayList(); try { FileInputStream inputStream = context.openFileInput(QUEUE_FILENAME); @@ -172,8 +172,8 @@ private void readPackagesInternal() { try { Object object = objectStream.readObject(); @SuppressWarnings("unchecked") - List packages = (List) object; - Logger.debug("queue thread read " + packages.size() + " packages"); + List packages = (List) object; + Logger.debug("queue handler read " + packages.size() + " packages"); this.packages = packages; } catch (ClassNotFoundException e) { @@ -209,7 +209,7 @@ private void writePackagesInternal() { try { objectStream.writeObject(packages); - Logger.verbose("queue thread wrote " + packages.size() + " packages"); + Logger.verbose("queue handler wrote " + packages.size() + " packages"); } catch (NotSerializableException e) { Logger.error("failed to serialize packages"); diff --git a/AdjustIo/src/com/adeven/adjustio/RequestThread.java b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java similarity index 61% rename from AdjustIo/src/com/adeven/adjustio/RequestThread.java rename to AdjustIo/src/com/adeven/adjustio/RequestHandler.java index 830eecabe..b0d09b85a 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestThread.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java @@ -22,58 +22,58 @@ import android.os.Looper; import android.os.Message; -public class RequestThread extends HandlerThread { +public class RequestHandler extends HandlerThread { private static final int CONNECTION_TIMEOUT = 1000 * 5; // 5 seconds TODO: 1 minute? private static final int SOCKET_TIMEOUT = 1000 * 5; // 5 seconds TODO: 1 minute? private static final int MESSAGE_ARG_INIT = 72401; - private static final int MESSAGE_ARG_TRACK = 72400; + private static final int MESSAGE_ARG_SEND = 72400; - private Handler trackingHandler; - private QueueThread queueThread; + private InternalHandler internalHandler; + private QueueHandler queueHandler; private HttpClient httpClient; - protected RequestThread(QueueThread queueThread) { + protected RequestHandler(QueueHandler queueHandler) { super(Logger.LOGTAG, MIN_PRIORITY); setDaemon(true); start(); - this.trackingHandler = new RequestHandler(getLooper(), this); - this.queueThread = queueThread; + this.internalHandler = new InternalHandler(getLooper(), this); + this.queueHandler = queueHandler; Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_INIT; - trackingHandler.sendMessage(message); + internalHandler.sendMessage(message); } - protected void trackPackage(TrackingPackage pack) { + protected void sendPackage(ActivityPackage pack) { Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_TRACK; + message.arg1 = MESSAGE_ARG_SEND; message.obj = pack; - trackingHandler.sendMessage(message); + internalHandler.sendMessage(message); } - private static final class RequestHandler extends Handler { - private final WeakReference requestThreadReference; + private static final class InternalHandler extends Handler { + private final WeakReference requestHandlerReference; - protected RequestHandler(Looper looper, RequestThread requestThread) { + protected InternalHandler(Looper looper, RequestHandler requestHandler) { super(looper); - this.requestThreadReference = new WeakReference(requestThread); + this.requestHandlerReference = new WeakReference(requestHandler); } public void handleMessage(Message message) { super.handleMessage(message); - RequestThread requestThread = requestThreadReference.get(); - if (requestThread == null) return; + RequestHandler requestHandler = requestHandlerReference.get(); + if (requestHandler == null) return; switch (message.arg1) { case MESSAGE_ARG_INIT: - requestThread.initInternal(); + requestHandler.initInternal(); break; - case MESSAGE_ARG_TRACK: - TrackingPackage trackingPackage = (TrackingPackage) message.obj; - requestThread.trackInternal(trackingPackage); + case MESSAGE_ARG_SEND: + ActivityPackage activityPackage = (ActivityPackage) message.obj; + requestHandler.sendInternal(activityPackage); break; } } @@ -89,41 +89,41 @@ private void initInternal() { Logger.error("init httpclient " + httpClient); } - private void trackInternal(TrackingPackage trackingPackage) { + private void sendInternal(ActivityPackage activityPackage) { try { - setUserAgent(trackingPackage.userAgent); - HttpUriRequest request = trackingPackage.getRequest(); + setUserAgent(activityPackage.userAgent); + HttpUriRequest request = activityPackage.getRequest(); HttpResponse response = httpClient.execute(request); - requestFinished(response, trackingPackage); + requestFinished(response, activityPackage); } catch (UnsupportedEncodingException e) { - trackNextPackage(trackingPackage, "Failed to encode parameters", e); + sendNextPackage(activityPackage, "Failed to encode parameters", e); } catch (ClientProtocolException e) { - closePackage(trackingPackage, "Client protocol error", e); + closePackage(activityPackage, "Client protocol error", e); } catch (SocketTimeoutException e) { - closePackage(trackingPackage, "Request timed out", e); + closePackage(activityPackage, "Request timed out", e); } catch (IOException e) { - closePackage(trackingPackage, "Request failed", e); + closePackage(activityPackage, "Request failed", e); } catch (Exception e) { - trackNextPackage(trackingPackage, "Runtime exeption", e); + sendNextPackage(activityPackage, "Runtime exeption", e); } } - private void requestFinished(HttpResponse response, TrackingPackage trackingPackage) { + private void requestFinished(HttpResponse response, ActivityPackage activityPackage) { int statusCode = response.getStatusLine().getStatusCode(); String responseString = parseResponse(response); if (statusCode == HttpStatus.SC_OK) { - Logger.info(trackingPackage.getSuccessMessage()); + Logger.info(activityPackage.getSuccessMessage()); } else { - Logger.warn(trackingPackage.getFailureMessage() + " (" + responseString + ")"); + Logger.warn(activityPackage.getFailureMessage() + " (" + responseString + ")"); } - queueThread.trackNextPackage(); + queueHandler.sendNextPackage(); } private String parseResponse(HttpResponse response) { @@ -140,8 +140,8 @@ private String parseResponse(HttpResponse response) { } } - private void closePackage(TrackingPackage trackingPackage, String message, Throwable e) { - String failureMessage = trackingPackage.getFailureMessage(); + private void closePackage(ActivityPackage activityPackage, String message, Throwable e) { + String failureMessage = activityPackage.getFailureMessage(); String logMessage = failureMessage + " Will retry later. (" + message; if (e != null) { logMessage += ": " + e; @@ -149,11 +149,11 @@ private void closePackage(TrackingPackage trackingPackage, String message, Throw logMessage += ")"; Logger.error(logMessage); - queueThread.closeFirstPackage(); + queueHandler.closeFirstPackage(); } - private void trackNextPackage(TrackingPackage trackingPackage, String message, Throwable e) { - String failureMessage = trackingPackage.getFailureMessage(); + private void sendNextPackage(ActivityPackage activityPackage, String message, Throwable e) { + String failureMessage = activityPackage.getFailureMessage(); String logMessage = failureMessage + " (" + message; if (e != null) { logMessage += ": " + e; @@ -161,7 +161,7 @@ private void trackNextPackage(TrackingPackage trackingPackage, String message, T logMessage += ")"; Logger.error(logMessage); - queueThread.trackNextPackage(); + queueHandler.sendNextPackage(); } private void setUserAgent(String userAgent) { From 32e0e5d9913d5bbf0da69954dfbed11252b12a64 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Wed, 3 Jul 2013 17:37:35 +0200 Subject: [PATCH 17/37] Rename SessionState to ActivityState --- .../com/adeven/adjustio/ActivityHandler.java | 119 +++++++++--------- .../{SessionState.java => ActivityState.java} | 60 +++++---- 2 files changed, 89 insertions(+), 90 deletions(-) rename AdjustIo/src/com/adeven/adjustio/{SessionState.java => ActivityState.java} (54%) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index bfe5bd5e6..85cd12827 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -25,7 +25,7 @@ import android.os.Message; public class ActivityHandler extends HandlerThread { - private static final String SESSION_STATE_FILENAME = "sessionstate1"; // TODO: change filename + private static final String SESSION_STATE_FILENAME = "activitystate1"; // TODO: change filename private static final long TIMER_INTERVAL = 1000 * 10; // 10 second, TODO: one minute private static final long SESSION_INTERVAL = 1000 * 15; // 15 seconds, TODO: 30 minutes @@ -38,7 +38,7 @@ public class ActivityHandler extends HandlerThread { private static final int MESSAGE_ARG_REVENUE = 72670; private InternalHandler internalHandler; - private SessionState sessionState; + private ActivityState activityState; private QueueHandler queueHandler; private static ScheduledExecutorService timer; // TODO: rename to timer @@ -160,7 +160,7 @@ private void initInternal(String token) { fbAttributionId = Util.getAttributionId(context); queueHandler = new QueueHandler(context); - readSessionState(); + readActivityState(); } private void startInternal() { @@ -172,41 +172,41 @@ private void startInternal() { long now = new Date().getTime(); // very first session - if (sessionState == null) { + if (activityState == null) { Logger.info("First session"); - sessionState = new SessionState(); - sessionState.sessionCount = 1; // this is the first session - sessionState.createdAt = now; // starting now + activityState = new ActivityState(); + activityState.sessionCount = 1; // this is the first session + activityState.createdAt = now; // starting now enqueueSessionPackage(); - writeSessionState(); + writeActivityState(); return; } - long lastInterval = now - sessionState.lastActivity; + long lastInterval = now - activityState.lastActivity; if (lastInterval < 0) { Logger.error("Time travel!"); - sessionState.lastActivity = now; - writeSessionState(); + activityState.lastActivity = now; + writeActivityState(); return; } // new session if (lastInterval > SESSION_INTERVAL) { - sessionState.lastInterval = lastInterval; + activityState.lastInterval = lastInterval; enqueueSessionPackage(); - sessionState.startNextSession(now); - writeSessionState(); + activityState.startNextSession(now); + writeActivityState(); return; } // new subsession if (lastInterval > SUBSESSION_INTERVAL) { - sessionState.subsessionCount++; + activityState.subsessionCount++; } - sessionState.sessionLength += lastInterval; - sessionState.lastActivity = now; - writeSessionState(); + activityState.sessionLength += lastInterval; + activityState.lastActivity = now; + writeActivityState(); } private void endInternal() { @@ -214,26 +214,27 @@ private void endInternal() { queueHandler.pauseSending(); stopTimer(); - updateSessionState(); - writeSessionState(); + updateActivityState(); + writeActivityState(); } + // TODO: set session attributes to -1 for events after session end? private void eventInternal(PackageBuilder eventBuilder) { Logger.error("event start"); if (!checkAppTokenNotNull(appToken)) return; - if (!checkSessionState(sessionState)) return; + if (!checkActivityState(activityState)) return; if (!checkEventTokenNotNull(eventBuilder.eventToken)) return; if (!checkEventTokenLength(eventBuilder.eventToken)) return; - sessionState.eventCount++; - updateSessionState(); + activityState.eventCount++; + updateActivityState(); injectGeneralAttributes(eventBuilder); - sessionState.injectEventAttributes(eventBuilder); + activityState.injectEventAttributes(eventBuilder); ActivityPackage eventPackage = eventBuilder.buildEventPackage(); queueHandler.addPackage(eventPackage); - writeSessionState(); + writeActivityState(); try { Thread.sleep(500); } catch(Exception e) {} Logger.error("event end"); } @@ -241,28 +242,28 @@ private void eventInternal(PackageBuilder eventBuilder) { private void revenueInternal(PackageBuilder revenueBuilder) { Logger.error("revenue start"); if (!checkAppTokenNotNull(appToken)) return; - if (!checkSessionState(sessionState)) return; + if (!checkActivityState(activityState)) return; if (!checkAmount(revenueBuilder.amountInCents)) return; if (!checkEventTokenLength(revenueBuilder.eventToken)) return; - sessionState.eventCount++; - updateSessionState(); + activityState.eventCount++; + updateActivityState(); injectGeneralAttributes(revenueBuilder); - sessionState.injectEventAttributes(revenueBuilder); + activityState.injectEventAttributes(revenueBuilder); ActivityPackage revenuePackage = revenueBuilder.buildRevenuePackage(); queueHandler.addPackage(revenuePackage); - writeSessionState(); + writeActivityState(); try { Thread.sleep(500); } catch(Exception e) {} Logger.error("revenue end"); } // called from inside - private void readSessionState() { - // if any exception gets raised we start with a fresh sessionState - sessionState = null; + private void readActivityState() { + // if any exception gets raised we start with a fresh activity state + activityState = null; try { FileInputStream inputStream = context.openFileInput(SESSION_STATE_FILENAME); @@ -270,18 +271,18 @@ private void readSessionState() { ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); try { - sessionState = (SessionState) objectStream.readObject(); - Logger.debug("Read session state: " + sessionState); + activityState = (ActivityState) objectStream.readObject(); + Logger.debug("Read activity state: " + activityState); } catch (ClassNotFoundException e) { - Logger.error("Failed to find session state class"); + Logger.error("Failed to find activity state class"); } catch (OptionalDataException e) {} catch (IOException e) { - Logger.error("Failed to read session states object"); + Logger.error("Failed to read activity states object"); } catch (ClassCastException e) { - Logger.error("Failed to cast session state object"); + Logger.error("Failed to cast activity state object"); } finally { objectStream.close(); @@ -289,15 +290,15 @@ private void readSessionState() { } catch (FileNotFoundException e) { - Logger.verbose("Session state file not found"); + Logger.verbose("Activity state file not found"); } catch (IOException e) { - Logger.error("Failed to read session state file"); + Logger.error("Failed to read activity state file"); } } - // TODO: move to sessionState? - private void writeSessionState() { + // TODO: move to activityState? + private void writeActivityState() { try { // TODO: remove sleeps! Thread.sleep(100); } @@ -309,11 +310,11 @@ private void writeSessionState() { ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream); try { - objectStream.writeObject(sessionState); - Logger.debug("Wrote session state: " + sessionState); + objectStream.writeObject(activityState); + Logger.debug("Wrote activity state: " + activityState); } catch (NotSerializableException e) { - Logger.error("Failed to serialize session state"); + Logger.error("Failed to serialize activity state"); } finally { objectStream.close(); @@ -321,37 +322,37 @@ private void writeSessionState() { } catch (IOException e) { - Logger.error("Failed to write session state (" + e + ")"); + Logger.error("Failed to write activity state (" + e + ")"); } } private void enqueueSessionPackage() { PackageBuilder builder = new PackageBuilder(); injectGeneralAttributes(builder); - sessionState.injectSessionAttributes(builder); + activityState.injectSessionAttributes(builder); ActivityPackage sessionPackage = builder.buildSessionPackage(); queueHandler.addPackage(sessionPackage); } // called from inside - private void updateSessionState() { - if (!checkSessionState(sessionState)) return; + private void updateActivityState() { + if (!checkActivityState(activityState)) return; long now = new Date().getTime(); - long lastInterval = now - sessionState.lastActivity; + long lastInterval = now - activityState.lastActivity; if (lastInterval < 0) { Logger.error("Time travel"); - sessionState.lastActivity = now; + activityState.lastActivity = now; return; } // ignore late updates if (lastInterval > SESSION_INTERVAL) return; - sessionState.sessionLength += lastInterval; - sessionState.timeSpent += lastInterval; - sessionState.lastActivity = now; + activityState.sessionLength += lastInterval; + activityState.timeSpent += lastInterval; + activityState.lastActivity = now; } private void injectGeneralAttributes(PackageBuilder builder) { @@ -387,8 +388,8 @@ private void stopTimer() { private void timerFired() { queueHandler.sendFirstPackage(); - updateSessionState(); - writeSessionState(); + updateActivityState(); + writeActivityState(); } private static boolean checkPermissions(Context context) { @@ -425,9 +426,9 @@ private static boolean checkPermission(Context context, String permission) { return granted; } - private static boolean checkSessionState(SessionState sessionState) { - if (sessionState == null) { - Logger.error("Missing session state."); + private static boolean checkActivityState(ActivityState activityState) { + if (activityState == null) { + Logger.error("Missing activity state."); return false; } return true; diff --git a/AdjustIo/src/com/adeven/adjustio/SessionState.java b/AdjustIo/src/com/adeven/adjustio/ActivityState.java similarity index 54% rename from AdjustIo/src/com/adeven/adjustio/SessionState.java rename to AdjustIo/src/com/adeven/adjustio/ActivityState.java index 6b13b1508..0623718ac 100644 --- a/AdjustIo/src/com/adeven/adjustio/SessionState.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityState.java @@ -4,7 +4,7 @@ import java.util.Date; import java.util.Locale; -public class SessionState implements Serializable { +public class ActivityState implements Serializable { private static final long serialVersionUID = 9039439291143138148L; // TODO: make attributes private? @@ -21,54 +21,44 @@ public class SessionState implements Serializable { protected long lastActivity; protected long lastInterval; - protected SessionState() { - eventCount = 0; // no events yet - sessionCount = 0; // the first session just started - subsessionCount = -1; // we don't know how many subssessions 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 - createdAt = -1; - lastActivity = -1; - lastInterval = -1; + protected ActivityState() { + eventCount = 0; // no events yet + sessionCount = 0; // the first session just started + subsessionCount = -1; // we don't know how many subssessions 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 + createdAt = -1; + lastActivity = -1; + lastInterval = -1; } protected void startNextSession(long now) { - sessionCount++; // the next session just started - subsessionCount = 1; // first subsession - sessionLength = 0; // no session length yet - timeSpent = 0; // no time spent yet - createdAt = now; - lastActivity = now; - lastInterval = 0; + sessionCount++; // the next session just started + subsessionCount = 1; // first subsession + sessionLength = 0; // no session length yet + timeSpent = 0; // no time spent yet + createdAt = now; + lastActivity = now; + lastInterval = 0; } protected void injectSessionAttributes(PackageBuilder builder) { - builder.sessionCount = sessionCount; - builder.subsessionCount = subsessionCount; - builder.sessionLength = sessionLength; - builder.timeSpent = timeSpent; - builder.createdAt = createdAt; - + injectGeneralAttributes(builder); builder.lastInterval = lastInterval; } protected void injectEventAttributes(PackageBuilder builder) { - builder.sessionCount = sessionCount; - builder.subsessionCount = subsessionCount; - builder.sessionLength = sessionLength; - builder.timeSpent = timeSpent; - builder.createdAt = createdAt; - + injectGeneralAttributes(builder); builder.eventCount = eventCount; } - + public String toString() { return String.format(Locale.US, "ec:%d sc:%d ssc:%d sl:%d ts:%d ca:%s la:%s", eventCount, sessionCount, subsessionCount, sessionLength, timeSpent, stamp(createdAt), stamp(lastActivity)); } - + private static String stamp(long dateMillis) { Date date = new Date(dateMillis); return String.format(Locale.US, @@ -77,4 +67,12 @@ private static String stamp(long dateMillis) { date.getMinutes(), date.getSeconds()); } + + private void injectGeneralAttributes(PackageBuilder builder) { + builder.sessionCount = sessionCount; + builder.subsessionCount = subsessionCount; + builder.sessionLength = sessionLength; + builder.timeSpent = timeSpent; + builder.createdAt = createdAt; + } } From 4d3675df4df304ccdf81f57250a8aeba74ee9873 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Wed, 3 Jul 2013 18:04:22 +0200 Subject: [PATCH 18/37] Rename QueueHandler to PackageHandler --- .../com/adeven/adjustio/ActivityHandler.java | 8 +-- ...{QueueHandler.java => PackageHandler.java} | 58 +++++++++---------- .../com/adeven/adjustio/RequestHandler.java | 4 +- 3 files changed, 33 insertions(+), 37 deletions(-) rename AdjustIo/src/com/adeven/adjustio/{QueueHandler.java => PackageHandler.java} (77%) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index 85cd12827..4f2076dc6 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -39,7 +39,7 @@ public class ActivityHandler extends HandlerThread { private InternalHandler internalHandler; private ActivityState activityState; - private QueueHandler queueHandler; + private PackageHandler queueHandler; private static ScheduledExecutorService timer; // TODO: rename to timer private Context context; @@ -159,7 +159,7 @@ private void initInternal(String token) { androidId = Util.getAndroidId(context); fbAttributionId = Util.getAttributionId(context); - queueHandler = new QueueHandler(context); + queueHandler = new PackageHandler(context); readActivityState(); } @@ -220,7 +220,6 @@ private void endInternal() { // TODO: set session attributes to -1 for events after session end? private void eventInternal(PackageBuilder eventBuilder) { - Logger.error("event start"); if (!checkAppTokenNotNull(appToken)) return; if (!checkActivityState(activityState)) return; if (!checkEventTokenNotNull(eventBuilder.eventToken)) return; @@ -236,11 +235,9 @@ private void eventInternal(PackageBuilder eventBuilder) { writeActivityState(); try { Thread.sleep(500); } catch(Exception e) {} - Logger.error("event end"); } private void revenueInternal(PackageBuilder revenueBuilder) { - Logger.error("revenue start"); if (!checkAppTokenNotNull(appToken)) return; if (!checkActivityState(activityState)) return; if (!checkAmount(revenueBuilder.amountInCents)) return; @@ -256,7 +253,6 @@ private void revenueInternal(PackageBuilder revenueBuilder) { writeActivityState(); try { Thread.sleep(500); } catch(Exception e) {} - Logger.error("revenue end"); } // called from inside diff --git a/AdjustIo/src/com/adeven/adjustio/QueueHandler.java b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java similarity index 77% rename from AdjustIo/src/com/adeven/adjustio/QueueHandler.java rename to AdjustIo/src/com/adeven/adjustio/PackageHandler.java index 00e741857..de5411b3e 100644 --- a/AdjustIo/src/com/adeven/adjustio/QueueHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java @@ -22,8 +22,8 @@ import android.os.Message; // persistent -public class QueueHandler extends HandlerThread { - private static final String QUEUE_FILENAME = "testqueue3"; // TODO: change filename +public class PackageHandler extends HandlerThread { + private static final String PACKAGE_QUEUE_FILENAME = "testqueue3"; // TODO: change filename private static final int MESSAGE_ARG_ADD = 72500; // TODO: change constants! private static final int MESSAGE_ARG_SEND_NEXT = 72510; @@ -34,10 +34,10 @@ public class QueueHandler extends HandlerThread { private RequestHandler requestHandler; private Context context; private AtomicBoolean isSending; - private List packages; + private List packageQueue; private boolean paused; - protected QueueHandler(Context context) { + protected PackageHandler(Context context) { super(Logger.LOGTAG, MIN_PRIORITY); setDaemon(true); start(); @@ -91,32 +91,32 @@ protected void resumeSending() { } private static final class InternalHandler extends Handler { - private final WeakReference queueHandlerReference; + private final WeakReference packageHandlerReference; - protected InternalHandler(Looper looper, QueueHandler queueHandler) { + protected InternalHandler(Looper looper, PackageHandler packageHandler) { super(looper); - this.queueHandlerReference = new WeakReference(queueHandler); + this.packageHandlerReference = new WeakReference(packageHandler); } public void handleMessage(Message message) { super.handleMessage(message); - QueueHandler queueHandler = queueHandlerReference.get(); - if (queueHandler == null) return; + PackageHandler packageHandler = packageHandlerReference.get(); + if (packageHandler == null) return; switch (message.arg1) { case MESSAGE_ARG_ADD: ActivityPackage activityPackage = (ActivityPackage) message.obj; - queueHandler.addInternal(activityPackage); + packageHandler.addInternal(activityPackage); break; case MESSAGE_ARG_SEND_FIRST: - queueHandler.sendFirstInternal(); + packageHandler.sendFirstInternal(); break; case MESSAGE_ARG_SEND_NEXT: - queueHandler.sendNextInternal(); + packageHandler.sendNextInternal(); break; case MESSAGE_ARG_READ: - queueHandler.readPackagesInternal(); + packageHandler.readPackageQueue(); break; } } @@ -125,11 +125,11 @@ public void handleMessage(Message message) { // internal methods run in dedicated queue thread private void addInternal(ActivityPackage newPackage) { - packages.add(newPackage); - Logger.debug("added package " + packages.size() + " (" + newPackage + ")"); + packageQueue.add(newPackage); + Logger.debug("added package " + packageQueue.size() + " (" + newPackage + ")"); Logger.verbose(newPackage.parameterString()); - writePackagesInternal(); + writePackageQueue(); sendFirstInternal(); } @@ -144,7 +144,7 @@ private void sendFirstInternal() { } try { - ActivityPackage firstPackage = packages.get(0); + ActivityPackage firstPackage = packageQueue.get(0); requestHandler.sendPackage(firstPackage); } catch (IndexOutOfBoundsException e) { @@ -153,28 +153,28 @@ private void sendFirstInternal() { } private void sendNextInternal() { - packages.remove(0); - writePackagesInternal(); + packageQueue.remove(0); + writePackageQueue(); isSending.set(false); sendFirstInternal(); } - private void readPackagesInternal() { + private void readPackageQueue() { // initialize with empty list; if any exception gets raised // while reading the queue file this list will be used - packages = new ArrayList(); + packageQueue = new ArrayList(); try { - FileInputStream inputStream = context.openFileInput(QUEUE_FILENAME); + FileInputStream inputStream = context.openFileInput(PACKAGE_QUEUE_FILENAME); BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); try { Object object = objectStream.readObject(); @SuppressWarnings("unchecked") - List packages = (List) object; - Logger.debug("queue handler read " + packages.size() + " packages"); - this.packages = packages; + List packageQueue = (List) object; + Logger.debug("queue handler read " + packageQueue.size() + " packages"); + this.packageQueue = packageQueue; } catch (ClassNotFoundException e) { Logger.error("failed to find queue class"); @@ -197,19 +197,19 @@ private void readPackagesInternal() { } } - private void writePackagesInternal() { + private void writePackageQueue() { try { // TODO: remove sleeps Thread.sleep(100); } catch (Exception e) {} try { - FileOutputStream outputStream = context.openFileOutput(QUEUE_FILENAME, Context.MODE_PRIVATE); + FileOutputStream outputStream = context.openFileOutput(PACKAGE_QUEUE_FILENAME, Context.MODE_PRIVATE); BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream); try { - objectStream.writeObject(packages); - Logger.verbose("queue handler wrote " + packages.size() + " packages"); + objectStream.writeObject(packageQueue); + Logger.verbose("queue handler wrote " + packageQueue.size() + " packages"); } catch (NotSerializableException e) { Logger.error("failed to serialize packages"); diff --git a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java index b0d09b85a..67a8aaa91 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java @@ -30,10 +30,10 @@ public class RequestHandler extends HandlerThread { private static final int MESSAGE_ARG_SEND = 72400; private InternalHandler internalHandler; - private QueueHandler queueHandler; + private PackageHandler queueHandler; private HttpClient httpClient; - protected RequestHandler(QueueHandler queueHandler) { + protected RequestHandler(PackageHandler queueHandler) { super(Logger.LOGTAG, MIN_PRIORITY); setDaemon(true); start(); From 4c992ae16f9d175e417067a232e16d30aac5e9e5 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Wed, 3 Jul 2013 19:41:32 +0200 Subject: [PATCH 19/37] Rename, unify, prepare macMd5 --- .../com/adeven/adjustio/ActivityHandler.java | 30 +++++++++---------- .../com/adeven/adjustio/ActivityPackage.java | 20 ++++++------- .../com/adeven/adjustio/ActivityState.java | 4 +-- .../com/adeven/adjustio/PackageBuilder.java | 9 +++--- .../com/adeven/adjustio/PackageHandler.java | 2 +- 5 files changed, 32 insertions(+), 33 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index 4f2076dc6..2934b8acc 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -38,15 +38,15 @@ public class ActivityHandler extends HandlerThread { private static final int MESSAGE_ARG_REVENUE = 72670; private InternalHandler internalHandler; + private PackageHandler packageHandler; private ActivityState activityState; - private PackageHandler queueHandler; private static ScheduledExecutorService timer; // TODO: rename to timer private Context context; private String appToken; private String macSha1; - private String macShort; // TODO: md5!!! + private String macShortMd5; // TODO: md5!!! private String androidId; // everything else here could be persisted private String fbAttributionId; private String userAgent; // changes, should be updated periodically @@ -154,19 +154,19 @@ private void initInternal(String token) { appToken = token; macSha1 = Util.sha1(macAddress); - macShort = macAddress.replaceAll(":", ""); // TODO: macMd5!!! + macShortMd5 = macAddress.replaceAll(":", ""); // TODO: macMd5!!! userAgent = Util.getUserAgent(context); androidId = Util.getAndroidId(context); fbAttributionId = Util.getAttributionId(context); - queueHandler = new PackageHandler(context); + packageHandler = new PackageHandler(context); readActivityState(); } private void startInternal() { if (!checkAppTokenNotNull(appToken)) return; - queueHandler.resumeSending(); + packageHandler.resumeSending(); startTimer(); long now = new Date().getTime(); @@ -178,7 +178,7 @@ private void startInternal() { activityState.sessionCount = 1; // this is the first session activityState.createdAt = now; // starting now - enqueueSessionPackage(); + transferSessionPackage(); writeActivityState(); return; } @@ -194,7 +194,7 @@ private void startInternal() { // new session if (lastInterval > SESSION_INTERVAL) { activityState.lastInterval = lastInterval; - enqueueSessionPackage(); + transferSessionPackage(); activityState.startNextSession(now); writeActivityState(); return; @@ -212,7 +212,7 @@ private void startInternal() { private void endInternal() { if (!checkAppTokenNotNull(appToken)) return; - queueHandler.pauseSending(); + packageHandler.pauseSending(); stopTimer(); updateActivityState(); writeActivityState(); @@ -231,7 +231,7 @@ private void eventInternal(PackageBuilder eventBuilder) { activityState.injectEventAttributes(eventBuilder); ActivityPackage eventPackage = eventBuilder.buildEventPackage(); - queueHandler.addPackage(eventPackage); + packageHandler.addPackage(eventPackage); writeActivityState(); try { Thread.sleep(500); } catch(Exception e) {} @@ -249,7 +249,7 @@ private void revenueInternal(PackageBuilder revenueBuilder) { activityState.injectEventAttributes(revenueBuilder); ActivityPackage revenuePackage = revenueBuilder.buildRevenuePackage(); - queueHandler.addPackage(revenuePackage); + packageHandler.addPackage(revenuePackage); writeActivityState(); try { Thread.sleep(500); } catch(Exception e) {} @@ -322,12 +322,12 @@ private void writeActivityState() { } } - private void enqueueSessionPackage() { + private void transferSessionPackage() { PackageBuilder builder = new PackageBuilder(); injectGeneralAttributes(builder); activityState.injectSessionAttributes(builder); ActivityPackage sessionPackage = builder.buildSessionPackage(); - queueHandler.addPackage(sessionPackage); + packageHandler.addPackage(sessionPackage); } // called from inside @@ -354,10 +354,10 @@ private void updateActivityState() { private void injectGeneralAttributes(PackageBuilder builder) { builder.userAgent = userAgent; builder.appToken = appToken; - builder.macShort = macShort; + builder.macShortMd5 = macShortMd5; builder.macSha1 = macSha1; builder.androidId = androidId; - builder.attributionId = fbAttributionId; + builder.fbAttributionId = fbAttributionId; } private void startTimer() { @@ -382,7 +382,7 @@ private void stopTimer() { } private void timerFired() { - queueHandler.sendFirstPackage(); + packageHandler.sendFirstPackage(); updateActivityState(); writeActivityState(); diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java b/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java index 0875f2480..f03c7b54b 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java @@ -29,20 +29,18 @@ public class ActivityPackage implements Serializable { protected String suffix; public String toString() { - return kind + suffix + " " + path; + return String.format("%s%s %s", kind, suffix, path); } - protected String parameterString() { - try { - String parameterString = "Parameters:"; - for (Map.Entry entity : parameters.entrySet()) { - parameterString += String.format("\n\t%-16s %s", entity.getKey(), entity.getValue()); - } - return parameterString; - } - catch (NullPointerException e) { - return "Parameters: null"; + protected String getParameterString() { + if (parameters == null) return "Parameters: null"; + + StringBuilder builder = new StringBuilder("Parameters:"); + + for (Map.Entry entity : parameters.entrySet()) { + builder.append(String.format("\n\t%-16s %s", entity.getKey(), entity.getValue())); } + return builder.toString(); } protected String getSuccessMessage() { diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityState.java b/AdjustIo/src/com/adeven/adjustio/ActivityState.java index 0623718ac..004673956 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityState.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityState.java @@ -51,14 +51,14 @@ protected void injectEventAttributes(PackageBuilder builder) { injectGeneralAttributes(builder); builder.eventCount = eventCount; } - + public String toString() { return String.format(Locale.US, "ec:%d sc:%d ssc:%d sl:%d ts:%d ca:%s la:%s", eventCount, sessionCount, subsessionCount, sessionLength, timeSpent, stamp(createdAt), stamp(lastActivity)); } - + private static String stamp(long dateMillis) { Date date = new Date(dateMillis); return String.format(Locale.US, diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java index 24f5e221b..066c61e67 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -13,9 +13,9 @@ public class PackageBuilder { // general protected String appToken; protected String macSha1; - protected String macShort; // TODO: md5! + protected String macShortMd5; protected String androidId; - protected String attributionId; + protected String fbAttributionId; protected String userAgent; // sessions @@ -81,9 +81,9 @@ private Map getDefaultParameters() { addDate(parameters, "created_at", createdAt); addString(parameters, "app_token", appToken); addString(parameters, "mac_sha1", macSha1); - addString(parameters, "mac", macShort); + addString(parameters, "mac", macShortMd5); // TODO: rename parameter addString(parameters, "android_id", androidId); - addString(parameters, "fb_id", attributionId); + addString(parameters, "fb_id", fbAttributionId); // session related (used for events as well) addInt(parameters, "session_id", sessionCount); // TODO: rename parameters @@ -138,6 +138,7 @@ private void addDate(Map parameters, String key, long value) { Date date = new Date(value); String dateString = date.toString(); // TODO: format with DateFormat + // TODO: nope, lets send the time since 1970 in rounded seconds (unix timestamp) addString(parameters, key, dateString); } diff --git a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java index de5411b3e..19b087585 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java @@ -127,7 +127,7 @@ public void handleMessage(Message message) { private void addInternal(ActivityPackage newPackage) { packageQueue.add(newPackage); Logger.debug("added package " + packageQueue.size() + " (" + newPackage + ")"); - Logger.verbose(newPackage.parameterString()); + Logger.verbose(newPackage.getParameterString()); writePackageQueue(); sendFirstInternal(); From 80c4c96e351136dd2075835b4e50496cacf59933 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Thu, 4 Jul 2013 12:20:26 +0200 Subject: [PATCH 20/37] Unify structure, improve logs --- .../com/adeven/adjustio/ActivityHandler.java | 53 +++++++++-------- .../com/adeven/adjustio/ActivityState.java | 2 +- .../com/adeven/adjustio/PackageHandler.java | 58 +++++++++++-------- 3 files changed, 60 insertions(+), 53 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index 2934b8acc..e03640ee7 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -252,15 +252,32 @@ private void revenueInternal(PackageBuilder revenueBuilder) { packageHandler.addPackage(revenuePackage); writeActivityState(); - try { Thread.sleep(500); } catch(Exception e) {} } // called from inside - private void readActivityState() { - // if any exception gets raised we start with a fresh activity state - activityState = null; + // called from inside + + private void updateActivityState() { + if (!checkActivityState(activityState)) return; + + long now = new Date().getTime(); + 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); @@ -269,6 +286,7 @@ private void readActivityState() { try { activityState = (ActivityState) objectStream.readObject(); Logger.debug("Read activity state: " + activityState); + return; } catch (ClassNotFoundException e) { Logger.error("Failed to find activity state class"); @@ -291,14 +309,14 @@ private void readActivityState() { catch (IOException e) { Logger.error("Failed to read activity state file"); } + + // start with a fresh activity state in case of any exception + activityState = null; } // TODO: move to activityState? private void writeActivityState() { - try { // TODO: remove sleeps! - Thread.sleep(100); - } - catch (Exception e) {} + try { Thread.sleep(300); } catch(Exception e) {} // TODO: remove sleeps! try { FileOutputStream outputStream = context.openFileOutput(SESSION_STATE_FILENAME, Context.MODE_PRIVATE); @@ -332,25 +350,6 @@ private void transferSessionPackage() { // called from inside - private void updateActivityState() { - if (!checkActivityState(activityState)) return; - - long now = new Date().getTime(); - 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 injectGeneralAttributes(PackageBuilder builder) { builder.userAgent = userAgent; builder.appToken = appToken; diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityState.java b/AdjustIo/src/com/adeven/adjustio/ActivityState.java index 004673956..9229eb7eb 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityState.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityState.java @@ -62,7 +62,7 @@ public String toString() { private static String stamp(long dateMillis) { Date date = new Date(dateMillis); return String.format(Locale.US, - "%2d:%2d:%2d", + "%02d:%02d:%02d", date.getHours(), date.getMinutes(), date.getSeconds()); diff --git a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java index 19b087585..02d8a942f 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java @@ -13,6 +13,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; import android.content.Context; @@ -21,20 +22,22 @@ import android.os.Looper; import android.os.Message; +// TODO: user String.format everywhere instead of "a" + "b" + // persistent public class PackageHandler extends HandlerThread { - private static final String PACKAGE_QUEUE_FILENAME = "testqueue3"; // TODO: change filename + private static final String PACKAGE_QUEUE_FILENAME = "testqueue6"; // TODO: change filename - private static final int MESSAGE_ARG_ADD = 72500; // TODO: change constants! + private static final int MESSAGE_ARG_INIT = 72501; // TODO: change constants! + private static final int MESSAGE_ARG_ADD = 72500; private static final int MESSAGE_ARG_SEND_NEXT = 72510; private static final int MESSAGE_ARG_SEND_FIRST = 72530; - private static final int MESSAGE_ARG_READ = 72520; private InternalHandler internalHandler; private RequestHandler requestHandler; - private Context context; - private AtomicBoolean isSending; private List packageQueue; + private AtomicBoolean isSending; + private Context context; private boolean paused; protected PackageHandler(Context context) { @@ -44,11 +47,9 @@ protected PackageHandler(Context context) { this.internalHandler = new InternalHandler(getLooper(), this); this.context = context; - this.isSending = new AtomicBoolean(); - this.requestHandler = new RequestHandler(this); Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_READ; + message.arg1 = MESSAGE_ARG_INIT; internalHandler.sendMessage(message); } @@ -105,6 +106,9 @@ public void handleMessage(Message message) { if (packageHandler == null) return; switch (message.arg1) { + case MESSAGE_ARG_INIT: + packageHandler.initInternal(); + break; case MESSAGE_ARG_ADD: ActivityPackage activityPackage = (ActivityPackage) message.obj; packageHandler.addInternal(activityPackage); @@ -115,18 +119,22 @@ public void handleMessage(Message message) { case MESSAGE_ARG_SEND_NEXT: packageHandler.sendNextInternal(); break; - case MESSAGE_ARG_READ: - packageHandler.readPackageQueue(); - break; } } } // internal methods run in dedicated queue thread + private void initInternal() { + requestHandler = new RequestHandler(this); + isSending = new AtomicBoolean(); + + readPackageQueue(); + } + private void addInternal(ActivityPackage newPackage) { packageQueue.add(newPackage); - Logger.debug("added package " + packageQueue.size() + " (" + newPackage + ")"); + Logger.debug(String.format(Locale.US, "Added package %d (%s)", packageQueue.size(), newPackage)); Logger.verbose(newPackage.getParameterString()); writePackageQueue(); @@ -160,10 +168,6 @@ private void sendNextInternal() { } private void readPackageQueue() { - // initialize with empty list; if any exception gets raised - // while reading the queue file this list will be used - packageQueue = new ArrayList(); - try { FileInputStream inputStream = context.openFileInput(PACKAGE_QUEUE_FILENAME); BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); @@ -173,28 +177,32 @@ private void readPackageQueue() { Object object = objectStream.readObject(); @SuppressWarnings("unchecked") List packageQueue = (List) object; - Logger.debug("queue handler read " + packageQueue.size() + " packages"); + Logger.debug(String.format(Locale.US, "Package handler read %d packages", packageQueue.size())); this.packageQueue = packageQueue; + return; } catch (ClassNotFoundException e) { - Logger.error("failed to find queue class"); + Logger.error("Failed to find package queue class"); } catch (OptionalDataException e) {} catch (IOException e) { - Logger.error("failed to read queue object"); + Logger.error("Failed to read package queue object"); } catch (ClassCastException e) { - Logger.error("failed to cast queue object"); + Logger.error("Failed to cast package queue object"); } finally { objectStream.close(); } } catch (FileNotFoundException e) { - Logger.verbose("queue file not found"); + Logger.verbose("Package queue file not found"); } catch (IOException e) { - Logger.error("failed to read queue file"); + Logger.error("Failed to read package queue file"); } + + // start with a fresh package queue in case of any exception + packageQueue = new ArrayList(); } private void writePackageQueue() { @@ -209,17 +217,17 @@ private void writePackageQueue() { try { objectStream.writeObject(packageQueue); - Logger.verbose("queue handler wrote " + packageQueue.size() + " packages"); + Logger.verbose(String.format(Locale.US, "Package handler wrote %d packages", packageQueue.size())); } catch (NotSerializableException e) { - Logger.error("failed to serialize packages"); + Logger.error("Failed to serialize packages"); } finally { objectStream.close(); } } catch (IOException e) { - Logger.error("failed to write packages (" + e.getLocalizedMessage() + ")"); + Logger.error(String.format("Failed to write packages (%s)", e.getLocalizedMessage())); e.printStackTrace(); } } From 0e5c05bd2f2df8a3633a55e986d9e34f74821e77 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Thu, 4 Jul 2013 19:10:19 +0200 Subject: [PATCH 21/37] Move getRequest to RequestHandler --- .../com/adeven/adjustio/ActivityHandler.java | 1 - .../com/adeven/adjustio/ActivityPackage.java | 36 +---------- .../com/adeven/adjustio/PackageBuilder.java | 2 +- .../com/adeven/adjustio/PackageHandler.java | 17 +++--- .../com/adeven/adjustio/RequestHandler.java | 59 ++++++++++++++----- 5 files changed, 53 insertions(+), 62 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index e03640ee7..68371fafc 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -41,7 +41,6 @@ public class ActivityHandler extends HandlerThread { private PackageHandler packageHandler; private ActivityState activityState; private static ScheduledExecutorService timer; // TODO: rename to timer - private Context context; private String appToken; diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java b/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java index f03c7b54b..e240d8eaa 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java @@ -3,19 +3,8 @@ package com.adeven.adjustio; import java.io.Serializable; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; import java.util.Map; -import org.apache.http.NameValuePair; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.message.BasicNameValuePair; - public class ActivityPackage implements Serializable { private static final long serialVersionUID = -35935556512024097L; @@ -44,31 +33,10 @@ protected String getParameterString() { } protected String getSuccessMessage() { - return "Tracked " + kind + suffix; + return String.format("Tracked %s%s", kind, suffix); } protected String getFailureMessage() { - return "Failed to track " + kind + suffix; - } - - protected HttpUriRequest getRequest() throws UnsupportedEncodingException { - String url = AdjustIo.BASE_URL + path; - HttpPost request = new HttpPost(url); - - String language = Locale.getDefault().getLanguage(); - request.addHeader("Accept-Language", language); - request.addHeader("Client-SDK", AdjustIo.CLIENT_SDK); - - List pairs = new ArrayList(); - for (Map.Entry entity : parameters.entrySet()) { - NameValuePair pair = new BasicNameValuePair(entity.getKey(), entity.getValue()); - pairs.add(pair); - } - - UrlEncodedFormEntity entity = new UrlEncodedFormEntity(pairs); - entity.setContentType(URLEncodedUtils.CONTENT_TYPE); - request.setEntity(entity); - - return request; + return String.format("Failed to track %s%s", kind, suffix); } } diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java index 066c61e67..a0612ba8e 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -38,7 +38,7 @@ protected ActivityPackage buildSessionPackage() { ActivityPackage sessionPackage = new ActivityPackage(); sessionPackage.path = "/startup"; sessionPackage.kind = "session start"; - sessionPackage.suffix = "."; + sessionPackage.suffix = ""; sessionPackage.parameters = parameters; sessionPackage.userAgent = userAgent; diff --git a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java index 02d8a942f..e6bb48f06 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java @@ -37,8 +37,8 @@ public class PackageHandler extends HandlerThread { private RequestHandler requestHandler; private List packageQueue; private AtomicBoolean isSending; - private Context context; private boolean paused; + private Context context; protected PackageHandler(Context context) { super(Logger.LOGTAG, MIN_PRIORITY); @@ -142,22 +142,19 @@ private void addInternal(ActivityPackage newPackage) { } private void sendFirstInternal() { + if (packageQueue.size() == 0) return; + if (paused) { - Logger.debug("paused"); + Logger.debug("Package handler is paused"); return; } if (isSending.getAndSet(true)) { - Logger.debug("locked"); + Logger.debug("Package handler is already sending"); return; } - try { - ActivityPackage firstPackage = packageQueue.get(0); - requestHandler.sendPackage(firstPackage); - } - catch (IndexOutOfBoundsException e) { - isSending.set(false); - } + ActivityPackage firstPackage = packageQueue.get(0); + requestHandler.sendPackage(firstPackage); } private void sendNextInternal() { diff --git a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java index 67a8aaa91..20fb53d62 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java @@ -5,13 +5,22 @@ 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 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; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.CoreProtocolPNames; import org.apache.http.params.HttpConnectionParams; @@ -30,16 +39,16 @@ public class RequestHandler extends HandlerThread { private static final int MESSAGE_ARG_SEND = 72400; private InternalHandler internalHandler; - private PackageHandler queueHandler; + private PackageHandler packageHandler; private HttpClient httpClient; - protected RequestHandler(PackageHandler queueHandler) { + protected RequestHandler(PackageHandler packageHandler) { super(Logger.LOGTAG, MIN_PRIORITY); setDaemon(true); start(); this.internalHandler = new InternalHandler(getLooper(), this); - this.queueHandler = queueHandler; + this.packageHandler = packageHandler; Message message = Message.obtain(); message.arg1 = MESSAGE_ARG_INIT; @@ -86,13 +95,12 @@ private void initInternal() { HttpConnectionParams.setConnectionTimeout(httpParams, CONNECTION_TIMEOUT); HttpConnectionParams.setSoTimeout(httpParams, SOCKET_TIMEOUT); httpClient = new DefaultHttpClient(httpParams); - Logger.error("init httpclient " + httpClient); } private void sendInternal(ActivityPackage activityPackage) { try { setUserAgent(activityPackage.userAgent); - HttpUriRequest request = activityPackage.getRequest(); + HttpUriRequest request = getRequest(activityPackage); HttpResponse response = httpClient.execute(request); requestFinished(response, activityPackage); } @@ -120,10 +128,10 @@ private void requestFinished(HttpResponse response, ActivityPackage activityPack if (statusCode == HttpStatus.SC_OK) { Logger.info(activityPackage.getSuccessMessage()); } else { - Logger.warn(activityPackage.getFailureMessage() + " (" + responseString + ")"); + Logger.error(String.format("%s. (%s)", activityPackage.getFailureMessage(), responseString)); } - queueHandler.sendNextPackage(); + packageHandler.sendNextPackage(); } private String parseResponse(HttpResponse response) { @@ -140,16 +148,14 @@ private String parseResponse(HttpResponse response) { } } - private void closePackage(ActivityPackage activityPackage, String message, Throwable e) { + private void closePackage(ActivityPackage activityPackage, String message, Throwable throwable) { String failureMessage = activityPackage.getFailureMessage(); - String logMessage = failureMessage + " Will retry later. (" + message; - if (e != null) { - logMessage += ": " + e; + if (throwable != null) { + Logger.error(String.format("%s. (%s: %s) Will retry later.", failureMessage, message, throwable)); + } else { + Logger.error(String.format("%s. (%s) Will retry later.", failureMessage, message)); } - logMessage += ")"; - Logger.error(logMessage); - - queueHandler.closeFirstPackage(); + packageHandler.closeFirstPackage(); } private void sendNextPackage(ActivityPackage activityPackage, String message, Throwable e) { @@ -161,11 +167,32 @@ private void sendNextPackage(ActivityPackage activityPackage, String message, Th logMessage += ")"; Logger.error(logMessage); - queueHandler.sendNextPackage(); + packageHandler.sendNextPackage(); } private void setUserAgent(String userAgent) { HttpParams httpParams = httpClient.getParams(); httpParams.setParameter(CoreProtocolPNames.USER_AGENT, userAgent); } + + private HttpUriRequest getRequest(ActivityPackage activityPackage) throws UnsupportedEncodingException { + String url = AdjustIo.BASE_URL + activityPackage.path; + HttpPost request = new HttpPost(url); + + String language = Locale.getDefault().getLanguage(); + request.addHeader("Accept-Language", language); + request.addHeader("Client-SDK", AdjustIo.CLIENT_SDK); + + List pairs = new ArrayList(); + for (Map.Entry entity : activityPackage.parameters.entrySet()) { + NameValuePair pair = new BasicNameValuePair(entity.getKey(), entity.getValue()); + pairs.add(pair); + } + + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(pairs); + entity.setContentType(URLEncodedUtils.CONTENT_TYPE); + request.setEntity(entity); + + return request; + } } From 0d4992ec5c4d8a23ce27560d7fe23ff5f38af890 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Fri, 5 Jul 2013 18:30:07 +0200 Subject: [PATCH 22/37] Improve logging, add clientSdk to packages --- .../com/adeven/adjustio/ActivityHandler.java | 20 +++++++++++------- .../com/adeven/adjustio/ActivityPackage.java | 21 ++++++++++++------- .../src/com/adeven/adjustio/AdjustIo.java | 2 -- .../com/adeven/adjustio/PackageBuilder.java | 17 +++++++++------ .../com/adeven/adjustio/PackageHandler.java | 10 +++------ .../com/adeven/adjustio/RequestHandler.java | 4 ++-- AdjustIo/src/com/adeven/adjustio/Util.java | 5 +++++ 7 files changed, 47 insertions(+), 32 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index 68371fafc..0691da27b 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -12,6 +12,7 @@ import java.io.OptionalDataException; import java.lang.ref.WeakReference; import java.util.Date; +import java.util.Locale; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -25,7 +26,7 @@ import android.os.Message; public class ActivityHandler extends HandlerThread { - private static final String SESSION_STATE_FILENAME = "activitystate1"; // TODO: change filename + private static final String SESSION_STATE_FILENAME = "activitystate2"; // TODO: change filename private static final long TIMER_INTERVAL = 1000 * 10; // 10 second, TODO: one minute private static final long SESSION_INTERVAL = 1000 * 15; // 15 seconds, TODO: 30 minutes @@ -49,6 +50,7 @@ public class ActivityHandler extends HandlerThread { private String androidId; // everything else here could be persisted private String fbAttributionId; private String userAgent; // changes, should be updated periodically + private String clientSdk; protected ActivityHandler(String appToken, Context context) { super(Logger.LOGTAG, MIN_PRIORITY); @@ -154,9 +156,10 @@ private void initInternal(String token) { appToken = token; macSha1 = Util.sha1(macAddress); macShortMd5 = macAddress.replaceAll(":", ""); // TODO: macMd5!!! - userAgent = Util.getUserAgent(context); androidId = Util.getAndroidId(context); fbAttributionId = Util.getAttributionId(context); + userAgent = Util.getUserAgent(context); + clientSdk = Util.CLIENT_SDK; packageHandler = new PackageHandler(context); readActivityState(); @@ -196,12 +199,15 @@ private void startInternal() { transferSessionPackage(); activityState.startNextSession(now); writeActivityState(); + Logger.debug(String.format(Locale.US, "Session %d", activityState.sessionCount)); return; } // new subsession if (lastInterval > SUBSESSION_INTERVAL) { activityState.subsessionCount++; + Logger.debug(String.format(Locale.US, "Subsession %d.%d", + activityState.sessionCount, activityState.subsessionCount)); } activityState.sessionLength += lastInterval; activityState.lastActivity = now; @@ -233,7 +239,7 @@ private void eventInternal(PackageBuilder eventBuilder) { packageHandler.addPackage(eventPackage); writeActivityState(); - try { Thread.sleep(500); } catch(Exception e) {} + Logger.debug(String.format(Locale.US, "Event %d", activityState.eventCount)); } private void revenueInternal(PackageBuilder revenueBuilder) { @@ -251,6 +257,7 @@ private void revenueInternal(PackageBuilder revenueBuilder) { packageHandler.addPackage(revenuePackage); writeActivityState(); + Logger.debug(String.format(Locale.US, "Event %d (revenue)", activityState.eventCount)); } // called from inside @@ -315,8 +322,6 @@ private void readActivityState() { // TODO: move to activityState? private void writeActivityState() { - try { Thread.sleep(300); } catch(Exception e) {} // TODO: remove sleeps! - try { FileOutputStream outputStream = context.openFileOutput(SESSION_STATE_FILENAME, Context.MODE_PRIVATE); BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); @@ -324,7 +329,7 @@ private void writeActivityState() { try { objectStream.writeObject(activityState); - Logger.debug("Wrote activity state: " + activityState); + Logger.verbose("Wrote activity state: " + activityState); } catch (NotSerializableException e) { Logger.error("Failed to serialize activity state"); @@ -350,12 +355,13 @@ private void transferSessionPackage() { // called from inside private void injectGeneralAttributes(PackageBuilder builder) { - builder.userAgent = userAgent; builder.appToken = appToken; builder.macShortMd5 = macShortMd5; builder.macSha1 = macSha1; builder.androidId = androidId; builder.fbAttributionId = fbAttributionId; + builder.userAgent = userAgent; + builder.clientSdk = clientSdk; } private void startTimer() { diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java b/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java index e240d8eaa..1668500e7 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java @@ -11,6 +11,7 @@ public class ActivityPackage implements Serializable { // data protected String path; protected String userAgent; + protected String clientSdk; protected Map parameters; // logs @@ -18,16 +19,20 @@ public class ActivityPackage implements Serializable { protected String suffix; public String toString() { - return String.format("%s%s %s", kind, suffix, path); + return String.format("%s%s", kind, suffix); } - protected String getParameterString() { - if (parameters == null) return "Parameters: null"; - - StringBuilder builder = new StringBuilder("Parameters:"); - - for (Map.Entry entity : parameters.entrySet()) { - builder.append(String.format("\n\t%-16s %s", entity.getKey(), entity.getValue())); + protected 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())); + } } return builder.toString(); } diff --git a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java index 9407c7827..029228725 100644 --- a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java +++ b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java @@ -23,8 +23,6 @@ * @since 11.10.12 */ public class AdjustIo { - protected static final String BASE_URL = "http://192.168.178.117:8509"; // TODO: change url! - protected static final String CLIENT_SDK = "android1.6"; // every call gets forwarded to activityHandler private static ActivityHandler activityHandler; diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java index a0612ba8e..640c524e4 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -17,6 +17,7 @@ public class PackageBuilder { protected String androidId; protected String fbAttributionId; protected String userAgent; + protected String clientSdk; // sessions protected int sessionCount; @@ -35,12 +36,11 @@ public class PackageBuilder { protected ActivityPackage buildSessionPackage() { Map parameters = getDefaultParameters(); - ActivityPackage sessionPackage = new ActivityPackage(); + ActivityPackage sessionPackage = getDefaultActivityPackage(); sessionPackage.path = "/startup"; sessionPackage.kind = "session start"; sessionPackage.suffix = ""; sessionPackage.parameters = parameters; - sessionPackage.userAgent = userAgent; return sessionPackage; } @@ -49,12 +49,11 @@ protected ActivityPackage buildEventPackage() { Map parameters = getDefaultParameters(); injectEventParameters(parameters); - ActivityPackage eventPackage = new ActivityPackage(); + ActivityPackage eventPackage = getDefaultActivityPackage(); eventPackage.path = "/event"; eventPackage.kind = "event"; eventPackage.suffix = getEventSuffix(); eventPackage.parameters = parameters; - eventPackage.userAgent = userAgent; return eventPackage; } @@ -64,16 +63,22 @@ protected ActivityPackage buildRevenuePackage() { injectEventParameters(parameters); addString(parameters, "amount", getAmountString()); - ActivityPackage revenuePackage = new ActivityPackage(); + ActivityPackage revenuePackage = getDefaultActivityPackage(); revenuePackage.path = "/revenue"; revenuePackage.kind = "revenue"; revenuePackage.suffix = getRevenueSuffix(); revenuePackage.parameters = parameters; - revenuePackage.userAgent = userAgent; return revenuePackage; } + private ActivityPackage getDefaultActivityPackage() { + ActivityPackage activityPackage = new ActivityPackage(); + activityPackage.userAgent = userAgent; + activityPackage.clientSdk = clientSdk; + return activityPackage; + } + private Map getDefaultParameters() { Map parameters = new HashMap(); diff --git a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java index e6bb48f06..d6a297d32 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java @@ -135,7 +135,7 @@ private void initInternal() { private void addInternal(ActivityPackage newPackage) { packageQueue.add(newPackage); Logger.debug(String.format(Locale.US, "Added package %d (%s)", packageQueue.size(), newPackage)); - Logger.verbose(newPackage.getParameterString()); + Logger.verbose(newPackage.getExtendedString()); writePackageQueue(); sendFirstInternal(); @@ -149,7 +149,7 @@ private void sendFirstInternal() { return; } if (isSending.getAndSet(true)) { - Logger.debug("Package handler is already sending"); + Logger.verbose("Package handler is already sending"); return; } @@ -203,10 +203,6 @@ private void readPackageQueue() { } private void writePackageQueue() { - try { // TODO: remove sleeps - Thread.sleep(100); - } catch (Exception e) {} - try { FileOutputStream outputStream = context.openFileOutput(PACKAGE_QUEUE_FILENAME, Context.MODE_PRIVATE); BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); @@ -214,7 +210,7 @@ private void writePackageQueue() { try { objectStream.writeObject(packageQueue); - Logger.verbose(String.format(Locale.US, "Package handler wrote %d packages", packageQueue.size())); + Logger.debug(String.format(Locale.US, "Package handler wrote %d packages", packageQueue.size())); } catch (NotSerializableException e) { Logger.error("Failed to serialize packages"); diff --git a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java index 20fb53d62..b6e3415c7 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java @@ -176,12 +176,12 @@ private void setUserAgent(String userAgent) { } private HttpUriRequest getRequest(ActivityPackage activityPackage) throws UnsupportedEncodingException { - String url = AdjustIo.BASE_URL + activityPackage.path; + String url = Util.BASE_URL + activityPackage.path; HttpPost request = new HttpPost(url); String language = Locale.getDefault().getLanguage(); request.addHeader("Accept-Language", language); - request.addHeader("Client-SDK", AdjustIo.CLIENT_SDK); + request.addHeader("Client-SDK", activityPackage.clientSdk); // TODO: set userAgent like this? List pairs = new ArrayList(); for (Map.Entry entity : activityPackage.parameters.entrySet()) { diff --git a/AdjustIo/src/com/adeven/adjustio/Util.java b/AdjustIo/src/com/adeven/adjustio/Util.java index eb8a54486..ba7395467 100644 --- a/AdjustIo/src/com/adeven/adjustio/Util.java +++ b/AdjustIo/src/com/adeven/adjustio/Util.java @@ -30,6 +30,8 @@ import android.text.TextUtils; import android.util.DisplayMetrics; + + // TODO: clean up, move functions to callers /** * Collects utility functions used by AdjustIo. @@ -38,6 +40,9 @@ * @since 11.10.12 */ public class Util { + protected static final String BASE_URL = "http://192.168.178.117:8509"; // TODO: change url! + protected static final String CLIENT_SDK = "android1.6"; + private static final String UNKNOWN = "unknown"; protected static String getUserAgent(Context context) { From 1a142087e8b1d8d73f636cd7c6b3a0d6aeeac380 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Mon, 8 Jul 2013 11:58:04 +0200 Subject: [PATCH 23/37] Use md5 of mac --- .../com/adeven/adjustio/ActivityHandler.java | 33 ++++++---------- AdjustIo/src/com/adeven/adjustio/Util.java | 38 ++++++++----------- 2 files changed, 28 insertions(+), 43 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index 0691da27b..99303b800 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -152,10 +152,11 @@ private void initInternal(String token) { if (!checkPermissions(context)) return; String macAddress = Util.getMacAddress(context); + String macShort = macAddress.replaceAll(":", ""); appToken = token; macSha1 = Util.sha1(macAddress); - macShortMd5 = macAddress.replaceAll(":", ""); // TODO: macMd5!!! + macShortMd5 = Util.md5(macShort); androidId = Util.getAndroidId(context); fbAttributionId = Util.getAttributionId(context); userAgent = Util.getUserAgent(context); @@ -260,17 +261,13 @@ private void revenueInternal(PackageBuilder revenueBuilder) { Logger.debug(String.format(Locale.US, "Event %d (revenue)", activityState.eventCount)); } - // called from inside - - // called from inside - private void updateActivityState() { if (!checkActivityState(activityState)) return; long now = new Date().getTime(); long lastInterval = now - activityState.lastActivity; if (lastInterval < 0) { - Logger.error("Time travel"); + Logger.error("Time travel!"); activityState.lastActivity = now; return; } @@ -291,7 +288,7 @@ private void readActivityState() { try { activityState = (ActivityState) objectStream.readObject(); - Logger.debug("Read activity state: " + activityState); + Logger.debug(String.format("Read activity state: %s", activityState)); return; } catch (ClassNotFoundException e) { @@ -329,7 +326,7 @@ private void writeActivityState() { try { objectStream.writeObject(activityState); - Logger.verbose("Wrote activity state: " + activityState); + Logger.verbose(String.format("Wrote activity state: %s", activityState)); } catch (NotSerializableException e) { Logger.error("Failed to serialize activity state"); @@ -340,7 +337,7 @@ private void writeActivityState() { } catch (IOException e) { - Logger.error("Failed to write activity state (" + e + ")"); + Logger.error(String.format("Failed to write activity state (%s)", e)); } } @@ -396,17 +393,11 @@ private static boolean checkPermissions(Context context) { boolean result = true; if (!checkPermission(context, android.Manifest.permission.INTERNET)) { - Logger.error( - "This SDK requires the INTERNET permission. " + - "See the README for details." - ); + Logger.error("Missing permission: INTERNET"); result = false; } if (!checkPermission(context, android.Manifest.permission.ACCESS_WIFI_STATE)) { - Logger.warn( - "You can improve your tracking results by adding the " + - "ACCESS_WIFI_STATE permission. See the README for details." - ); + Logger.warn("Missing permission: ACCESS_WIFI_STATE"); } return result; @@ -414,7 +405,7 @@ private static boolean checkPermissions(Context context) { private static boolean checkContext(Context context) { if (context == null) { - Logger.error("Missing context."); + Logger.error("Missing context"); return false; } return true; @@ -444,7 +435,7 @@ private static boolean checkAppTokenNotNull(String appToken) { private static boolean checkAppTokenLength(String appToken) { if (appToken.length() != 12) { - Logger.error("Malformed App Token '" + appToken + "'"); + Logger.error(String.format("Malformed App Token '%s'", appToken)); return false; } return true; @@ -463,7 +454,7 @@ private static boolean checkEventTokenLength(String eventToken) { return true; if (eventToken.length() != 6) { - Logger.error("Malformed Event Token '" + eventToken + "'"); + Logger.error(String.format("Malformed Event Token '%s'", eventToken)); return false; } return true; @@ -471,7 +462,7 @@ private static boolean checkEventTokenLength(String eventToken) { private static boolean checkAmount(float amount) { if (amount <= 0.0f) { - Logger.error("Invalid amount " + amount); + Logger.error(String.format(Locale.US, "Invalid amount %f", amount)); return false; } return true; diff --git a/AdjustIo/src/com/adeven/adjustio/Util.java b/AdjustIo/src/com/adeven/adjustio/Util.java index ba7395467..7bc701e98 100644 --- a/AdjustIo/src/com/adeven/adjustio/Util.java +++ b/AdjustIo/src/com/adeven/adjustio/Util.java @@ -12,6 +12,7 @@ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; +import java.math.BigInteger; import java.security.MessageDigest; import java.util.Locale; @@ -297,35 +298,28 @@ protected static String getAttributionId(Context context) { } protected static String sha1(String text) { + return hash(text, "SHA-1"); + } + + protected static String md5(String text) { + return hash(text, "MD5"); + } + + private static String hash(String text, String method) { try { - MessageDigest mesd = MessageDigest.getInstance("SHA-1"); - byte[] bytes = text.getBytes("iso-8859-1"); + byte[] bytes = text.getBytes("UTF-8"); + MessageDigest mesd = MessageDigest.getInstance(method); mesd.update(bytes, 0, bytes.length); - byte[] sha2hash = mesd.digest(); - return convertToHex(sha2hash); + byte[] hash = mesd.digest(); + return convertToHex(hash); } catch (Exception e) { return ""; } } private static String convertToHex(byte[] bytes) { - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < bytes.length; i++) { - int halfbyte = (bytes[i] >>> 4) & 0x0F; - int two_halfs = 0; - - do { - if ((0 <= halfbyte) && (halfbyte <= 9)) { - buffer.append((char) ('0' + halfbyte)); - } else { - buffer.append((char) ('a' + (halfbyte - 10))); - } - - halfbyte = bytes[i] & 0x0F; - } while (two_halfs++ < 1); - } - - String hex = buffer.toString(); - return hex; + BigInteger bigInt = new BigInteger(1, bytes); + String formatString = "%0" + (bytes.length << 1) + "x"; + return String.format(formatString, bigInt); } } From 624fd7e5a2d145d65e2075e95b09563533a0623e Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Mon, 8 Jul 2013 14:24:45 +0200 Subject: [PATCH 24/37] Format createdAt timestamp --- .../com/adeven/adjustio/ActivityHandler.java | 33 ++++++++++--------- .../com/adeven/adjustio/ActivityState.java | 17 +++++----- .../com/adeven/adjustio/PackageBuilder.java | 16 +++++++-- 3 files changed, 40 insertions(+), 26 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index 99303b800..cd7cc3961 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -196,8 +196,10 @@ private void startInternal() { // new session if (lastInterval > SESSION_INTERVAL) { + activityState.createdAt = now; activityState.lastInterval = lastInterval; transferSessionPackage(); + activityState.startNextSession(now); writeActivityState(); Logger.debug(String.format(Locale.US, "Session %d", activityState.sessionCount)); @@ -231,31 +233,30 @@ private void eventInternal(PackageBuilder eventBuilder) { if (!checkEventTokenNotNull(eventBuilder.eventToken)) return; if (!checkEventTokenLength(eventBuilder.eventToken)) return; - activityState.eventCount++; + long now = new Date().getTime(); updateActivityState(); - injectGeneralAttributes(eventBuilder); - activityState.injectEventAttributes(eventBuilder); + activityState.createdAt = now; + activityState.eventCount++; - ActivityPackage eventPackage = eventBuilder.buildEventPackage(); - packageHandler.addPackage(eventPackage); + transferEventPackage(eventBuilder); writeActivityState(); Logger.debug(String.format(Locale.US, "Event %d", activityState.eventCount)); } + private void revenueInternal(PackageBuilder revenueBuilder) { if (!checkAppTokenNotNull(appToken)) return; if (!checkActivityState(activityState)) return; if (!checkAmount(revenueBuilder.amountInCents)) return; if (!checkEventTokenLength(revenueBuilder.eventToken)) return; + long now = new Date().getTime(); + activityState.createdAt = now; activityState.eventCount++; updateActivityState(); - injectGeneralAttributes(revenueBuilder); - activityState.injectEventAttributes(revenueBuilder); - ActivityPackage revenuePackage = revenueBuilder.buildRevenuePackage(); - packageHandler.addPackage(revenuePackage); + transferEventPackage(revenueBuilder); writeActivityState(); Logger.debug(String.format(Locale.US, "Event %d (revenue)", activityState.eventCount)); @@ -349,7 +350,12 @@ private void transferSessionPackage() { packageHandler.addPackage(sessionPackage); } - // called from inside + private void transferEventPackage(PackageBuilder eventBuilder) { + injectGeneralAttributes(eventBuilder); + activityState.injectEventAttributes(eventBuilder); + ActivityPackage eventPackage = eventBuilder.buildEventPackage(); + packageHandler.addPackage(eventPackage); + } private void injectGeneralAttributes(PackageBuilder builder) { builder.appToken = appToken; @@ -467,9 +473,4 @@ private static boolean checkAmount(float amount) { } return true; } -} - - - - -// TODO: remove trailing lines +} \ No newline at end of file diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityState.java b/AdjustIo/src/com/adeven/adjustio/ActivityState.java index 9229eb7eb..655894047 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityState.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityState.java @@ -15,10 +15,11 @@ public class ActivityState implements Serializable { // session attributes protected int subsessionCount; - protected long sessionLength; // all durations in milliseconds + protected long sessionLength; // all durations in milliseconds protected long timeSpent; - protected long createdAt; // all times in milliseconds since 1970 - protected long lastActivity; + protected long lastActivity; // all times in milliseconds since 1970 + + protected long createdAt; protected long lastInterval; protected ActivityState() { @@ -27,8 +28,8 @@ protected ActivityState() { subsessionCount = -1; // we don't know how many subssessions 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 - createdAt = -1; lastActivity = -1; + createdAt = -1; lastInterval = -1; } @@ -37,9 +38,9 @@ protected void startNextSession(long now) { subsessionCount = 1; // first subsession sessionLength = 0; // no session length yet timeSpent = 0; // no time spent yet - createdAt = now; lastActivity = now; - lastInterval = 0; + createdAt = -1; + lastInterval = -1; } protected void injectSessionAttributes(PackageBuilder builder) { @@ -54,9 +55,9 @@ protected void injectEventAttributes(PackageBuilder builder) { public String toString() { return String.format(Locale.US, - "ec:%d sc:%d ssc:%d sl:%d ts:%d ca:%s la:%s", + "ec:%d sc:%d ssc:%d sl:%d ts:%d la:%s", eventCount, sessionCount, subsessionCount, sessionLength, - timeSpent, stamp(createdAt), stamp(lastActivity)); + timeSpent, stamp(lastActivity)); } private static String stamp(long dateMillis) { diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java index 640c524e4..25ac14a99 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -1,5 +1,6 @@ package com.adeven.adjustio; +import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Locale; @@ -10,6 +11,9 @@ import android.util.Base64; public class PackageBuilder { + + private static String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'Z"; + // general protected String appToken; protected String macSha1; @@ -33,6 +37,8 @@ public class PackageBuilder { protected float amountInCents; protected Map callbackParameters; + private static SimpleDateFormat dateFormat; + protected ActivityPackage buildSessionPackage() { Map parameters = getDefaultParameters(); @@ -142,8 +148,7 @@ private void addDate(Map parameters, String key, long value) { if (value < 0) return; Date date = new Date(value); - String dateString = date.toString(); // TODO: format with DateFormat - // TODO: nope, lets send the time since 1970 in rounded seconds (unix timestamp) + String dateString = getDateFormat().format(date); addString(parameters, key, dateString); } @@ -164,4 +169,11 @@ private void addMap(Map parameters, String key, Map Date: Mon, 8 Jul 2013 15:24:38 +0200 Subject: [PATCH 25/37] Set UserAgent of request instead of client, clean up --- .../com/adeven/adjustio/ActivityHandler.java | 8 +++---- .../com/adeven/adjustio/ActivityState.java | 2 -- .../com/adeven/adjustio/PackageHandler.java | 2 -- .../com/adeven/adjustio/RequestHandler.java | 24 +++++++------------ 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index cd7cc3961..9744d603f 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -41,15 +41,15 @@ public class ActivityHandler extends HandlerThread { private InternalHandler internalHandler; private PackageHandler packageHandler; private ActivityState activityState; - private static ScheduledExecutorService timer; // TODO: rename to timer + private static ScheduledExecutorService timer; private Context context; private String appToken; private String macSha1; - private String macShortMd5; // TODO: md5!!! - private String androidId; // everything else here could be persisted + 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 userAgent; // changes, should be updated periodically private String clientSdk; protected ActivityHandler(String appToken, Context context) { diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityState.java b/AdjustIo/src/com/adeven/adjustio/ActivityState.java index 655894047..02336810e 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityState.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityState.java @@ -7,8 +7,6 @@ public class ActivityState implements Serializable { private static final long serialVersionUID = 9039439291143138148L; - // TODO: make attributes private? - // global counters protected int eventCount; protected int sessionCount; diff --git a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java index d6a297d32..66d0ded18 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java @@ -22,8 +22,6 @@ import android.os.Looper; import android.os.Message; -// TODO: user String.format everywhere instead of "a" + "b" - // persistent public class PackageHandler extends HandlerThread { private static final String PACKAGE_QUEUE_FILENAME = "testqueue6"; // TODO: change filename diff --git a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java index b6e3415c7..262f51dd4 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java @@ -22,7 +22,6 @@ import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.CoreProtocolPNames; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; @@ -99,7 +98,6 @@ private void initInternal() { private void sendInternal(ActivityPackage activityPackage) { try { - setUserAgent(activityPackage.userAgent); HttpUriRequest request = getRequest(activityPackage); HttpResponse response = httpClient.execute(request); requestFinished(response, activityPackage); @@ -143,8 +141,8 @@ private String parseResponse(HttpResponse response) { return responseString; } catch (Exception e) { - Logger.error("Error parsing response: " + e); - return "Failed parsing response"; + Logger.error(String.format("Failed to parse response (%s)", e)); + return "Failed to parse response"; } } @@ -158,30 +156,26 @@ private void closePackage(ActivityPackage activityPackage, String message, Throw packageHandler.closeFirstPackage(); } - private void sendNextPackage(ActivityPackage activityPackage, String message, Throwable e) { + private void sendNextPackage(ActivityPackage activityPackage, String message, Throwable throwable) { String failureMessage = activityPackage.getFailureMessage(); - String logMessage = failureMessage + " (" + message; - if (e != null) { - logMessage += ": " + e; + if (throwable != null) { + Logger.error(String.format("%s (%s: %s)", failureMessage, message, throwable)); + } else { + Logger.error(String.format("%s (%s)", failureMessage, message)); } - logMessage += ")"; - Logger.error(logMessage); packageHandler.sendNextPackage(); } - private void setUserAgent(String userAgent) { - HttpParams httpParams = httpClient.getParams(); - httpParams.setParameter(CoreProtocolPNames.USER_AGENT, userAgent); - } private HttpUriRequest getRequest(ActivityPackage activityPackage) throws UnsupportedEncodingException { String url = Util.BASE_URL + activityPackage.path; HttpPost request = new HttpPost(url); String language = Locale.getDefault().getLanguage(); + request.addHeader("User-Agent", activityPackage.userAgent); + request.addHeader("Client-SDK", activityPackage.clientSdk); request.addHeader("Accept-Language", language); - request.addHeader("Client-SDK", activityPackage.clientSdk); // TODO: set userAgent like this? List pairs = new ArrayList(); for (Map.Entry entity : activityPackage.parameters.entrySet()) { From 323a739497f97449f75221ce49429ae770206457 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Tue, 9 Jul 2013 10:51:59 +0200 Subject: [PATCH 26/37] Use double instead of float for revenue --- .../com/adeven/adjustio/ActivityHandler.java | 41 ++++++++----------- .../com/adeven/adjustio/ActivityPackage.java | 2 +- .../src/com/adeven/adjustio/AdjustIo.java | 8 ++-- AdjustIo/src/com/adeven/adjustio/Logger.java | 4 -- .../com/adeven/adjustio/PackageBuilder.java | 16 ++++---- .../com/adeven/adjustio/PackageHandler.java | 4 +- .../com/adeven/adjustio/RequestHandler.java | 4 +- AdjustIo/src/com/adeven/adjustio/Util.java | 4 +- 8 files changed, 34 insertions(+), 49 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index 9744d603f..7be72c080 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -26,10 +26,10 @@ import android.os.Message; public class ActivityHandler extends HandlerThread { - private static final String SESSION_STATE_FILENAME = "activitystate2"; // TODO: change filename + private static final String SESSION_STATE_FILENAME = "activitystate2"; // TODO: filename - private static final long TIMER_INTERVAL = 1000 * 10; // 10 second, TODO: one minute - private static final long SESSION_INTERVAL = 1000 * 15; // 15 seconds, TODO: 30 minutes + private static final long TIMER_INTERVAL = 1000 * 10; // 10 second, TODO: time 1 minute + private static final long SESSION_INTERVAL = 1000 * 15; // 15 seconds, TODO: time 30 minutes private static final long SUBSESSION_INTERVAL = 1000 * 1; // one second private static final int MESSAGE_ARG_INIT = 72630; @@ -89,7 +89,7 @@ protected void trackEvent(String eventToken, Map parameters) { internalHandler.sendMessage(message); } - protected void trackRevenue(float amountInCents, String eventToken, Map parameters) { + protected void trackRevenue(double amountInCents, String eventToken, Map parameters) { PackageBuilder builder = new PackageBuilder(); builder.amountInCents = amountInCents; builder.eventToken = eventToken; @@ -138,13 +138,6 @@ public void handleMessage(Message message) { } } - // TODO: rename internal methods? - // TODO: remove internal for all methods without external part - // TODO: move internal methods up to the public interface? - // like foo(), fooInternal(), bar(), barInternal(), etc. - - // called from outside - private void initInternal(String token) { if (!checkAppTokenNotNull(token)) return; if (!checkAppTokenLength(token)) return; @@ -234,11 +227,14 @@ private void eventInternal(PackageBuilder eventBuilder) { if (!checkEventTokenLength(eventBuilder.eventToken)) return; long now = new Date().getTime(); - updateActivityState(); activityState.createdAt = now; activityState.eventCount++; + updateActivityState(); - transferEventPackage(eventBuilder); + injectGeneralAttributes(eventBuilder); + activityState.injectEventAttributes(eventBuilder); + ActivityPackage eventPackage = eventBuilder.buildEventPackage(); + packageHandler.addPackage(eventPackage); writeActivityState(); Logger.debug(String.format(Locale.US, "Event %d", activityState.eventCount)); @@ -256,7 +252,10 @@ private void revenueInternal(PackageBuilder revenueBuilder) { activityState.eventCount++; updateActivityState(); - transferEventPackage(revenueBuilder); + injectGeneralAttributes(revenueBuilder); + activityState.injectEventAttributes(revenueBuilder); + ActivityPackage eventPackage = revenueBuilder.buildRevenuePackage(); + packageHandler.addPackage(eventPackage); writeActivityState(); Logger.debug(String.format(Locale.US, "Event %d (revenue)", activityState.eventCount)); @@ -318,7 +317,6 @@ private void readActivityState() { activityState = null; } - // TODO: move to activityState? private void writeActivityState() { try { FileOutputStream outputStream = context.openFileOutput(SESSION_STATE_FILENAME, Context.MODE_PRIVATE); @@ -350,13 +348,6 @@ private void transferSessionPackage() { packageHandler.addPackage(sessionPackage); } - private void transferEventPackage(PackageBuilder eventBuilder) { - injectGeneralAttributes(eventBuilder); - activityState.injectEventAttributes(eventBuilder); - ActivityPackage eventPackage = eventBuilder.buildEventPackage(); - packageHandler.addPackage(eventPackage); - } - private void injectGeneralAttributes(PackageBuilder builder) { builder.appToken = appToken; builder.macShortMd5 = macShortMd5; @@ -466,11 +457,11 @@ private static boolean checkEventTokenLength(String eventToken) { return true; } - private static boolean checkAmount(float amount) { - if (amount <= 0.0f) { + private static boolean checkAmount(double amount) { + if (amount <= 0.0) { Logger.error(String.format(Locale.US, "Invalid amount %f", amount)); return false; } return true; } -} \ No newline at end of file +} diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java b/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java index 1668500e7..20f06f0dc 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java @@ -1,4 +1,4 @@ -// TODO: add header comments +// TODO: comments: add header comments package com.adeven.adjustio; diff --git a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java index 029228725..bd35ecc4d 100644 --- a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java +++ b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java @@ -89,20 +89,20 @@ public static void trackEvent(String eventToken, Map parameters) * callbacks, you can also pass in parameters that will be forwarded to your * server. * - * @param amountInCents The amount in cents (example: 1.5f means one and a half cents) + * @param amountInCents The amount in cents (example: 1.5 means one and a half cents) * @param eventToken The token for this revenue event (see above) * @param parameters Parameters for this revenue event (see above) */ - public static void trackRevenue(float amountInCents) { + public static void trackRevenue(double amountInCents) { AdjustIo.trackRevenue(amountInCents, null); } - public static void trackRevenue(float amountInCents, String eventToken) { + public static void trackRevenue(double amountInCents, String eventToken) { AdjustIo.trackRevenue(amountInCents, eventToken, null); } - public static void trackRevenue(float amountInCents, String eventToken, Map parameters) { + public static void trackRevenue(double amountInCents, String eventToken, Map parameters) { try { activityHandler.trackRevenue(amountInCents, eventToken, parameters); } catch (NullPointerException e) { diff --git a/AdjustIo/src/com/adeven/adjustio/Logger.java b/AdjustIo/src/com/adeven/adjustio/Logger.java index 185515a4d..774f32d98 100644 --- a/AdjustIo/src/com/adeven/adjustio/Logger.java +++ b/AdjustIo/src/com/adeven/adjustio/Logger.java @@ -2,10 +2,6 @@ import android.util.Log; -// TODO: avoid string building in production? -// http://stackoverflow.com/questions/2018263/android-logging -// TODO: go through all log calls and set a proper log level - public class Logger { protected static final String LOGTAG = "AdjustIo"; diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java index 25ac14a99..2b3dd8aed 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -34,13 +34,14 @@ public class PackageBuilder { // events protected int eventCount; protected String eventToken; - protected float amountInCents; + protected double amountInCents; protected Map callbackParameters; private static SimpleDateFormat dateFormat; protected ActivityPackage buildSessionPackage() { Map parameters = getDefaultParameters(); + addDuration(parameters, "last_interval", lastInterval); ActivityPackage sessionPackage = getDefaultActivityPackage(); sessionPackage.path = "/startup"; @@ -92,30 +93,29 @@ private Map getDefaultParameters() { addDate(parameters, "created_at", createdAt); addString(parameters, "app_token", appToken); addString(parameters, "mac_sha1", macSha1); - addString(parameters, "mac", macShortMd5); // TODO: rename parameter + addString(parameters, "mac", macShortMd5); // TODO: parameters addString(parameters, "android_id", androidId); addString(parameters, "fb_id", fbAttributionId); // session related (used for events as well) - addInt(parameters, "session_id", sessionCount); // TODO: rename parameters + addInt(parameters, "session_count", sessionCount); addInt(parameters, "subsession_count", subsessionCount); addDuration(parameters, "session_length", sessionLength); addDuration(parameters, "time_spent", timeSpent); - addDuration(parameters, "last_interval", lastInterval); return parameters; } private void injectEventParameters(Map parameters) { addInt(parameters, "event_count", eventCount); - addString(parameters, "event_id", eventToken); // TODO: rename parameters + addString(parameters, "event_id", eventToken); // TODO: parameters addMap(parameters, "params", callbackParameters); } private String getAmountString() { - int amountInMillis = Math.round(10 * amountInCents); - amountInCents = amountInMillis / 10.0f; // now rounded to one decimal point - String amountString = Integer.toString(amountInMillis); + long amountInMillis = Math.round(10 * amountInCents); + amountInCents = amountInMillis / 10.0; // now rounded to one decimal point + String amountString = Long.toString(amountInMillis); return amountString; } diff --git a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java index 66d0ded18..2be58eea8 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java @@ -24,9 +24,9 @@ // persistent public class PackageHandler extends HandlerThread { - private static final String PACKAGE_QUEUE_FILENAME = "testqueue6"; // TODO: change filename + private static final String PACKAGE_QUEUE_FILENAME = "testqueue6"; // TODO: filename - private static final int MESSAGE_ARG_INIT = 72501; // TODO: change constants! + private static final int MESSAGE_ARG_INIT = 72501; // TODO: constants private static final int MESSAGE_ARG_ADD = 72500; private static final int MESSAGE_ARG_SEND_NEXT = 72510; private static final int MESSAGE_ARG_SEND_FIRST = 72530; diff --git a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java index 262f51dd4..48a338f6d 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java @@ -31,8 +31,8 @@ import android.os.Message; public class RequestHandler extends HandlerThread { - private static final int CONNECTION_TIMEOUT = 1000 * 5; // 5 seconds TODO: 1 minute? - private static final int SOCKET_TIMEOUT = 1000 * 5; // 5 seconds TODO: 1 minute? + private static final int CONNECTION_TIMEOUT = 1000 * 5; // 5 seconds TODO: time 1 minute + private static final int SOCKET_TIMEOUT = 1000 * 5; // 5 seconds TODO: time 1 minute private static final int MESSAGE_ARG_INIT = 72401; private static final int MESSAGE_ARG_SEND = 72400; diff --git a/AdjustIo/src/com/adeven/adjustio/Util.java b/AdjustIo/src/com/adeven/adjustio/Util.java index 7bc701e98..6c63948c7 100644 --- a/AdjustIo/src/com/adeven/adjustio/Util.java +++ b/AdjustIo/src/com/adeven/adjustio/Util.java @@ -32,8 +32,6 @@ import android.util.DisplayMetrics; - -// TODO: clean up, move functions to callers /** * Collects utility functions used by AdjustIo. * @@ -41,7 +39,7 @@ * @since 11.10.12 */ public class Util { - protected static final String BASE_URL = "http://192.168.178.117:8509"; // TODO: change url! + protected static final String BASE_URL = "http://192.168.178.117:8509"; // TODO: url protected static final String CLIENT_SDK = "android1.6"; private static final String UNKNOWN = "unknown"; From eaa47be9a87642f102f66f63301d4e453a825824 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Tue, 9 Jul 2013 11:21:14 +0200 Subject: [PATCH 27/37] Log intervals in seconds --- AdjustIo/src/com/adeven/adjustio/ActivityState.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityState.java b/AdjustIo/src/com/adeven/adjustio/ActivityState.java index 02336810e..5ab2572c5 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityState.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityState.java @@ -53,9 +53,10 @@ protected void injectEventAttributes(PackageBuilder builder) { public String toString() { return String.format(Locale.US, - "ec:%d sc:%d ssc:%d sl:%d ts:%d la:%s", - eventCount, sessionCount, subsessionCount, sessionLength, - timeSpent, stamp(lastActivity)); + "ec:%d sc:%d ssc:%d sl:%.1f ts:%.1f la:%s", + eventCount, sessionCount, subsessionCount, + sessionLength / 1000.0, timeSpent / 1000.0, + stamp(lastActivity)); } private static String stamp(long dateMillis) { From 43d875c3a31041292ec26e2b64079938bbdffab7 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Tue, 9 Jul 2013 16:38:11 +0200 Subject: [PATCH 28/37] Mave MessageArg constants in InternalHandlers --- .../com/adeven/adjustio/ActivityHandler.java | 38 +++++++++---------- .../com/adeven/adjustio/PackageBuilder.java | 24 ++++++------ .../com/adeven/adjustio/PackageHandler.java | 26 ++++++------- .../com/adeven/adjustio/RequestHandler.java | 14 +++---- 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index 7be72c080..6da4dda50 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -28,15 +28,9 @@ public class ActivityHandler extends HandlerThread { private static final String SESSION_STATE_FILENAME = "activitystate2"; // TODO: filename - private static final long TIMER_INTERVAL = 1000 * 10; // 10 second, TODO: time 1 minute - private static final long SESSION_INTERVAL = 1000 * 15; // 15 seconds, TODO: time 30 minutes - private static final long SUBSESSION_INTERVAL = 1000 * 1; // one second - - private static final int MESSAGE_ARG_INIT = 72630; - private static final int MESSAGE_ARG_START = 72640; - private static final int MESSAGE_ARG_END = 72650; - private static final int MESSAGE_ARG_EVENT = 72660; - private static final int MESSAGE_ARG_REVENUE = 72670; + private static final long TIMER_INTERVAL = 1000 * 3; // 10 second, TODO: time 1 minute + private static final long SESSION_INTERVAL = 1000 * 5; // 15 seconds, TODO: time 30 minutes + private static final long SUBSESSION_INTERVAL = 1000 * 1; // one second private InternalHandler internalHandler; private PackageHandler packageHandler; @@ -61,20 +55,20 @@ protected ActivityHandler(String appToken, Context context) { this.context = context; Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_INIT; + message.arg1 = InternalHandler.INIT; message.obj = appToken; internalHandler.sendMessage(message); } protected void trackSubsessionStart() { Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_START; + message.arg1 = InternalHandler.START; internalHandler.sendMessage(message); } protected void trackSubsessionEnd() { Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_END; + message.arg1 = InternalHandler.END; internalHandler.sendMessage(message); } @@ -84,7 +78,7 @@ protected void trackEvent(String eventToken, Map parameters) { builder.callbackParameters = parameters; Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_EVENT; + message.arg1 = InternalHandler.EVENT; message.obj = builder; internalHandler.sendMessage(message); } @@ -96,12 +90,18 @@ protected void trackRevenue(double amountInCents, String eventToken, Map sessionHandlerReference; protected InternalHandler(Looper looper, ActivityHandler sessionHandler) { @@ -116,21 +116,21 @@ public void handleMessage(Message message) { if (sessionHandler == null) return; switch (message.arg1) { - case MESSAGE_ARG_INIT: + case INIT: String appToken = (String) message.obj; sessionHandler.initInternal(appToken); break; - case MESSAGE_ARG_START: + case START: sessionHandler.startInternal(); break; - case MESSAGE_ARG_END: + case END: sessionHandler.endInternal(); break; - case MESSAGE_ARG_EVENT: + case EVENT: PackageBuilder eventBuilder = (PackageBuilder) message.obj; sessionHandler.eventInternal(eventBuilder); break; - case MESSAGE_ARG_REVENUE: + case REVENUE: PackageBuilder revenueBuilder = (PackageBuilder) message.obj; sessionHandler.revenueInternal(revenueBuilder); break; diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java index 2b3dd8aed..e99447a6b 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -90,26 +90,26 @@ 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", macShortMd5); // TODO: parameters + 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, "fb_id", fbAttributionId); + addString(parameters, "fb_id", fbAttributionId); // 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); + addInt(parameters, "session_count", sessionCount); + addInt(parameters, "subsession_count", subsessionCount); + addDuration(parameters, "session_length", sessionLength); + addDuration(parameters, "time_spent", timeSpent); return parameters; } private void injectEventParameters(Map parameters) { - addInt(parameters, "event_count", eventCount); - addString(parameters, "event_id", eventToken); // TODO: parameters - addMap(parameters, "params", callbackParameters); + addInt(parameters, "event_count", eventCount); + addString(parameters, "event_token", eventToken); + addMap(parameters, "params", callbackParameters); } private String getAmountString() { diff --git a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java index 2be58eea8..720a3dcc9 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java @@ -26,11 +26,6 @@ public class PackageHandler extends HandlerThread { private static final String PACKAGE_QUEUE_FILENAME = "testqueue6"; // TODO: filename - private static final int MESSAGE_ARG_INIT = 72501; // TODO: constants - private static final int MESSAGE_ARG_ADD = 72500; - private static final int MESSAGE_ARG_SEND_NEXT = 72510; - private static final int MESSAGE_ARG_SEND_FIRST = 72530; - private InternalHandler internalHandler; private RequestHandler requestHandler; private List packageQueue; @@ -47,14 +42,14 @@ protected PackageHandler(Context context) { this.context = context; Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_INIT; + message.arg1 = InternalHandler.INIT; internalHandler.sendMessage(message); } // add a package to the queue, trigger sending protected void addPackage(ActivityPackage pack) { Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_ADD; + message.arg1 = InternalHandler.ADD; message.obj = pack; internalHandler.sendMessage(message); } @@ -62,7 +57,7 @@ protected void addPackage(ActivityPackage pack) { // try to send the oldest package protected void sendFirstPackage() { Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_SEND_FIRST; + message.arg1 = InternalHandler.SEND_FIRST; internalHandler.sendMessage(message); } @@ -70,7 +65,7 @@ protected void sendFirstPackage() { // (after success or possibly permanent failure) protected void sendNextPackage() { Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_SEND_NEXT; + message.arg1 = InternalHandler.SEND_NEXT; internalHandler.sendMessage(message); } @@ -90,6 +85,11 @@ protected void resumeSending() { } 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 SEND_FIRST = 4; + private final WeakReference packageHandlerReference; protected InternalHandler(Looper looper, PackageHandler packageHandler) { @@ -104,17 +104,17 @@ public void handleMessage(Message message) { if (packageHandler == null) return; switch (message.arg1) { - case MESSAGE_ARG_INIT: + case INIT: packageHandler.initInternal(); break; - case MESSAGE_ARG_ADD: + case ADD: ActivityPackage activityPackage = (ActivityPackage) message.obj; packageHandler.addInternal(activityPackage); break; - case MESSAGE_ARG_SEND_FIRST: + case SEND_FIRST: packageHandler.sendFirstInternal(); break; - case MESSAGE_ARG_SEND_NEXT: + case SEND_NEXT: packageHandler.sendNextInternal(); break; } diff --git a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java index 48a338f6d..c729f2acb 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java @@ -34,9 +34,6 @@ public class RequestHandler extends HandlerThread { private static final int CONNECTION_TIMEOUT = 1000 * 5; // 5 seconds TODO: time 1 minute private static final int SOCKET_TIMEOUT = 1000 * 5; // 5 seconds TODO: time 1 minute - private static final int MESSAGE_ARG_INIT = 72401; - private static final int MESSAGE_ARG_SEND = 72400; - private InternalHandler internalHandler; private PackageHandler packageHandler; private HttpClient httpClient; @@ -50,18 +47,21 @@ protected RequestHandler(PackageHandler packageHandler) { this.packageHandler = packageHandler; Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_INIT; + message.arg1 = InternalHandler.INIT; internalHandler.sendMessage(message); } protected void sendPackage(ActivityPackage pack) { Message message = Message.obtain(); - message.arg1 = MESSAGE_ARG_SEND; + message.arg1 = InternalHandler.SEND; message.obj = pack; internalHandler.sendMessage(message); } private static final class InternalHandler extends Handler { + private static final int INIT = 72401; + private static final int SEND = 72400; + private final WeakReference requestHandlerReference; protected InternalHandler(Looper looper, RequestHandler requestHandler) { @@ -76,10 +76,10 @@ public void handleMessage(Message message) { if (requestHandler == null) return; switch (message.arg1) { - case MESSAGE_ARG_INIT: + case INIT: requestHandler.initInternal(); break; - case MESSAGE_ARG_SEND: + case SEND: ActivityPackage activityPackage = (ActivityPackage) message.obj; requestHandler.sendInternal(activityPackage); break; From aa1e8a7d4875669a7b686cd7537d806320bce60f Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Tue, 9 Jul 2013 17:14:39 +0200 Subject: [PATCH 29/37] Update header comments --- .../com/adeven/adjustio/ActivityHandler.java | 18 +++++++++++++----- .../com/adeven/adjustio/ActivityPackage.java | 9 ++++++++- .../src/com/adeven/adjustio/ActivityState.java | 9 +++++++++ AdjustIo/src/com/adeven/adjustio/AdjustIo.java | 2 +- AdjustIo/src/com/adeven/adjustio/Logger.java | 9 +++++++++ .../com/adeven/adjustio/PackageBuilder.java | 10 +++++++++- .../com/adeven/adjustio/PackageHandler.java | 11 ++++++++++- .../com/adeven/adjustio/RequestHandler.java | 15 +++++++++++---- AdjustIo/src/com/adeven/adjustio/Util.java | 6 +++--- 9 files changed, 73 insertions(+), 16 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index 6da4dda50..b4e2727fe 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -1,3 +1,12 @@ +// +// ActivityHandler.java +// AdjustIo +// +// Created by Christian Wellenbrock on 2013-06-25. +// Copyright (c) 2013 adeven. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + package com.adeven.adjustio; import java.io.BufferedInputStream; @@ -26,11 +35,11 @@ import android.os.Message; public class ActivityHandler extends HandlerThread { - private static final String SESSION_STATE_FILENAME = "activitystate2"; // TODO: filename + private static final String SESSION_STATE_FILENAME = "AdjustIoActivityState"; - private static final long TIMER_INTERVAL = 1000 * 3; // 10 second, TODO: time 1 minute - private static final long SESSION_INTERVAL = 1000 * 5; // 15 seconds, TODO: time 30 minutes - private static final long SUBSESSION_INTERVAL = 1000 * 1; // one second + private static final long TIMER_INTERVAL = 1000 * 60 * 1; // 1 minute + private static final long SESSION_INTERVAL = 1000 * 60 * 30; // 30 minutes + private static final long SUBSESSION_INTERVAL = 1000 * 1; // one second private InternalHandler internalHandler; private PackageHandler packageHandler; @@ -219,7 +228,6 @@ private void endInternal() { writeActivityState(); } - // TODO: set session attributes to -1 for events after session end? private void eventInternal(PackageBuilder eventBuilder) { if (!checkAppTokenNotNull(appToken)) return; if (!checkActivityState(activityState)) return; diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java b/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java index 20f06f0dc..2735a308b 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityPackage.java @@ -1,4 +1,11 @@ -// TODO: comments: add header comments +// +// ActivityPackage.java +// AdjustIo +// +// Created by Christian Wellenbrock on 2013-06-25. +// Copyright (c) 2013 adeven. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// package com.adeven.adjustio; diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityState.java b/AdjustIo/src/com/adeven/adjustio/ActivityState.java index 5ab2572c5..a71c42ec9 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityState.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityState.java @@ -1,3 +1,12 @@ +// +// ActivityState.java +// AdjustIo +// +// Created by Christian Wellenbrock on 2013-06-25. +// Copyright (c) 2013 adeven. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + package com.adeven.adjustio; import java.io.Serializable; diff --git a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java index bd35ecc4d..f1b3ed034 100644 --- a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java +++ b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java @@ -2,7 +2,7 @@ // AdjustIo.java // AdjustIo // -// Created by Christian Wellenbrock on 11.10.12. +// Created by Christian Wellenbrock on 2012-10-11. // Copyright (c) 2012 adeven. All rights reserved. // See the file MIT-LICENSE for copying permission. // diff --git a/AdjustIo/src/com/adeven/adjustio/Logger.java b/AdjustIo/src/com/adeven/adjustio/Logger.java index 774f32d98..fbf11e83e 100644 --- a/AdjustIo/src/com/adeven/adjustio/Logger.java +++ b/AdjustIo/src/com/adeven/adjustio/Logger.java @@ -1,3 +1,12 @@ +// +// Logger.java +// AdjustIo +// +// Created by Christian Wellenbrock on 2013-04-18. +// Copyright (c) 2013 adeven. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + package com.adeven.adjustio; import android.util.Log; diff --git a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java index e99447a6b..199bd18bc 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageBuilder.java @@ -1,3 +1,12 @@ +// +// PackageBuilder.java +// AdjustIo +// +// Created by Christian Wellenbrock on 2013-06-25. +// Copyright (c) 2013 adeven. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + package com.adeven.adjustio; import java.text.SimpleDateFormat; @@ -155,7 +164,6 @@ private void addDate(Map parameters, String key, long value) { private void addDuration(Map parameters, String key, long durationInMilliSeconds) { if (durationInMilliSeconds < 0) return; - // TODO: test rounding long durationInSeconds = (durationInMilliSeconds + 500) / 1000; addInt(parameters, key, durationInSeconds); } diff --git a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java index 720a3dcc9..06a05142e 100644 --- a/AdjustIo/src/com/adeven/adjustio/PackageHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/PackageHandler.java @@ -1,3 +1,12 @@ +// +// PackageHandler.java +// AdjustIo +// +// Created by Christian Wellenbrock on 2013-06-25. +// Copyright (c) 2013 adeven. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + package com.adeven.adjustio; import java.io.BufferedInputStream; @@ -24,7 +33,7 @@ // persistent public class PackageHandler extends HandlerThread { - private static final String PACKAGE_QUEUE_FILENAME = "testqueue6"; // TODO: filename + private static final String PACKAGE_QUEUE_FILENAME = "AdjustIoPackageQueue"; private InternalHandler internalHandler; private RequestHandler requestHandler; diff --git a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java index c729f2acb..ca63baf5f 100644 --- a/AdjustIo/src/com/adeven/adjustio/RequestHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/RequestHandler.java @@ -1,3 +1,12 @@ +// +// RequestHandler.java +// AdjustIo +// +// Created by Christian Wellenbrock on 2013-06-25. +// Copyright (c) 2013 adeven. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + package com.adeven.adjustio; import java.io.ByteArrayOutputStream; @@ -31,8 +40,8 @@ import android.os.Message; public class RequestHandler extends HandlerThread { - private static final int CONNECTION_TIMEOUT = 1000 * 5; // 5 seconds TODO: time 1 minute - private static final int SOCKET_TIMEOUT = 1000 * 5; // 5 seconds TODO: time 1 minute + private static final int CONNECTION_TIMEOUT = 1000 * 60 * 1; // 1 minute + private static final int SOCKET_TIMEOUT = 1000 * 60 * 1; // 1 minute private InternalHandler internalHandler; private PackageHandler packageHandler; @@ -87,8 +96,6 @@ public void handleMessage(Message message) { } } - // TODO: use SSLSessionCache? - // http://candrews.integralblue.com/2011/09/best-way-to-use-httpclient-in-android/ private void initInternal() { HttpParams httpParams = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParams, CONNECTION_TIMEOUT); diff --git a/AdjustIo/src/com/adeven/adjustio/Util.java b/AdjustIo/src/com/adeven/adjustio/Util.java index 6c63948c7..fb0c29bfe 100644 --- a/AdjustIo/src/com/adeven/adjustio/Util.java +++ b/AdjustIo/src/com/adeven/adjustio/Util.java @@ -2,7 +2,7 @@ // Util.java // AdjustIo // -// Created by Christian Wellenbrock on 11.10.12. +// Created by Christian Wellenbrock on 2012-10-11. // Copyright (c) 2012 adeven. All rights reserved. // See the file MIT-LICENSE for copying permission. // @@ -39,8 +39,8 @@ * @since 11.10.12 */ public class Util { - protected static final String BASE_URL = "http://192.168.178.117:8509"; // TODO: url - protected static final String CLIENT_SDK = "android1.6"; + protected static final String BASE_URL = "http://192.168.178.117:8509"; // TODO: change url + protected static final String CLIENT_SDK = "android1.6"; // TODO: change sdk private static final String UNKNOWN = "unknown"; From 3f7abe36bae148c3a58d32af25f837b7054ce265 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Tue, 9 Jul 2013 18:14:48 +0200 Subject: [PATCH 30/37] Update main comments --- .../src/com/adeven/adjustio/AdjustIo.java | 71 +++++++++++-------- AdjustIo/src/com/adeven/adjustio/Util.java | 3 - 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java index f1b3ed034..1ba632226 100644 --- a/AdjustIo/src/com/adeven/adjustio/AdjustIo.java +++ b/AdjustIo/src/com/adeven/adjustio/AdjustIo.java @@ -18,24 +18,19 @@ * * Use the methods of this class to tell AdjustIo about the usage of your app. * See the README for details. - * - * @author wellle - * @since 11.10.12 */ public class AdjustIo { - // every call gets forwarded to activityHandler - private static ActivityHandler activityHandler; - - // TODO: update all comments /** - * Tell AdjustIo that the application did launch. + * Tell AdjustIo that an activity did resume. * - * This is required to initialize AdjustIo. - * Call this in the onCreate method of your launch activity. + * This is used to initialize AdjustIo and keep track of the current session state. + * Call this in the onResume method of every activity of your app. * - * @param context Your application context - * Generally obtained by calling getApplication() + * @param appToken The App Token for your app. This unique identifier can + * be found in your dashboard at http://adjust.io and should always + * be 12 characters long. + * @param activity The activity that has just resumed. */ public static void onResume(String appToken, Activity activity) { @@ -45,6 +40,12 @@ public static void onResume(String appToken, Activity activity) { activityHandler.trackSubsessionStart(); } + /** + * Tell AdjustIo 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 { activityHandler.trackSubsessionEnd(); @@ -54,17 +55,17 @@ public static void onPause() { } /** - * Track any kind of event. + * Tell AdjustIo that a particular event has happened. * - * You can assign a callback url to the event which - * will get called every time the event is reported. You can also provide - * parameters that will be forwarded to these callbacks. + * In your dashboard at http://adjust.io 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 token for this kind of event - * It must be exactly six characters long - * You create them in your dashboard at http://www.adjust.io - * @param parameters An optional dictionary containing callback parameters - * Provide key-value-pairs to be forwarded to your callbacks + * @param eventToken The Event Token for this kind of event. They are created + * in the dashboard at http://adjust.io 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) { @@ -81,17 +82,17 @@ public static void trackEvent(String eventToken, Map parameters) /** - * Tell AdjustIo that the current user generated some revenue. + * Tell AdjustIo 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 various types of specific revenues - * 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 - * server. + * 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 (see above) - * @param parameters Parameters for this revenue event (see above) + * @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) { @@ -114,10 +115,13 @@ public static void trackRevenue(double amountInCents, String eventToken, Map Date: Wed, 10 Jul 2013 16:14:38 +0200 Subject: [PATCH 31/37] Rename startNextSession to resetSessionAttributes --- .../com/adeven/adjustio/ActivityHandler.java | 17 +++++++++++------ .../src/com/adeven/adjustio/ActivityState.java | 3 +-- AdjustIo/src/com/adeven/adjustio/Util.java | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java index b4e2727fe..3393f9299 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityHandler.java @@ -178,13 +178,14 @@ private void startInternal() { // very first session if (activityState == null) { - Logger.info("First session"); activityState = new ActivityState(); activityState.sessionCount = 1; // this is the first session activityState.createdAt = now; // starting now transferSessionPackage(); + activityState.resetSessionAttributes(now); writeActivityState(); + Logger.info("First session"); return; } @@ -198,21 +199,25 @@ private void startInternal() { // new session if (lastInterval > SESSION_INTERVAL) { + activityState.sessionCount++; activityState.createdAt = now; activityState.lastInterval = lastInterval; - transferSessionPackage(); - activityState.startNextSession(now); + transferSessionPackage(); + activityState.resetSessionAttributes(now); writeActivityState(); - Logger.debug(String.format(Locale.US, "Session %d", activityState.sessionCount)); + Logger.debug(String.format(Locale.US, + "Session %d", activityState.sessionCount)); return; } // new subsession if (lastInterval > SUBSESSION_INTERVAL) { activityState.subsessionCount++; - Logger.debug(String.format(Locale.US, "Subsession %d.%d", - activityState.sessionCount, activityState.subsessionCount)); + Logger.info(String.format(Locale.US, + "Started subsession %d of session %d", + activityState.subsessionCount, + activityState.sessionCount)); } activityState.sessionLength += lastInterval; activityState.lastActivity = now; diff --git a/AdjustIo/src/com/adeven/adjustio/ActivityState.java b/AdjustIo/src/com/adeven/adjustio/ActivityState.java index a71c42ec9..8a94baf31 100644 --- a/AdjustIo/src/com/adeven/adjustio/ActivityState.java +++ b/AdjustIo/src/com/adeven/adjustio/ActivityState.java @@ -40,8 +40,7 @@ protected ActivityState() { lastInterval = -1; } - protected void startNextSession(long now) { - sessionCount++; // the next session just started + protected void resetSessionAttributes(long now) { subsessionCount = 1; // first subsession sessionLength = 0; // no session length yet timeSpent = 0; // no time spent yet diff --git a/AdjustIo/src/com/adeven/adjustio/Util.java b/AdjustIo/src/com/adeven/adjustio/Util.java index c007a6f46..7b2387652 100644 --- a/AdjustIo/src/com/adeven/adjustio/Util.java +++ b/AdjustIo/src/com/adeven/adjustio/Util.java @@ -36,8 +36,8 @@ * Collects utility functions used by AdjustIo. */ public class Util { - protected static final String BASE_URL = "http://192.168.178.117:8509"; // TODO: change url - protected static final String CLIENT_SDK = "android1.6"; // TODO: change sdk + protected static final String BASE_URL = "http://192.168.178.117:8509"; // TODO: change url + protected static final String CLIENT_SDK = "android1.6"; // TODO: change sdk private static final String UNKNOWN = "unknown"; From 004c274a95bc72edc444f17e3bbc988081e363a4 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Thu, 11 Jul 2013 20:11:29 +0200 Subject: [PATCH 32/37] Update README --- README.md | 94 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 4a73f3dd8..6597c3dd8 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,19 @@ + + ## Summary -This is the Android SDK of AdjustIo. You ca read more about it at [adjust.io][]. +This is the Android SDK of AdjustIo. You ca read more about AdjustIo at [adjust.io][]. ## Basic Installation These are the minimal steps required to integrate the AdjustIo SDK into your Android project. We are going to assume that you use Eclipse for your Android development. ### 1. Get the SDK -Download the latest version from our [tags page][tags]. Extract the archive in a folder of your liking. + +Download the latest version from our [releases page][releases]. Extract the archive in a folder of your choice. ### 2. Add it to your project + In the Eclipse menu select `File|New|Project...`. ![][project] @@ -23,12 +27,14 @@ On the top of the next screen click the `Browse...` button and locate the folder ![][import] ### 3. Integrate AdjustIo into your app + In the Package Explorer right click on your Android project and select `Properties`. ![][properties] In the left pane select `Android`. In the bottom right group `Library` click the `Add...` button. From the list select the AdjustIo library project and click `OK`. Save your changed project properties by clicking `OK` again. + ![][library] 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. @@ -40,67 +46,109 @@ In the Package Explorer open the `AndroidManifest.xml` of your Android project. ![][permissions] -In the Package Explorer open the launch activity of your Android App. Add the `import` statement to the top of the source file. In the `onCreate` method of your activity call the method `appDidLaunch`. This tells AdjustIo about the launch of your Application. Replace `` with the App Token that you can find in your dashboard at [adjust.io][]. +To provide proper session tracking it is required to call certain AdjustIo 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. +- Call `AdjustIo.onResume` in your Activity's `onResume` method. Create the method if needed. +- Replace `{YourAppToken}` with your App Token. You can find in your [dashboard]. +- Call `AdjustIo.onPause` in your Activity's `onPause` method. Create the method if needed. ```java import com.adeven.adjustio.AdjustIo; // ... -AdjustIo.appDidLaunch("", getApplication()); +public class YourActivity extends Activity { + protected void onResume() { + super.onResume(); + AdjustIo.onResume("{YourAppToken}", this); + } + protected void onPause() { + super.onPause(); + AdjustIo.onPause(); + } + // ... +} ``` +Repeat this 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. + + ![][activity] ### 4. Build your app -Build and run your Android app. In your LogCat viewer you can set the filter `tag:AdjustIo` to hide all other logs. After your app has launched you should see the following AdjustIo log: `Tracked session start.` + +Build and run your Android app. In your LogCat viewer you can set the filter `tag:AdjustIo` to hide all other logs. After your app has launched you should see the following AdjustIo log: `Tracked session start` ![][log] +### 5. Adjust Logging + +You can increase or decrease the amount of logs you see by calling `setLogLevel` with one of the following parameters. Make sure to import `android.util.Log`. + +```objc +AdjustIo.setLogLevel(Log.VERBOSE); // enable all logging +AdjustIo.setLogLevel(Log.DEBUG); // enable more logging +AdjustIo.setLogLevel(Log.INFO); // the default +AdjustIo.setLogLevel(Log.WARN); // disable info logging +AdjustIo.setLogLevel(Log.ERROR); // disable warnings as well +AdjustIo.setLogLevel(Log.ASSERT); // disable errors as well +``` + ## Additional Features -Once you have integrated the AdjustIo SDK into you project, you can take advantage of the following features wherever you see fit. +Once you have integrated the AdjustIo SDK into your project, you can take advantage of the following features. ### Add tracking of custom events. -You can tell AdjustIo about every event you consider to be of your interest. Suppose you want to track every tap on a button. Currently you would have to ask us for an event token and we would give you one, like `abc123`. In your button's onClick method you could then add the following code to track the click: + +You can tell AdjustIo 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: ```java AdjustIo.trackEvent("abc123"); ``` -You can also register a callback URL for that event and we will send a request to that URL whenever the event happens. Additianally you can put some key-value-pairs in a Map and pass it to the trackEvent method. In that case we will forward these named parameters to your callback URL. Suppose you registered the URL `http://www.adeven.com/callback` for your event and execute the following lines: +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 callback URL. - Map parameters = new HashMap(); - parameters.put("key", "value"); - parameters.put("foo", "bar"); - AdjustIo.trackEvent("abc123", parameters); +For example, suppose you have registered the URL `http://www.adeven.com/callback` for your event with Event Token `abc123` and execute the following lines: -In that case we would track the event and send a request to `http://www.adeven.com/callback?key=value&foo=bar`. In any case you need to import AdjustIo in any source file that makes use of the SDK. Please note that we don't store your custom parameters. If you haven't registered a callback URL for an event, there is no point in sending us parameters. +```java +Map parameters = new HashMap(); +parameters.put("key", "value"); +parameters.put("foo", "bar"); +AdjustIo.trackEvent("abc123", parameters); +``` + +In that case we would track the event and send a request to: + + http://www.adeven.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 this placeholder would be replaced with the AndroidID of the current device. 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. ### Add tracking of revenue -If your users can generate revenue by clicking on advertisements you can track those revenues. If the click is worth one Cent, you could make the following call to track that revenue: + +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 -AdjustIo.trackRevenue(1.0f); +AdjustIo.trackRevenue(1.0); ``` -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 ask us for event tokens that you can then use. In that case you would make a call like this: +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: ```java -AdjustIo.trackRevenue(1.0f, "abc123"); +AdjustIo.trackRevenue(1.0, "abc123"); ``` -You can also register a callback URL again and provide a map of named parameters, just like it worked with normal events. +Again, you can register a callback and provide a dictionary of named parameters, just like it worked with normal events. ```java Map parameters = new HashMap(); parameters.put("key", "value"); parameters.put("foo", "bar"); -AdjustIo.trackRevenue(1.0f, "abc123", parameters); +AdjustIo.trackRevenue(1.0, "abc123", parameters); ``` -In any case, don't forget to import AdjustIo. Again, there is no point in sending parameters if you haven't registered a callback URL for that revenue event. - -[adjust.io]: http://www.adjust.io -[tags]: https://github.com/adeven/adjust_android_sdk/tags +[adjust.io]: http://adjust.io +[dashboard]: http://adjust.io +[releases]: https://github.com/adeven/adjust_android_sdk/releases [project]: https://raw.github.com/adeven/adjust_sdk/master/Resources/android/project.png [android]: https://raw.github.com/adeven/adjust_sdk/master/Resources/android/android.png [import]: https://raw.github.com/adeven/adjust_sdk/master/Resources/android/import.png From 42ba2297ef0cb56701c7bae2fe25b32aa9c22734 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Thu, 11 Jul 2013 20:16:38 +0200 Subject: [PATCH 33/37] Wrap lines of README --- README.md | 94 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 6597c3dd8..676a2db6b 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,18 @@ - - ## Summary -This is the Android SDK of AdjustIo. You ca read more about AdjustIo at [adjust.io][]. +This is the Android SDK of AdjustIo. You ca read more about AdjustIo at +[adjust.io]. ## Basic Installation -These are the minimal steps required to integrate the AdjustIo SDK into your Android project. We are going to assume that you use Eclipse for your Android development. +These are the minimal steps required to integrate the AdjustIo SDK into your +Android project. We are going to assume that you use Eclipse for your Android +development. ### 1. Get the SDK -Download the latest version from our [releases page][releases]. Extract the archive in a folder of your choice. +Download the latest version from our [releases page][releases]. Extract the +archive in a folder of your choice. ### 2. Add it to your project @@ -18,26 +20,35 @@ In the Eclipse menu select `File|New|Project...`. ![][project] -From the Wizard expand the `Android` group and select `Android Project from Existing Code` and click `Next`. +From the Wizard expand the `Android` group and select `Android Project from +Existing Code` and click `Next`. ![][android] -On the top of the next screen click the `Browse...` button and locate the folder you extracted in step 1. Select the AdjustIo subfolder and click `Open`. In the `Projects:` group make sure the AdjustIo project is selected. Also tick the option `Copy projects into workspace` and click `Finish`. +On the top of the next screen click the `Browse...` button and locate the +folder you extracted in step 1. Select the AdjustIo subfolder and click `Open`. +In the `Projects:` group make sure the AdjustIo project is selected. Also tick +the option `Copy projects into workspace` and click `Finish`. ![][import] ### 3. Integrate AdjustIo into your app -In the Package Explorer right click on your Android project and select `Properties`. +In the Package Explorer right click on your Android project and select +`Properties`. ![][properties] -In the left pane select `Android`. In the bottom right group `Library` click the `Add...` button. From the list select the AdjustIo library project and click `OK`. Save your changed project properties by clicking `OK` again. +In the left pane select `Android`. In the bottom right group `Library` click +the `Add...` button. From the list select the AdjustIo library project and +click `OK`. Save your changed project properties by clicking `OK` again. ![][library] -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. +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. ```java @@ -46,13 +57,19 @@ In the Package Explorer open the `AndroidManifest.xml` of your Android project. ![][permissions] -To provide proper session tracking it is required to call certain AdjustIo 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: +To provide proper session tracking it is required to call certain AdjustIo +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. -- Call `AdjustIo.onResume` in your Activity's `onResume` method. Create the method if needed. -- Replace `{YourAppToken}` with your App Token. You can find in your [dashboard]. -- Call `AdjustIo.onPause` in your Activity's `onPause` method. Create the method if needed. +- Call `AdjustIo.onResume` in your Activity's `onResume` method. Create the + method if needed. +- Replace `{YourAppToken}` with your App Token. You can find in your + [dashboard]. +- Call `AdjustIo.onPause` in your Activity's `onPause` method. Create the + method if needed. ```java import com.adeven.adjustio.AdjustIo; @@ -70,20 +87,26 @@ public class YourActivity extends Activity { } ``` -Repeat this 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. +Repeat this 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. ![][activity] ### 4. Build your app -Build and run your Android app. In your LogCat viewer you can set the filter `tag:AdjustIo` to hide all other logs. After your app has launched you should see the following AdjustIo log: `Tracked session start` +Build and run your Android app. In your LogCat viewer you can set the filter +`tag:AdjustIo` to hide all other logs. After your app has launched you should +see the following AdjustIo log: `Tracked session start` ![][log] ### 5. Adjust Logging -You can increase or decrease the amount of logs you see by calling `setLogLevel` with one of the following parameters. Make sure to import `android.util.Log`. +You can increase or decrease the amount of logs you see by calling +`setLogLevel` with one of the following parameters. Make sure to import +`android.util.Log`. ```objc AdjustIo.setLogLevel(Log.VERBOSE); // enable all logging @@ -96,19 +119,29 @@ AdjustIo.setLogLevel(Log.ASSERT); // disable errors as well ## Additional Features -Once you have integrated the AdjustIo SDK into your project, you can take advantage of the following features. +Once you have integrated the AdjustIo SDK into your project, you can take +advantage of the following features. ### Add tracking of custom events. -You can tell AdjustIo 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 tell AdjustIo 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: ```java AdjustIo.trackEvent("abc123"); ``` -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 callback URL. +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 +callback URL. -For example, suppose you have registered the URL `http://www.adeven.com/callback` for your event with Event Token `abc123` and execute the following lines: +For example, suppose you have registered the URL +`http://www.adeven.com/callback` for your event with Event Token `abc123` and +execute the following lines: ```java Map parameters = new HashMap(); @@ -121,23 +154,34 @@ In that case we would track the event and send a request to: http://www.adeven.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 this placeholder would be replaced with the AndroidID of the current device. 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. +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 +this placeholder would be replaced with the AndroidID of the current device. +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. ### Add tracking of revenue -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: +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 AdjustIo.trackRevenue(1.0); ``` -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: +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: ```java AdjustIo.trackRevenue(1.0, "abc123"); ``` -Again, you can register a callback and provide a dictionary of named parameters, just like it worked with normal events. +Again, you can register a callback and provide a dictionary of named +parameters, just like it worked with normal events. ```java Map parameters = new HashMap(); From b84596f1098303cbcc990d71ee6597ad8807e81f Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Mon, 15 Jul 2013 11:33:37 +0200 Subject: [PATCH 34/37] Update images in README --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 676a2db6b..27266177a 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,6 @@ In the left pane select `Android`. In the bottom right group `Library` click the `Add...` button. From the list select the AdjustIo library project and click `OK`. Save your changed project properties by clicking `OK` again. - ![][library] In the Package Explorer open the `AndroidManifest.xml` of your Android project. @@ -87,13 +86,12 @@ public class YourActivity extends Activity { } ``` +![][activity] + Repeat this 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. - -![][activity] - ### 4. Build your app Build and run your Android app. In your LogCat viewer you can set the filter @@ -199,8 +197,8 @@ AdjustIo.trackRevenue(1.0, "abc123", parameters); [properties]: https://raw.github.com/adeven/adjust_sdk/master/Resources/android/properties.png [library]: https://raw.github.com/adeven/adjust_sdk/master/Resources/android/library.png [permissions]: https://raw.github.com/adeven/adjust_sdk/master/Resources/android/permissions.png -[activity]: https://raw.github.com/adeven/adjust_sdk/master/Resources/android/activity.png -[log]: https://raw.github.com/adeven/adjust_sdk/master/Resources/android/log.png +[activity]: https://raw.github.com/adeven/adjust_sdk/master/Resources/android/activity2.png +[log]: https://raw.github.com/adeven/adjust_sdk/master/Resources/android/log2.png ## License From 23acc632b7ce776cb3ae4f1330c3dc555c961e72 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Tue, 16 Jul 2013 11:18:21 +0200 Subject: [PATCH 35/37] Split sections of README --- README.md | 61 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 27266177a..b8a0127d4 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ development. Download the latest version from our [releases page][releases]. Extract the archive in a folder of your choice. -### 2. Add it to your project +### 2. Create the AdjustIo project In the Eclipse menu select `File|New|Project...`. @@ -32,7 +32,7 @@ the option `Copy projects into workspace` and click `Finish`. ![][import] -### 3. Integrate AdjustIo into your app +### 3. Add the AdjustIo library to your project In the Package Explorer right click on your Android project and select `Properties`. @@ -45,6 +45,8 @@ click `OK`. Save your changed project properties by clicking `OK` again. ![][library] +### 4. Add permissions + 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. @@ -56,6 +58,8 @@ aren't present already. ![][permissions] +### 5. Integrate AdjustIo into your app + To provide proper session tracking it is required to call certain AdjustIo 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 @@ -63,13 +67,15 @@ for **each** Activity of your app: - Open the source file of your Activity. - Add the `import` statement at the top of the file. -- Call `AdjustIo.onResume` in your Activity's `onResume` method. Create the +- In your Activity's `onResume` method call `AdjustIo.onResume`. Create the method if needed. - Replace `{YourAppToken}` with your App Token. You can find in your [dashboard]. -- Call `AdjustIo.onPause` in your Activity's `onPause` method. Create the +- In your Activity's `orPause` method call `AdjustIo.onPause`. Create the method if needed. +After these steps your activity should look like this: + ```java import com.adeven.adjustio.AdjustIo; // ... @@ -88,11 +94,11 @@ public class YourActivity extends Activity { ![][activity] -Repeat this 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. +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. -### 4. Build your app +### 6. Build your app Build and run your Android app. In your LogCat viewer you can set the filter `tag:AdjustIo` to hide all other logs. After your app has launched you should @@ -100,13 +106,13 @@ see the following AdjustIo log: `Tracked session start` ![][log] -### 5. Adjust Logging +### 7. Adjust Logging You can increase or decrease the amount of logs you see by calling `setLogLevel` with one of the following parameters. Make sure to import `android.util.Log`. -```objc +```java AdjustIo.setLogLevel(Log.VERBOSE); // enable all logging AdjustIo.setLogLevel(Log.DEBUG); // enable more logging AdjustIo.setLogLevel(Log.INFO); // the default @@ -208,21 +214,20 @@ The adjust-sdk is licensed under the MIT License. Copyright (c) 2012 adeven GmbH, http://www.adeven.com -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 98285dc21ce2acd59629ee727a4616493e468ab9 Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Tue, 16 Jul 2013 11:19:32 +0200 Subject: [PATCH 36/37] Add migration guide --- doc/migrate.md | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 doc/migrate.md diff --git a/doc/migrate.md b/doc/migrate.md new file mode 100644 index 000000000..4f6ae55a9 --- /dev/null +++ b/doc/migrate.md @@ -0,0 +1,59 @@ +## Migrate to AdjustIo SDK for Android v2.0 + +1. Delete the old `AdjustIo` project from your `Package Explorer`. Download + version v2.0 and create a new `Android Project from Existing Code` as + described in the [README]. + + ![][import] + +2. We no longer use the `AdjustIo.appDidLaunch()` method for initialization. + Delete the call in your launch activity's `onCreate` method. + +3. Instead, to provide proper session tracking, it is required to call certain + new AdjustIo 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 `AdjustIo.onResume`. Create the + method if needed. + - Replace `{YourAppToken}` with your App Token. You can find in your + [dashboard]. + - In your Activity's `orPause` method call `AdjustIo.onPause`. Create the + method if needed. + + After these steps your activity should look like this: + + ```java + import com.adeven.adjustio.AdjustIo; + // ... + public class YourActivity extends Activity { + protected void onResume() { + super.onResume(); + AdjustIo.onResume("{YourAppToken}", this); + } + protected void onPause() { + super.onPause(); + AdjustIo.onPause(); + } + // ... + } + ``` + + ![][activity] + + 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. + +4. The `amount` parameter of the `trackRevenue` methods is now of type + `double`, so you can drop the `f` suffixes in number literals (`12.3f` + becomes `12.3`). + +[README]: ../README.md +[import]: https://raw.github.com/adeven/adjust_sdk/master/Resources/android/import.png +[activity]: https://raw.github.com/adeven/adjust_sdk/master/Resources/android/activity2.png +[dashboard]: http://adjust.io + From 7c2f41055c4a10dfdc7bdaae8252f2fa130463ed Mon Sep 17 00:00:00 2001 From: Christian Wellenbrock Date: Tue, 16 Jul 2013 17:42:34 +0200 Subject: [PATCH 37/37] Update version to 2.0 --- AdjustIo/src/com/adeven/adjustio/Util.java | 4 ++-- VERSION | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/AdjustIo/src/com/adeven/adjustio/Util.java b/AdjustIo/src/com/adeven/adjustio/Util.java index 7b2387652..1a6db0051 100644 --- a/AdjustIo/src/com/adeven/adjustio/Util.java +++ b/AdjustIo/src/com/adeven/adjustio/Util.java @@ -36,8 +36,8 @@ * Collects utility functions used by AdjustIo. */ public class Util { - protected static final String BASE_URL = "http://192.168.178.117:8509"; // TODO: change url - protected static final String CLIENT_SDK = "android1.6"; // TODO: change sdk + protected static final String BASE_URL = "https://app.adjust.io"; + protected static final String CLIENT_SDK = "android2.0"; private static final String UNKNOWN = "unknown"; diff --git a/VERSION b/VERSION index 810ee4e91..cd5ac039d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.6 +2.0