diff --git a/Adjust/adjust/build.gradle b/Adjust/adjust/build.gradle index 0d3660504..2b36ab660 100644 --- a/Adjust/adjust/build.gradle +++ b/Adjust/adjust/build.gradle @@ -2,13 +2,13 @@ apply plugin: 'com.android.library' android { compileSdkVersion 24 - buildToolsVersion "24.0.1" + buildToolsVersion "24.0.2" defaultConfig { minSdkVersion 9 targetSdkVersion 24 versionCode 1 - versionName "4.7.0" + versionName "4.10.0" } } diff --git a/Adjust/adjust/src/main/AndroidManifest.xml b/Adjust/adjust/src/main/AndroidManifest.xml index d3e803eec..eb26f9c9d 100644 --- a/Adjust/adjust/src/main/AndroidManifest.xml +++ b/Adjust/adjust/src/main/AndroidManifest.xml @@ -1,8 +1,11 @@ - + + + + android:targetSdkVersion="24" /> + diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityHandler.java index 4267fea72..7630bb524 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityHandler.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityHandler.java @@ -16,20 +16,17 @@ import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Handler; -import android.os.HandlerThread; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; import static com.adjust.sdk.Constants.ACTIVITY_STATE_FILENAME; import static com.adjust.sdk.Constants.ATTRIBUTION_FILENAME; -import static com.adjust.sdk.Constants.LOGTAG; - -public class ActivityHandler extends HandlerThread implements IActivityHandler { +import static com.adjust.sdk.Constants.SESSION_CALLBACK_PARAMETERS_FILENAME; +import static com.adjust.sdk.Constants.SESSION_PARTNER_PARAMETERS_FILENAME; +public class ActivityHandler implements IActivityHandler { private static long FOREGROUND_TIMER_INTERVAL; private static long FOREGROUND_TIMER_START; private static long BACKGROUND_TIMER_INTERVAL; @@ -41,14 +38,17 @@ public class ActivityHandler extends HandlerThread implements IActivityHandler { private static final String ATTRIBUTION_NAME = "Attribution"; private static final String FOREGROUND_TIMER_NAME = "Foreground timer"; private static final String BACKGROUND_TIMER_NAME = "Background timer"; + private static final String DELAY_START_TIMER_NAME = "Delay Start timer"; + private static final String SESSION_CALLBACK_PARAMETERS_NAME = "Session Callback parameters"; + private static final String SESSION_PARTNER_PARAMETERS_NAME = "Session Partner parameters"; - private Handler internalHandler; + private CustomScheduledExecutor scheduledExecutor; private IPackageHandler packageHandler; private ActivityState activityState; private ILogger logger; private TimerCycle foregroundTimer; - private ScheduledExecutorService scheduler; private TimerOnce backgroundTimer; + private TimerOnce delayStartTimer; private InternalState internalState; private DeviceInfo deviceInfo; @@ -56,11 +56,66 @@ public class ActivityHandler extends HandlerThread implements IActivityHandler { private AdjustAttribution attribution; private IAttributionHandler attributionHandler; private ISdkClickHandler sdkClickHandler; + private SessionParameters sessionParameters; + + @Override + public void teardown(boolean deleteState) { + if (backgroundTimer != null) { + backgroundTimer.teardown(); + } + if (foregroundTimer != null) { + foregroundTimer.teardown(); + } + if (delayStartTimer != null) { + delayStartTimer.teardown(); + } + if (scheduledExecutor != null) { + try { + scheduledExecutor.shutdownNow(); + } catch(SecurityException se) {} + } + if (packageHandler != null) { + packageHandler.teardown(deleteState); + } + if (attributionHandler != null) { + attributionHandler.teardown(); + } + if (sdkClickHandler != null) { + sdkClickHandler.teardown(); + } + if (sessionParameters != null) { + if (sessionParameters.callbackParameters != null) { + sessionParameters.callbackParameters.clear(); + } + if (sessionParameters.partnerParameters != null) { + sessionParameters.partnerParameters.clear(); + } + } + + teardownActivityStateS(deleteState); + teardownAttributionS(deleteState); + teardownAllSessionParametersS(deleteState); + + packageHandler = null; + logger = null; + foregroundTimer = null; + scheduledExecutor = null; + backgroundTimer = null; + delayStartTimer = null; + internalState = null; + deviceInfo = null; + adjustConfig = null; + attributionHandler = null; + sdkClickHandler = null; + sessionParameters = null; + } public class InternalState { boolean enabled; boolean offline; boolean background; + boolean delayStart; + boolean updatePackages; public boolean isEnabled() { return enabled; @@ -85,70 +140,48 @@ public boolean isBackground() { public boolean isForeground() { return !background; } + + public boolean isDelayStart() { + return delayStart; + } + + public boolean isToStartNow() { + return !delayStart; + } + + public boolean isToUpdatePackages() { + return updatePackages; + } } private ActivityHandler(AdjustConfig adjustConfig) { - super(LOGTAG, MIN_PRIORITY); - setDaemon(true); - start(); - init(adjustConfig); // init logger to be available everywhere logger = AdjustFactory.getLogger(); - if (AdjustConfig.ENVIRONMENT_PRODUCTION.equals(adjustConfig.environment)) { - logger.setLogLevel(LogLevel.ASSERT); - } else { - logger.setLogLevel(adjustConfig.logLevel); - } - this.internalHandler = new Handler(getLooper()); - internalState = new InternalState(); + logger.lockLogLevel(); - // read files to have sync values available - readAttribution(adjustConfig.context); - readActivityState(adjustConfig.context); + scheduledExecutor = new CustomScheduledExecutor("ActivityHandler"); + internalState = new InternalState(); // enabled by default - if (activityState == null) { - internalState.enabled = true; - } else { - internalState.enabled = activityState.enabled; - } + internalState.enabled = true; // online by default internalState.offline = false; // in the background by default internalState.background = true; + // delay start not configured by default + internalState.delayStart = false; + // does not need to update packages by default + internalState.updatePackages = false; - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - initInternal(); + initI(); } }); - - // get timer values - FOREGROUND_TIMER_INTERVAL = AdjustFactory.getTimerInterval(); - FOREGROUND_TIMER_START = AdjustFactory.getTimerStart(); - BACKGROUND_TIMER_INTERVAL = AdjustFactory.getTimerInterval(); - - // initialize timers to be available in onResume/onPause - // after initInternal so that the handlers are initialized - foregroundTimer = new TimerCycle(new Runnable() { - @Override - public void run() { - foregroundTimerFired(); - } - }, FOREGROUND_TIMER_START, FOREGROUND_TIMER_INTERVAL, FOREGROUND_TIMER_NAME); - - // create background timer - scheduler = Executors.newSingleThreadScheduledExecutor(); - backgroundTimer = new TimerOnce(scheduler, new Runnable() { - @Override - public void run() { - backgroundTimerFired(); - } - }, BACKGROUND_TIMER_NAME); } @Override @@ -194,16 +227,18 @@ public static ActivityHandler getInstance(AdjustConfig adjustConfig) { public void onResume() { internalState.background = false; - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - stopBackgroundTimer(); + delayStartI(); + + stopBackgroundTimerI(); - startForegroundTimer(); + startForegroundTimerI(); logger.verbose("Subsession start"); - startInternal(); + startI(); } }); } @@ -212,32 +247,32 @@ public void run() { public void onPause() { internalState.background = true; - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - stopForegroundTimer(); + stopForegroundTimerI(); - startBackgroundTimer(); + startBackgroundTimerI(); logger.verbose("Subsession end"); - endInternal(); + endI(); } }); } @Override public void trackEvent(final AdjustEvent event) { - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { if (activityState == null) { - logger.warn("Event triggered before first application launch.\n" + - "This will trigger the SDK start and an install without user interaction.\n" + + logger.warn("Event tracked before first activity resumed.\n" + + "If it was triggered in the Application class, it might timestamp or even send an install long before the user opens the app.\n" + "Please check https://github.com/adjust/android_sdk#can-i-trigger-an-event-at-application-launch for more information."); - startInternal(); + startI(); } - trackEventInternal(event); + trackEventI(event); } }); } @@ -251,13 +286,13 @@ public void finishedTrackingActivity(ResponseData responseData) { } // check if it's an event response if (responseData instanceof EventResponseData) { - launchEventResponseTasks((EventResponseData)responseData); + launchEventResponseTasksI((EventResponseData)responseData); return; } } @Override - public void setEnabled(boolean enabled) { + public void setEnabled(final boolean enabled) { // compare with the saved or internal state if (!hasChangedState(this.isEnabled(), enabled, "Adjust already enabled", "Adjust already disabled")) { @@ -269,20 +304,25 @@ public void setEnabled(boolean enabled) { if (activityState == null) { updateStatus(!enabled, - "Package handler and attribution handler will start as paused due to the SDK being disabled", - "Package and attribution handler will still start as paused due to the SDK being offline", - "Package handler and attribution handler will start as active due to the SDK being enabled"); + "Handlers will start as paused due to the SDK being disabled", + "Handlers will still start as paused", + "Handlers will start as active due to the SDK being enabled"); return; } // save new enabled state in activity state - activityState.enabled = enabled; - writeActivityState(); + Runnable saveEnabled = new Runnable() { + @Override + public void run() { + activityState.enabled = enabled; + } + }; + writeActivityStateS(saveEnabled); updateStatus(!enabled, - "Pausing package handler and attribution handler due to SDK being disabled", - "Package and attribution handler remain paused due to SDK being offline", - "Resuming package handler and attribution handler due to SDK being enabled"); + "Pausing handlers due to SDK being disabled", + "Handlers remain paused", + "Resuming handlers due to SDK being enabled"); } private void updateStatus(boolean pausingState, String pausingMessage, @@ -291,18 +331,21 @@ private void updateStatus(boolean pausingState, String pausingMessage, // it is changing from an active state to a pause state if (pausingState) { logger.info(pausingMessage); - updateHandlersStatusAndSend(); - return; } - - // it is remaining in a pause state - if (paused()) { - logger.info(remainsPausedMessage); - // it is changing from a pause state to an active state + // check if it's remaining in a pause state + else if (pausedI(false)) { // safe to use internal version of paused (read only), can suffer from phantom read but not an issue + // including the sdk click handler + if (pausedI(true)) { + logger.info(remainsPausedMessage); + } else { + logger.info(remainsPausedMessage + ", except the Sdk Click Handler"); + } } else { + // it is changing from a pause state to an active state logger.info(unPausingMessage); - updateHandlersStatusAndSend(); } + + updateHandlersStatusAndSend(); } private boolean hasChangedState(boolean previousState, boolean newState, @@ -334,20 +377,24 @@ public void setOfflineMode(boolean offline) { if (activityState == null) { updateStatus(offline, - "Package handler and attribution handler will start paused due to SDK being offline", - "Package and attribution handler will still start as paused due to SDK being disabled", - "Package handler and attribution handler will start as active due to SDK being online"); + "Handlers will start paused due to SDK being offline", + "Handlers will still start as paused", + "Handlers will start as active due to SDK being online"); return; } updateStatus(offline, - "Pausing package and attribution handler to put SDK offline mode", - "Package and attribution handler remain paused due to SDK being disabled", - "Resuming package handler and attribution handler to put SDK in online mode"); + "Pausing handlers to put SDK offline mode", + "Handlers remain paused", + "Resuming handlers to put SDK in online mode"); } @Override public boolean isEnabled() { + return isEnabledI(); + } + + private boolean isEnabledI() { if (activityState != null) { return activityState.enabled; } else { @@ -357,16 +404,16 @@ public boolean isEnabled() { @Override public void readOpenUrl(final Uri url, final long clickTime) { - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - readOpenUrlInternal(url, clickTime); + readOpenUrlI(url, clickTime); } }); } @Override - public boolean updateAttribution(AdjustAttribution attribution) { + public boolean updateAttributionI(AdjustAttribution attribution) { if (attribution == null) { return false; } @@ -375,104 +422,185 @@ public boolean updateAttribution(AdjustAttribution attribution) { return false; } - saveAttribution(attribution); - return true; - } - - private void saveAttribution(AdjustAttribution attribution) { this.attribution = attribution; - writeAttribution(); + writeAttributionI(); + return true; } @Override - public void setAskingAttribution(boolean askingAttribution) { - activityState.askingAttribution = askingAttribution; - writeActivityState(); + public void setAskingAttribution(final boolean askingAttribution) { + Runnable saveAskingAttribution = new Runnable() { + @Override + public void run() { + activityState.askingAttribution = askingAttribution; + } + }; + + writeActivityStateS(saveAskingAttribution); } @Override public void sendReferrer(final String referrer, final long clickTime) { - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - sendReferrerInternal(referrer, clickTime); + sendReferrerI(referrer, clickTime); } }); } @Override public void launchEventResponseTasks(final EventResponseData eventResponseData) { - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - launchEventResponseTasksInternal(eventResponseData); + launchEventResponseTasksI(eventResponseData); } }); } @Override public void launchSessionResponseTasks(final SessionResponseData sessionResponseData) { - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - launchSessionResponseTasksInternal(sessionResponseData); + launchSessionResponseTasksI(sessionResponseData); } }); } @Override public void launchAttributionResponseTasks(final AttributionResponseData attributionResponseData) { - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - launchAttributionResponseTasksInternal(attributionResponseData); + launchAttributionResponseTasksI(attributionResponseData); } }); } - public ActivityPackage getAttributionPackage() { - long now = System.currentTimeMillis(); - PackageBuilder attributionBuilder = new PackageBuilder(adjustConfig, - deviceInfo, - activityState, - now); - return attributionBuilder.buildAttributionPackage(); + @Override + public void sendFirstPackages () { + scheduledExecutor.submit(new Runnable() { + @Override + public void run() { + sendFirstPackagesI(); + } + }); } - public InternalState getInternalState() { - return internalState; + @Override + public void addSessionCallbackParameter(final String key, final String value) { + scheduledExecutor.submit(new Runnable() { + @Override + public void run() { + addSessionCallbackParameterI(key, value); + } + }); } - private void updateHandlersStatusAndSend() { - internalHandler.post(new Runnable() { + @Override + public void addSessionPartnerParameter(final String key, final String value) { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - updateHandlersStatusAndSendInternal(); + addSessionPartnerParameterI(key, value); } }); } - private void foregroundTimerFired() { - internalHandler.post(new Runnable() { + @Override + public void removeSessionCallbackParameter(final String key) { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - foregroundTimerFiredInternal(); + removeSessionCallbackParameterI(key); } }); } - private void backgroundTimerFired() { - internalHandler.post(new Runnable() { + @Override + public void removeSessionPartnerParameter(final String key) { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - backgroundTimerFiredInternal(); + removeSessionPartnerParameterI(key); } }); } - private void initInternal() { + @Override + public void resetSessionCallbackParameters() { + scheduledExecutor.submit(new Runnable() { + @Override + public void run() { + resetSessionCallbackParametersI(); + } + }); + } + + @Override + public void resetSessionPartnerParameters() { + scheduledExecutor.submit(new Runnable() { + @Override + public void run() { + resetSessionPartnerParametersI(); + } + }); + } + + @Override + public void setPushToken(final String token) { + scheduledExecutor.submit(new Runnable() { + @Override + public void run() { + setPushTokenI(token); + } + }); + } + + public ActivityPackage getAttributionPackageI() { + long now = System.currentTimeMillis(); + PackageBuilder attributionBuilder = new PackageBuilder(adjustConfig, + deviceInfo, + activityState, + now); + return attributionBuilder.buildAttributionPackage(); + } + + public InternalState getInternalState() { + return internalState; + } + + private void updateHandlersStatusAndSend() { + scheduledExecutor.submit(new Runnable() { + @Override + public void run() { + updateHandlersStatusAndSendI(); + } + }); + } + + private void initI() { SESSION_INTERVAL = AdjustFactory.getSessionInterval(); SUBSESSION_INTERVAL = AdjustFactory.getSubsessionInterval(); + // get timer values + FOREGROUND_TIMER_INTERVAL = AdjustFactory.getTimerInterval(); + FOREGROUND_TIMER_START = AdjustFactory.getTimerStart(); + BACKGROUND_TIMER_INTERVAL = AdjustFactory.getTimerInterval(); + + // has to be read in the background + readAttributionI(adjustConfig.context); + readActivityStateI(adjustConfig.context); + + sessionParameters = new SessionParameters(); + readSessionCallbackParametersI(adjustConfig.context); + readSessionPartnerParametersI(adjustConfig.context); + + if (activityState != null) { + internalState.enabled = activityState.enabled; + internalState.updatePackages = activityState.updatePackages; + } deviceInfo = new DeviceInfo(adjustConfig.context, adjustConfig.sdkPrefix); @@ -497,36 +625,90 @@ private void initInternal() { logger.info("Default tracker: '%s'", adjustConfig.defaultTracker); } - if (adjustConfig.referrer != null) { - sendReferrer(adjustConfig.referrer, adjustConfig.referrerClickTime); // send to background queue to make sure that activityState is valid + foregroundTimer = new TimerCycle(scheduledExecutor, + new Runnable() { + @Override + public void run() { + foregroundTimerFiredI(); + } + }, FOREGROUND_TIMER_START, FOREGROUND_TIMER_INTERVAL, FOREGROUND_TIMER_NAME); + + // create background timer + if (adjustConfig.sendInBackground) { + logger.info("Send in background configured"); + + backgroundTimer = new TimerOnce(scheduledExecutor, new Runnable() { + @Override + public void run() { + backgroundTimerFiredI(); + } + }, BACKGROUND_TIMER_NAME); } - packageHandler = AdjustFactory.getPackageHandler(this, adjustConfig.context, toSend()); + // configure delay start timer + if (activityState == null && + adjustConfig.delayStart != null && + adjustConfig.delayStart > 0.0) + { + logger.info("Delay start configured"); + internalState.delayStart = true; + delayStartTimer = new TimerOnce(scheduledExecutor, new Runnable() { + @Override + public void run() { + sendFirstPackagesI(); + } + }, DELAY_START_TIMER_NAME); + } + + Util.setUserAgent(adjustConfig.userAgent); + + packageHandler = AdjustFactory.getPackageHandler(this, adjustConfig.context, toSendI(false)); + + ActivityPackage attributionPackage = getAttributionPackageI(); - ActivityPackage attributionPackage = getAttributionPackage(); attributionHandler = AdjustFactory.getAttributionHandler(this, attributionPackage, - toSend(), + toSendI(false), adjustConfig.hasAttributionChangedListener()); - sdkClickHandler = AdjustFactory.getSdkClickHandler(toSend()); + sdkClickHandler = AdjustFactory.getSdkClickHandler(toSendI(true)); + + if (isToUpdatePackagesI()) { + updatePackagesI(); + } + + if (adjustConfig.referrer != null) { + sendReferrerI(adjustConfig.referrer, adjustConfig.referrerClickTime); // send to background queue to make sure that activityState is valid + } + + sessionParametersActionsI(adjustConfig.sessionParametersActionsArray); } - private void startInternal() { + private void sessionParametersActionsI(List sessionParametersActionsArray) { + if (sessionParametersActionsArray == null) { + return; + } + + for (IRunActivityHandler sessionParametersAction : sessionParametersActionsArray) { + sessionParametersAction.run(this); + } + } + + private void startI() { // it shouldn't start if it was disabled after a first session if (activityState != null && !activityState.enabled) { return; } - updateHandlersStatusAndSendInternal(); + updateHandlersStatusAndSendI(); - processSession(); + processSessionI(); - checkAttributionState(); + checkAttributionStateI(); } - private void processSession() { + private void processSessionI() { long now = System.currentTimeMillis(); // very first session @@ -534,10 +716,11 @@ private void processSession() { activityState = new ActivityState(); activityState.sessionCount = 1; // this is the first session - transferSessionPackage(now); + transferSessionPackageI(now); activityState.resetSessionAttributes(now); activityState.enabled = internalState.isEnabled(); - writeActivityState(); + activityState.updatePackages = internalState.isToUpdatePackages(); + writeActivityStateI(); return; } @@ -546,7 +729,7 @@ private void processSession() { if (lastInterval < 0) { logger.error(TIME_TRAVEL); activityState.lastActivity = now; - writeActivityState(); + writeActivityStateI(); return; } @@ -555,9 +738,9 @@ private void processSession() { activityState.sessionCount++; activityState.lastInterval = lastInterval; - transferSessionPackage(now); + transferSessionPackageI(now); activityState.resetSessionAttributes(now); - writeActivityState(); + writeActivityStateI(); return; } @@ -569,15 +752,15 @@ private void processSession() { logger.verbose("Started subsession %d of session %d", activityState.subsessionCount, activityState.sessionCount); - writeActivityState(); + writeActivityStateI(); return; } logger.verbose("Time span since last activity too short for a new subsession"); } - private void checkAttributionState() { - if (!checkActivityState(activityState)) { return; } + private void checkAttributionStateI() { + if (!checkActivityStateI(activityState)) { return; } // if it's a new session if (activityState.subsessionCount <= 1) { @@ -592,29 +775,30 @@ private void checkAttributionState() { attributionHandler.getAttribution(); } - private void endInternal() { + private void endI() { // pause sending if it's not allowed to send - if (!toSend()) { - pauseSending(); + if (!toSendI()) { + pauseSendingI(); } - if (updateActivityState(System.currentTimeMillis())) { - writeActivityState(); + if (updateActivityStateI(System.currentTimeMillis())) { + writeActivityStateI(); } } - private void trackEventInternal(AdjustEvent event) { - if (!checkActivityState(activityState)) return; - if (!this.isEnabled()) return; - if (!checkEvent(event)) return; + private void trackEventI(AdjustEvent event) { + if (!checkActivityStateI(activityState)) return; + if (!isEnabledI()) return; + if (!checkEventI(event)) return; + if (!checkOrderIdI(event.orderId)) return; long now = System.currentTimeMillis(); activityState.eventCount++; - updateActivityState(now); + updateActivityStateI(now); PackageBuilder eventBuilder = new PackageBuilder(adjustConfig, deviceInfo, activityState, now); - ActivityPackage eventPackage = eventBuilder.buildEventPackage(event); + ActivityPackage eventPackage = eventBuilder.buildEventPackage(event, sessionParameters, internalState.isDelayStart()); packageHandler.addPackage(eventPackage); if (adjustConfig.eventBufferingEnabled) { @@ -625,13 +809,13 @@ private void trackEventInternal(AdjustEvent event) { // if it is in the background and it can send, start the background timer if (adjustConfig.sendInBackground && internalState.isBackground()) { - startBackgroundTimer(); + startBackgroundTimerI(); } - writeActivityState(); + writeActivityStateI(); } - private void launchEventResponseTasksInternal(final EventResponseData eventResponseData) { + private void launchEventResponseTasksI(final EventResponseData eventResponseData) { Handler handler = new Handler(adjustConfig.context.getMainLooper()); // success callback @@ -664,26 +848,23 @@ public void run() { } } - private void launchSessionResponseTasksInternal(SessionResponseData sessionResponseData) { + private void launchSessionResponseTasksI(SessionResponseData sessionResponseData) { // use the same handler to ensure that all tasks are executed sequentially Handler handler = new Handler(adjustConfig.context.getMainLooper()); // try to update the attribution - boolean attributionUpdated = updateAttribution(sessionResponseData.attribution); + boolean attributionUpdated = updateAttributionI(sessionResponseData.attribution); // if attribution changed, launch attribution changed delegate if (attributionUpdated) { - launchAttributionListener(handler); + launchAttributionListenerI(handler); } // launch Session tracking listener if available - launchSessionResponseListener(sessionResponseData, handler); - - // if there is any, try to launch the deeplink - prepareDeeplink(sessionResponseData, handler); + launchSessionResponseListenerI(sessionResponseData, handler); } - private void launchSessionResponseListener(final SessionResponseData sessionResponseData, Handler handler) { + private void launchSessionResponseListenerI(final SessionResponseData sessionResponseData, Handler handler) { // success callback if (sessionResponseData.success && adjustConfig.onSessionTrackingSucceededListener != null) { logger.debug("Launching success session tracking listener"); @@ -714,19 +895,22 @@ public void run() { } } - private void launchAttributionResponseTasksInternal(AttributionResponseData responseData) { + private void launchAttributionResponseTasksI(AttributionResponseData attributionResponseData) { Handler handler = new Handler(adjustConfig.context.getMainLooper()); // try to update the attribution - boolean attributionUpdated = updateAttribution(responseData.attribution); + boolean attributionUpdated = updateAttributionI(attributionResponseData.attribution); // if attribution changed, launch attribution changed delegate if (attributionUpdated) { - launchAttributionListener(handler); + launchAttributionListenerI(handler); } + + // if there is any, try to launch the deeplink + prepareDeeplinkI(attributionResponseData.deeplink, handler); } - private void launchAttributionListener(Handler handler) { + private void launchAttributionListenerI(Handler handler) { if (adjustConfig.onAttributionChangedListener == null) { return; } @@ -740,26 +924,21 @@ public void run() { handler.post(runnable); } - private void prepareDeeplink(ResponseData responseData, final Handler handler) { - if (responseData.jsonResponse == null) { - return; - } - - final String deeplink = responseData.jsonResponse.optString("deeplink", null); - + private void prepareDeeplinkI(final Uri deeplink, final Handler handler) { if (deeplink == null) { return; } - final Uri location = Uri.parse(deeplink); - final Intent deeplinkIntent = createDeeplinkIntent(location); + logger.info("Deferred deeplink received (%s)", deeplink); + + final Intent deeplinkIntent = createDeeplinkIntentI(deeplink); Runnable runnable = new Runnable() { @Override public void run() { boolean toLaunchDeeplink = true; if (adjustConfig.onDeeplinkResponseListener != null) { - toLaunchDeeplink = adjustConfig.onDeeplinkResponseListener.launchReceivedDeeplink(location); + toLaunchDeeplink = adjustConfig.onDeeplinkResponseListener.launchReceivedDeeplink(deeplink); } if (toLaunchDeeplink) { launchDeeplinkMain(deeplinkIntent, deeplink); @@ -769,12 +948,12 @@ public void run() { handler.post(runnable); } - private Intent createDeeplinkIntent(Uri location) { + private Intent createDeeplinkIntentI(Uri deeplink) { Intent mapIntent; if (adjustConfig.deepLinkComponent == null) { - mapIntent = new Intent(Intent.ACTION_VIEW, location); + mapIntent = new Intent(Intent.ACTION_VIEW, deeplink); } else { - mapIntent = new Intent(Intent.ACTION_VIEW, location, adjustConfig.context, adjustConfig.deepLinkComponent); + mapIntent = new Intent(Intent.ACTION_VIEW, deeplink, adjustConfig.context, adjustConfig.deepLinkComponent); } mapIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -783,7 +962,7 @@ private Intent createDeeplinkIntent(Uri location) { return mapIntent; } - private void launchDeeplinkMain(final Intent deeplinkIntent, final String deeplink) { + private void launchDeeplinkMain(Intent deeplinkIntent, Uri deeplink) { // Verify it resolves PackageManager packageManager = adjustConfig.context.getPackageManager(); List activities = packageManager.queryIntentActivities(deeplinkIntent, 0); @@ -791,32 +970,33 @@ private void launchDeeplinkMain(final Intent deeplinkIntent, final String deepli // Start an activity if it's safe if (!isIntentSafe) { - logger.error("Unable to open deep link (%s)", deeplink); + logger.error("Unable to open deferred deep link (%s)", deeplink); return; } // add it to the handler queue - logger.info("Open deep link (%s)", deeplink); + logger.info("Open deferred deep link (%s)", deeplink); adjustConfig.context.startActivity(deeplinkIntent); } - private void sendReferrerInternal(String referrer, long clickTime) { + private void sendReferrerI(String referrer, long clickTime) { if (referrer == null || referrer.length() == 0 ) { return; } - PackageBuilder clickPackageBuilder = queryStringClickPackageBuilder(referrer); + PackageBuilder clickPackageBuilder = queryStringClickPackageBuilderI(referrer); if (clickPackageBuilder == null) { return; } clickPackageBuilder.referrer = referrer; - ActivityPackage clickPackage = clickPackageBuilder.buildClickPackage(Constants.REFTAG, clickTime); + clickPackageBuilder.clickTime = clickTime; + ActivityPackage clickPackage = clickPackageBuilder.buildClickPackage(Constants.REFTAG); sdkClickHandler.sendSdkClick(clickPackage); } - private void readOpenUrlInternal(Uri url, long clickTime) { + private void readOpenUrlI(Uri url, long clickTime) { if (url == null) { return; } @@ -827,18 +1007,19 @@ private void readOpenUrlInternal(Uri url, long clickTime) { queryString = ""; } - PackageBuilder clickPackageBuilder = queryStringClickPackageBuilder(queryString); + PackageBuilder clickPackageBuilder = queryStringClickPackageBuilderI(queryString); if (clickPackageBuilder == null) { return; } clickPackageBuilder.deeplink = url.toString(); - ActivityPackage clickPackage = clickPackageBuilder.buildClickPackage(Constants.DEEPLINK, clickTime); + clickPackageBuilder.clickTime = clickTime; + ActivityPackage clickPackage = clickPackageBuilder.buildClickPackage(Constants.DEEPLINK); sdkClickHandler.sendSdkClick(clickPackage); } - private PackageBuilder queryStringClickPackageBuilder(String queryString) { + private PackageBuilder queryStringClickPackageBuilderI(String queryString) { if (queryString == null) { return null; } @@ -851,7 +1032,7 @@ private PackageBuilder queryStringClickPackageBuilder(String queryString) { String[] queryPairs = queryString.split("&"); for (String pair : queryPairs) { - readQueryString(pair, queryStringParameters, queryStringAttribution); + readQueryStringI(pair, queryStringParameters, queryStringAttribution); } String reftag = queryStringParameters.remove(Constants.REFTAG); @@ -865,9 +1046,9 @@ private PackageBuilder queryStringClickPackageBuilder(String queryString) { return builder; } - private boolean readQueryString(String queryString, - Map extraParameters, - AdjustAttribution queryStringAttribution) { + private boolean readQueryStringI(String queryString, + Map extraParameters, + AdjustAttribution queryStringAttribution) { String[] pairComponents = queryString.split("="); if (pairComponents.length != 2) return false; @@ -880,16 +1061,16 @@ private boolean readQueryString(String queryString, String keyWOutPrefix = key.substring(ADJUST_PREFIX.length()); if (keyWOutPrefix.length() == 0) return false; - if (!trySetAttribution(queryStringAttribution, keyWOutPrefix, value)) { + if (!trySetAttributionI(queryStringAttribution, keyWOutPrefix, value)) { extraParameters.put(keyWOutPrefix, value); } return true; } - private boolean trySetAttribution(AdjustAttribution queryStringAttribution, - String key, - String value) { + private boolean trySetAttributionI(AdjustAttribution queryStringAttribution, + String key, + String value) { if (key.equals("tracker")) { queryStringAttribution.trackerName = value; return true; @@ -913,14 +1094,14 @@ private boolean trySetAttribution(AdjustAttribution queryStringAttribution, return false; } - private void updateHandlersStatusAndSendInternal() { + private void updateHandlersStatusAndSendI() { // check if it should stop sending - if (!toSend()) { - pauseSending(); + if (!toSendI()) { + pauseSendingI(); return; } - resumeSending(); + resumeSendingI(); // try to send if (!adjustConfig.eventBufferingEnabled) { @@ -928,22 +1109,29 @@ private void updateHandlersStatusAndSendInternal() { } } - private void pauseSending() { + private void pauseSendingI() { attributionHandler.pauseSending(); packageHandler.pauseSending(); - sdkClickHandler.pauseSending(); + // the conditions to pause the sdk click handler are less restrictive + // it's possible for the sdk click handler to be active while others are paused + if (!toSendI(true)) { + sdkClickHandler.pauseSending(); + } else { + sdkClickHandler.resumeSending(); + } } - private void resumeSending() { + private void resumeSendingI() { attributionHandler.resumeSending(); packageHandler.resumeSending(); sdkClickHandler.resumeSending(); } - private boolean updateActivityState(long now) { - if (!checkActivityState(activityState)) { return false; } + private boolean updateActivityStateI(long now) { + if (!checkActivityStateI(activityState)) { return false; } long lastInterval = now - activityState.lastActivity; + // ignore late updates if (lastInterval > SESSION_INTERVAL) { return false; @@ -967,43 +1155,57 @@ public static boolean deleteAttribution(Context context) { return context.deleteFile(ATTRIBUTION_FILENAME); } - private void transferSessionPackage(long now) { + public static boolean deleteSessionCallbackParameters(Context context) { + return context.deleteFile(SESSION_CALLBACK_PARAMETERS_FILENAME); + } + + public static boolean deleteSessionPartnerParameters(Context context) { + return context.deleteFile(SESSION_PARTNER_PARAMETERS_FILENAME); + } + + private void transferSessionPackageI(long now) { PackageBuilder builder = new PackageBuilder(adjustConfig, deviceInfo, activityState, now); - ActivityPackage sessionPackage = builder.buildSessionPackage(); + ActivityPackage sessionPackage = builder.buildSessionPackage(sessionParameters, internalState.isDelayStart()); packageHandler.addPackage(sessionPackage); packageHandler.sendFirstPackage(); } - private void startForegroundTimer() { - // don't start the timer if it's disabled or offline - if (paused()) { + private void startForegroundTimerI() { + // don't start the timer if it's disabled + if (!isEnabledI()) { return; } foregroundTimer.start(); } - private void stopForegroundTimer() { + private void stopForegroundTimerI() { foregroundTimer.suspend(); } - private void foregroundTimerFiredInternal() { - if (paused()) { - // stop the timer cycle if it's disabled/offline - stopForegroundTimer(); + private void foregroundTimerFiredI() { + // stop the timer cycle if it's disabled + if (!isEnabledI()) { + stopForegroundTimerI(); return; } - packageHandler.sendFirstPackage(); + if (toSendI()) { + packageHandler.sendFirstPackage(); + } - if (updateActivityState(System.currentTimeMillis())) { - writeActivityState(); + if (updateActivityStateI(System.currentTimeMillis())) { + writeActivityStateI(); } } - private void startBackgroundTimer() { + private void startBackgroundTimerI() { + if (backgroundTimer == null) { + return; + } + // check if it can send in the background - if (!toSend()) { + if (!toSendI()) { return; } @@ -1015,15 +1217,226 @@ private void startBackgroundTimer() { backgroundTimer.startIn(BACKGROUND_TIMER_INTERVAL); } - private void stopBackgroundTimer() { + private void stopBackgroundTimerI() { + if (backgroundTimer == null) { + return; + } + backgroundTimer.cancel(); } - private void backgroundTimerFiredInternal() { - packageHandler.sendFirstPackage(); + private void backgroundTimerFiredI() { + if (toSendI()) { + packageHandler.sendFirstPackage(); + } + } + + private void delayStartI() { + // it's not configured to start delayed or already finished + if (internalState.isToStartNow()) { + return; + } + + // the delay has already started + if (isToUpdatePackagesI()) { + return; + } + + // check against max start delay + double delayStartSeconds = adjustConfig.delayStart != null ? adjustConfig.delayStart : 0.0; + long maxDelayStartMilli = AdjustFactory.getMaxDelayStart(); + + long delayStartMilli = (long)(delayStartSeconds * 1000); + if (delayStartMilli > maxDelayStartMilli) { + double maxDelayStartSeconds = maxDelayStartMilli / 1000; + String delayStartFormatted = Util.SecondsDisplayFormat.format(delayStartSeconds); + String maxDelayStartFormatted = Util.SecondsDisplayFormat.format(maxDelayStartSeconds); + + logger.warn("Delay start of %s seconds bigger than max allowed value of %s seconds", delayStartFormatted, maxDelayStartFormatted); + delayStartMilli = maxDelayStartMilli; + delayStartSeconds = maxDelayStartSeconds; + } + + String delayStartFormatted = Util.SecondsDisplayFormat.format(delayStartSeconds); + logger.info("Waiting %s seconds before starting first session", delayStartFormatted); + + delayStartTimer.startIn(delayStartMilli); + + internalState.updatePackages = true; + + if (activityState != null) { + activityState.updatePackages = true; + writeActivityStateI(); + } } - private void readActivityState(Context context) { + private void sendFirstPackagesI() { + if (internalState.isToStartNow()) { + logger.info("Start delay expired or never configured"); + return; + } + + // update packages in queue + updatePackagesI(); + // no longer is in delay start + internalState.delayStart = false; + // cancel possible still running timer if it was called by user + delayStartTimer.cancel(); + // and release timer + delayStartTimer = null; + // update the status and try to send first package + updateHandlersStatusAndSendI(); + } + + private void updatePackagesI() { + // update activity packages + packageHandler.updatePackages(sessionParameters); + // no longer needs to update packages + internalState.updatePackages = false; + if (activityState != null) { + activityState.updatePackages = false; + writeActivityStateI(); + } + } + + private boolean isToUpdatePackagesI() { + if (activityState != null) { + return activityState.updatePackages; + } else { + return internalState.isToUpdatePackages(); + } + } + + public void addSessionCallbackParameterI(String key, String value) { + if (!Util.isValidParameter(key, "key", "Session Callback")) return; + if (!Util.isValidParameter(value, "value", "Session Callback")) return; + + if (sessionParameters.callbackParameters == null) { + sessionParameters.callbackParameters = new LinkedHashMap(); + } + + String oldValue = sessionParameters.callbackParameters.get(key); + + if (value.equals(oldValue)) { + logger.verbose("Key %s already present with the same value", key); + return; + } + + if (oldValue != null) { + logger.warn("Key %s will be overwritten", key); + } + + sessionParameters.callbackParameters.put(key, value); + + writeSessionCallbackParametersI(); + } + + public void addSessionPartnerParameterI(String key, String value) { + if (!Util.isValidParameter(key, "key", "Session Partner")) return; + if (!Util.isValidParameter(value, "value", "Session Partner")) return; + + if (sessionParameters.partnerParameters == null) { + sessionParameters.partnerParameters = new LinkedHashMap(); + } + + String oldValue = sessionParameters.partnerParameters.get(key); + + if (value.equals(oldValue)) { + logger.verbose("Key %s already present with the same value", key); + return; + } + + if (oldValue != null) { + logger.warn("Key %s will be overwritten", key); + } + + sessionParameters.partnerParameters.put(key, value); + + writeSessionPartnerParametersI(); + } + + public void removeSessionCallbackParameterI(String key) { + if (!Util.isValidParameter(key, "key", "Session Callback")) return; + + if (sessionParameters.callbackParameters == null) { + logger.warn("Session Callback parameters are not set"); + return; + } + + String oldValue = sessionParameters.callbackParameters.remove(key); + + if (oldValue == null) { + logger.warn("Key %s does not exist", key); + return; + } + + logger.debug("Key %s will be removed", key); + + writeSessionCallbackParametersI(); + } + + public void removeSessionPartnerParameterI(String key) { + if (!Util.isValidParameter(key, "key", "Session Partner")) return; + + if (sessionParameters.partnerParameters == null) { + logger.warn("Session Partner parameters are not set"); + return; + } + + String oldValue = sessionParameters.partnerParameters.remove(key); + + if (oldValue == null) { + logger.warn("Key %s does not exist", key); + return; + } + + logger.debug("Key %s will be removed", key); + + writeSessionPartnerParametersI(); + } + + public void resetSessionCallbackParametersI() { + if (sessionParameters.callbackParameters == null) { + logger.warn("Session Callback parameters are not set"); + } + + sessionParameters.callbackParameters = null; + + writeSessionCallbackParametersI(); + } + + public void resetSessionPartnerParametersI() { + if (sessionParameters.partnerParameters == null) { + logger.warn("Session Partner parameters are not set"); + } + + sessionParameters.partnerParameters = null; + + writeSessionPartnerParametersI(); + } + + private void setPushTokenI(String token) { + if (token == null) { + return; + } + + if (token.equals(activityState.pushToken)) { + return; + } + + long now = System.currentTimeMillis(); + PackageBuilder clickPackageBuilder = new PackageBuilder(adjustConfig, deviceInfo, activityState, now); + clickPackageBuilder.pushToken = token; + + ActivityPackage clickPackage = clickPackageBuilder.buildClickPackage(Constants.PUSH); + sdkClickHandler.sendSdkClick(clickPackage); + + // save new push token + activityState.pushToken = token; + writeActivityStateI(); + } + + private void readActivityStateI(Context context) { try { activityState = Util.readObject(context, ACTIVITY_STATE_FILENAME, ACTIVITY_STATE_NAME, ActivityState.class); } catch (Exception e) { @@ -1032,7 +1445,7 @@ private void readActivityState(Context context) { } } - private void readAttribution(Context context) { + private void readAttributionI(Context context) { try { attribution = Util.readObject(context, ATTRIBUTION_FILENAME, ATTRIBUTION_NAME, AdjustAttribution.class); } catch (Exception e) { @@ -1041,15 +1454,111 @@ private void readAttribution(Context context) { } } - private synchronized void writeActivityState() { - Util.writeObject(activityState, adjustConfig.context, ACTIVITY_STATE_FILENAME, ACTIVITY_STATE_NAME); + private void readSessionCallbackParametersI(Context context) { + try { + sessionParameters.callbackParameters = Util.readObject(context, + SESSION_CALLBACK_PARAMETERS_FILENAME, + SESSION_CALLBACK_PARAMETERS_NAME, + (Class>)(Class)Map.class); + } catch (Exception e) { + logger.error("Failed to read %s file (%s)", SESSION_CALLBACK_PARAMETERS_NAME, e.getMessage()); + sessionParameters.callbackParameters = null; + } + } + + private void readSessionPartnerParametersI(Context context) { + try { + sessionParameters.partnerParameters = Util.readObject(context, + SESSION_PARTNER_PARAMETERS_FILENAME, + SESSION_PARTNER_PARAMETERS_NAME, + (Class>)(Class)Map.class); + } catch (Exception e) { + logger.error("Failed to read %s file (%s)", SESSION_PARTNER_PARAMETERS_NAME, e.getMessage()); + sessionParameters.partnerParameters = null; + } + } + + private void writeActivityStateI() { + writeActivityStateS(null); + } + + private void writeActivityStateS(Runnable changeActivityState) { + synchronized (ActivityState.class) { + if (activityState == null) { + return; + } + if (changeActivityState != null) { + changeActivityState.run(); + } + Util.writeObject(activityState, adjustConfig.context, ACTIVITY_STATE_FILENAME, ACTIVITY_STATE_NAME); + } + } + + private void teardownActivityStateS(boolean toDelete) { + synchronized (ActivityState.class) { + if (activityState == null) { + return; + } + if (toDelete && adjustConfig != null && adjustConfig.context != null) { + deleteActivityState(adjustConfig.context); + } + activityState = null; + } + } + + private void writeAttributionI() { + synchronized (AdjustAttribution.class) { + if (attribution == null) { + return; + } + Util.writeObject(attribution, adjustConfig.context, ATTRIBUTION_FILENAME, ATTRIBUTION_NAME); + } + } + + private void teardownAttributionS(boolean toDelete) { + synchronized (AdjustAttribution.class) { + if (attribution == null) { + return; + } + if (toDelete && adjustConfig != null && adjustConfig.context != null) { + deleteAttribution(adjustConfig.context); + } + attribution = null; + } + } + + private void writeSessionCallbackParametersI() { + synchronized (SessionParameters.class) { + if (sessionParameters == null) { + return; + } + Util.writeObject(sessionParameters.callbackParameters, adjustConfig.context, SESSION_CALLBACK_PARAMETERS_FILENAME, SESSION_CALLBACK_PARAMETERS_NAME); + } } - private void writeAttribution() { - Util.writeObject(attribution, adjustConfig.context, ATTRIBUTION_FILENAME, ATTRIBUTION_NAME); + private void writeSessionPartnerParametersI() { + synchronized (SessionParameters.class) { + if (sessionParameters == null) { + return; + } + Util.writeObject(sessionParameters.partnerParameters, adjustConfig.context, SESSION_PARTNER_PARAMETERS_FILENAME, SESSION_PARTNER_PARAMETERS_NAME); + } } - private boolean checkEvent(AdjustEvent event) { + private void teardownAllSessionParametersS(boolean toDelete) { + synchronized (SessionParameters.class) { + if (sessionParameters == null) { + return; + } + if (toDelete && adjustConfig != null && adjustConfig.context != null) { + deleteSessionCallbackParameters(adjustConfig.context); + deleteSessionPartnerParameters(adjustConfig.context); + } + sessionParameters = null; + } + } + + private boolean checkEventI(AdjustEvent event) { if (event == null) { logger.error("Event missing"); return false; @@ -1063,7 +1572,23 @@ private boolean checkEvent(AdjustEvent event) { return true; } - private boolean checkActivityState(ActivityState activityState) { + private boolean checkOrderIdI(String orderId) { + if (orderId == null || orderId.isEmpty()) { + return true; // no order ID given + } + + if (activityState.findOrderId(orderId)) { + logger.info("Skipping duplicated order ID '%s'", orderId); + return false; // order ID found -> used already + } + + activityState.addOrderId(orderId); + logger.verbose("Added order ID '%s'", orderId); + // activity state will get written by caller + return true; + } + + private boolean checkActivityStateI(ActivityState activityState) { if (activityState == null) { logger.error("Missing activity state"); return false; @@ -1071,13 +1596,29 @@ private boolean checkActivityState(ActivityState activityState) { return true; } - private boolean paused() { - return internalState.isOffline() || !this.isEnabled(); + private boolean pausedI() { + return pausedI(false); + } + + private boolean pausedI(boolean sdkClickHandlerOnly) { + if (sdkClickHandlerOnly) { + // sdk click handler is paused if either: + return internalState.isOffline() || // it's offline + !isEnabledI(); // is disabled + } + // other handlers are paused if either: + return internalState.isOffline() || // it's offline + !isEnabledI() || // is disabled + internalState.isDelayStart(); // is in delayed start + } + + private boolean toSendI() { + return toSendI(false); } - private boolean toSend() { - // if it's offline, disabled -> don't send - if (paused()) { + private boolean toSendI(boolean sdkClickHandlerOnly) { + // don't send when it's paused + if (pausedI(sdkClickHandlerOnly)) { return false; } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityPackage.java b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityPackage.java index aff2421c5..aa7608105 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityPackage.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityPackage.java @@ -25,9 +25,11 @@ public class ActivityPackage implements Serializable { private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("path", String.class), new ObjectStreamField("clientSdk", String.class), - new ObjectStreamField("parameters", (Class>)((Class)Map.class)), + new ObjectStreamField("parameters", (Class>)(Class)Map.class), new ObjectStreamField("activityKind", ActivityKind.class), - new ObjectStreamField("suffix", String.class) + new ObjectStreamField("suffix", String.class), + new ObjectStreamField("callbackParameters", (Class>)(Class)Map.class), + new ObjectStreamField("partnerParameters", (Class>)(Class)Map.class), }; private transient int hashCode; @@ -41,6 +43,10 @@ public class ActivityPackage implements Serializable { private ActivityKind activityKind = ActivityKind.UNKNOWN; private String suffix; + // delay + private Map callbackParameters; + private Map partnerParameters; + private int retries; public String getPath() { @@ -67,6 +73,14 @@ public void setParameters(Map parameters) { this.parameters = parameters; } + public void setCallbackParameters(Map callbackParameters) { + this.callbackParameters = callbackParameters; + } + + public void setPartnerParameters(Map partnerParameters) { + this.partnerParameters = partnerParameters; + } + public ActivityKind getActivityKind() { return activityKind; } @@ -87,6 +101,14 @@ public int increaseRetries() { return retries; } + public Map getCallbackParameters() { + return callbackParameters; + } + + public Map getPartnerParameters() { + return partnerParameters; + } + public ActivityPackage(ActivityKind activityKind) { this.activityKind = activityKind; } @@ -126,6 +148,8 @@ private void readObject(ObjectInputStream stream) throws ClassNotFoundException, parameters = Util.readObjectField(fields, "parameters", null); activityKind = Util.readObjectField(fields, "activityKind", ActivityKind.UNKNOWN); suffix = Util.readStringField(fields, "suffix", null); + callbackParameters = Util.readObjectField(fields, "callbackParameters", null); + partnerParameters = Util.readObjectField(fields, "partnerParameters", null); } @Override @@ -137,9 +161,11 @@ public boolean equals(Object other) { if (!Util.equalString(path, otherActivityPackage.path)) return false; if (!Util.equalString(clientSdk, otherActivityPackage.clientSdk)) return false; - if (!Util.equalsMap(parameters, otherActivityPackage.parameters)) return false; + if (!Util.equalObject(parameters, otherActivityPackage.parameters)) return false; if (!Util.equalEnum(activityKind, otherActivityPackage.activityKind)) return false; if (!Util.equalString(suffix, otherActivityPackage.suffix)) return false; + if (!Util.equalObject(callbackParameters, otherActivityPackage.callbackParameters)) return false; + if (!Util.equalObject(partnerParameters, otherActivityPackage.partnerParameters)) return false; return true; } @@ -149,9 +175,11 @@ public int hashCode() { hashCode = 17; hashCode = 37 * hashCode + Util.hashString(path); hashCode = 37 * hashCode + Util.hashString(clientSdk); - hashCode = 37 * hashCode + Util.hashMap(parameters); + hashCode = 37 * hashCode + Util.hashObject(parameters); hashCode = 37 * hashCode + Util.hashEnum(activityKind); hashCode = 37 * hashCode + Util.hashString(suffix); + hashCode = 37 * hashCode + Util.hashObject(callbackParameters); + hashCode = 37 * hashCode + Util.hashObject(partnerParameters); } return hashCode; } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityState.java b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityState.java index b00aed10d..0faf85168 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityState.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/ActivityState.java @@ -16,10 +16,13 @@ import java.io.ObjectStreamField; import java.io.Serializable; import java.util.Calendar; +import java.util.LinkedList; +import java.util.List; import java.util.Locale; public class ActivityState implements Serializable, Cloneable { private static final long serialVersionUID = 9039439291143138148L; + private static int ORDER_ID_MAXCOUNT = 10; private transient ILogger logger; private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("uuid", String.class), @@ -31,7 +34,10 @@ public class ActivityState implements Serializable, Cloneable { new ObjectStreamField("sessionLength", long.class), new ObjectStreamField("timeSpent", long.class), new ObjectStreamField("lastActivity", long.class), - new ObjectStreamField("lastInterval", long.class) + new ObjectStreamField("lastInterval", long.class), + new ObjectStreamField("updatePackages", boolean.class), + new ObjectStreamField("orderIds", (Class>)(Class) LinkedList.class), + new ObjectStreamField("pushToken", String.class) }; // persistent data @@ -51,6 +57,12 @@ public class ActivityState implements Serializable, Cloneable { protected long lastInterval; + protected boolean updatePackages; + + protected LinkedList orderIds; + + protected String pushToken; + protected ActivityState() { logger = AdjustFactory.getLogger(); // create UUID for new devices @@ -65,6 +77,9 @@ protected ActivityState() { timeSpent = -1; // this information will be collected and attached to the next session lastActivity = -1; lastInterval = -1; + updatePackages = false; + orderIds = null; + pushToken = null; } protected void resetSessionAttributes(long now) { @@ -75,6 +90,24 @@ protected void resetSessionAttributes(long now) { lastInterval = -1; } + protected void addOrderId(String orderId) { + if (orderIds == null) { + orderIds = new LinkedList(); + } + + if (orderIds.size() >= ORDER_ID_MAXCOUNT) { + orderIds.removeLast(); + } + orderIds.addFirst(orderId); + } + + protected boolean findOrderId(String orderId) { + if (orderIds == null) { + return false; + } + return orderIds.contains(orderId); + } + @Override public String toString() { return String.format(Locale.US, @@ -84,14 +117,6 @@ public String toString() { stamp(lastActivity), uuid); } - public ActivityState shallowCopy() { - try { - return (ActivityState) super.clone(); - } catch (CloneNotSupportedException e) { - return null; - } - } - @Override public boolean equals(Object other) { if (other == this) return true; @@ -108,6 +133,9 @@ public boolean equals(Object other) { if (!Util.equalLong(sessionLength, otherActivityState.sessionLength)) return false; if (!Util.equalLong(timeSpent, otherActivityState.timeSpent)) return false; if (!Util.equalLong(lastInterval, otherActivityState.lastInterval)) return false; + if (!Util.equalBoolean(updatePackages, otherActivityState.updatePackages)) return false; + if (!Util.equalObject(orderIds, otherActivityState.orderIds)) return false; + if (!Util.equalString(pushToken, otherActivityState.pushToken)) return false; return true; } @@ -123,10 +151,12 @@ public int hashCode() { hashCode = 37 * hashCode + Util.hashLong(sessionLength); hashCode = 37 * hashCode + Util.hashLong(timeSpent); hashCode = 37 * hashCode + Util.hashLong(lastInterval); + hashCode = 37 * hashCode + Util.hashBoolean(updatePackages); + hashCode = 37 * hashCode + Util.hashObject(orderIds); + hashCode = 37 * hashCode + Util.hashString(pushToken); return hashCode; } - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { GetField fields = stream.readFields(); @@ -143,6 +173,12 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo enabled = Util.readBooleanField(fields, "enabled", true); askingAttribution = Util.readBooleanField(fields, "askingAttribution", false); + updatePackages = Util.readBooleanField(fields, "updatePackages", false); + + orderIds = Util.readObjectField(fields, "orderIds", null); + + pushToken = Util.readStringField(fields, "pushToken", null); + // create UUID for migrating devices if (uuid == null) { uuid = Util.createUuid(); diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/Adjust.java b/Adjust/adjust/src/main/java/com/adjust/sdk/Adjust.java index 6f0fdde69..f95db2f34 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/Adjust.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/Adjust.java @@ -76,9 +76,47 @@ public static void setOfflineMode(boolean enabled) { adjustInstance.setOfflineMode(enabled); } + public static void sendFirstPackages() { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.sendFirstPackages(); + } + + public static void addSessionCallbackParameter(String key, String value) { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.addSessionCallbackParameter(key, value); + } + + public static void addSessionPartnerParameter(String key, String value) { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.addSessionPartnerParameter(key, value); + } + + public static void removeSessionCallbackParameter(String key) { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.removeSessionCallbackParameter(key); + } + + public static void removeSessionPartnerParameter(String key) { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.removeSessionPartnerParameter(key); + } + + public static void resetSessionCallbackParameters() { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.resetSessionCallbackParameters(); + } + + public static void resetSessionPartnerParameters() { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.resetSessionPartnerParameters(); + } + + public static void setPushToken(String token) { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.setPushToken(token); + } + public static void getGoogleAdId(Context context, OnDeviceIdsRead onDeviceIdRead) { Util.getGoogleAdId(context, onDeviceIdRead); } } - - diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustConfig.java b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustConfig.java index 742a0d8b0..134d4a549 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustConfig.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustConfig.java @@ -2,6 +2,8 @@ import android.content.Context; +import java.util.List; + /** * Created by pfms on 06/11/14. */ @@ -10,7 +12,6 @@ public class AdjustConfig { String appToken; String environment; String processName; - LogLevel logLevel; String sdkPrefix; boolean eventBufferingEnabled; String defaultTracker; @@ -25,11 +26,29 @@ public class AdjustConfig { OnSessionTrackingFailedListener onSessionTrackingFailedListener; OnDeeplinkResponseListener onDeeplinkResponseListener; boolean sendInBackground; + Double delayStart; + List sessionParametersActionsArray; + boolean allowSuppressLogLevel; + ILogger logger; + String userAgent; public static final String ENVIRONMENT_SANDBOX = "sandbox"; public static final String ENVIRONMENT_PRODUCTION = "production"; public AdjustConfig(Context context, String appToken, String environment) { + init(context, appToken, environment, false); + } + + public AdjustConfig(Context context, String appToken, String environment, boolean allowSuppressLogLevel) { + init(context, appToken, environment, allowSuppressLogLevel); + } + + private void init(Context context, String appToken, String environment, boolean allowSuppressLogLevel) { + this.allowSuppressLogLevel = allowSuppressLogLevel; + logger = AdjustFactory.getLogger(); + // default values + setLogLevel(LogLevel.INFO, environment); + if (!isValid(context, appToken, environment)) { return; } @@ -39,7 +58,6 @@ public AdjustConfig(Context context, String appToken, String environment) { this.environment = environment; // default values - this.logLevel = LogLevel.INFO; this.eventBufferingEnabled = false; this.sendInBackground = false; } @@ -57,7 +75,7 @@ public void setSendInBackground(boolean sendInBackground) { } public void setLogLevel(LogLevel logLevel) { - this.logLevel = logLevel; + setLogLevel(logLevel, environment); } public void setSdkPrefix(String sdkPrefix) { @@ -102,6 +120,13 @@ public void setOnDeeplinkResponseListener(OnDeeplinkResponseListener onDeeplinkR this.onDeeplinkResponseListener = onDeeplinkResponseListener; } + public void setDelayStart(double delayStart) { + this.delayStart = delayStart; + } + + public void setUserAgent(String userAgent) { + this.userAgent = userAgent; + } public boolean hasAttributionChangedListener() { return onAttributionChangedListener != null; } @@ -126,8 +151,29 @@ private boolean isValid(Context context, String appToken, String environment) { return true; } - private static boolean checkContext(Context context) { - ILogger logger = AdjustFactory.getLogger(); + private void setLogLevel(LogLevel logLevel, String environment) { + LogLevel newLogLevel = null; + if (ENVIRONMENT_PRODUCTION.equals(environment)) { + // production && allows supress -> Supress + if (allowSuppressLogLevel) { + newLogLevel = LogLevel.SUPRESS; + } else { + // production && not allow supress -> Assert + newLogLevel = LogLevel.ASSERT; + } + } else { + // not allow supress && try supress -> Assert + if (!allowSuppressLogLevel && + logLevel == LogLevel.SUPRESS) { + newLogLevel = LogLevel.ASSERT; + } else { + newLogLevel = logLevel; + } + } + logger.setLogLevel(newLogLevel); + } + + private boolean checkContext(Context context) { if (context == null) { logger.error("Missing context"); return false; @@ -141,8 +187,7 @@ private static boolean checkContext(Context context) { return true; } - private static boolean checkAppToken(String appToken) { - ILogger logger = AdjustFactory.getLogger(); + private boolean checkAppToken(String appToken) { if (appToken == null) { logger.error("Missing App Token"); return false; @@ -156,8 +201,7 @@ private static boolean checkAppToken(String appToken) { return true; } - private static boolean checkEnvironment(String environment) { - ILogger logger = AdjustFactory.getLogger(); + private boolean checkEnvironment(String environment) { if (environment == null) { logger.error("Missing environment"); return false; diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustEvent.java b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustEvent.java index ae5fdc3a7..8bd6dd5a9 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustEvent.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustEvent.java @@ -12,6 +12,7 @@ public class AdjustEvent { String currency; Map callbackParameters; Map partnerParameters; + String orderId; private static ILogger logger = AdjustFactory.getLogger(); @@ -29,8 +30,8 @@ public void setRevenue(double revenue, String currency) { } public void addCallbackParameter(String key, String value) { - if (!isValidParameter(key, "key", "Callback")) return; - if (!isValidParameter(value, "value", "Callback")) return; + if (!Util.isValidParameter(key, "key", "Callback")) return; + if (!Util.isValidParameter(value, "value", "Callback")) return; if (callbackParameters == null) { callbackParameters = new LinkedHashMap(); @@ -39,13 +40,13 @@ public void addCallbackParameter(String key, String value) { String previousValue = callbackParameters.put(key, value); if (previousValue != null) { - logger.warn("key %s was overwritten", key); + logger.warn("Key %s was overwritten", key); } } public void addPartnerParameter(String key, String value) { - if (!isValidParameter(key, "key", "Partner")) return; - if (!isValidParameter(value, "value", "Partner")) return; + if (!Util.isValidParameter(key, "key", "Partner")) return; + if (!Util.isValidParameter(value, "value", "Partner")) return; if (partnerParameters == null) { partnerParameters = new LinkedHashMap(); @@ -54,10 +55,14 @@ public void addPartnerParameter(String key, String value) { String previousValue = partnerParameters.put(key, value); if (previousValue != null) { - logger.warn("key %s was overwritten", key); + logger.warn("Key %s was overwritten", key); } } + public void setOrderId(String orderId) { + this.orderId = orderId; + } + public boolean isValid() { return eventToken != null; } @@ -96,17 +101,4 @@ private boolean checkRevenue(Double revenue, String currency) { } return true; } - - private boolean isValidParameter(String attribute, String attributeType, String parameterName) { - if (attribute == null) { - logger.error("%s parameter %s is missing", parameterName, attributeType); - return false; - } - if (attribute.equals("")) { - logger.error("%s parameter %s is empty", parameterName, attributeType); - return false; - } - - return true; - } } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustFactory.java b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustFactory.java index aaffeb235..c8c41282a 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustFactory.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustFactory.java @@ -22,6 +22,7 @@ public class AdjustFactory { private static long subsessionInterval = -1; private static BackoffStrategy sdkClickBackoffStrategy = null; private static BackoffStrategy packageHandlerBackoffStrategy = null; + private static long maxDelayStart = -1; public static class URLGetConnection { HttpsURLConnection httpsURLConnection; @@ -144,6 +145,13 @@ public static ISdkClickHandler getSdkClickHandler(boolean startsSending) { return sdkClickHandler; } + public static long getMaxDelayStart() { + if (maxDelayStart == -1) { + return Constants.ONE_SECOND * 10; // 10 seconds + } + return maxDelayStart; + } + public static void setPackageHandler(IPackageHandler packageHandler) { AdjustFactory.packageHandler = packageHandler; } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustInstance.java b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustInstance.java index fac831993..4d1482653 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustInstance.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/AdjustInstance.java @@ -2,14 +2,17 @@ import android.net.Uri; +import java.util.ArrayList; +import java.util.List; + /** * Created by pfms on 04/12/14. */ public class AdjustInstance { - private String referrer; private long referrerClickTime; private ActivityHandler activityHandler; + private List sessionParametersActionsArray; private static ILogger getLogger() { return AdjustFactory.getLogger(); @@ -23,6 +26,7 @@ public void onCreate(AdjustConfig adjustConfig) { adjustConfig.referrer = this.referrer; adjustConfig.referrerClickTime = this.referrerClickTime; + adjustConfig.sessionParametersActionsArray = sessionParametersActionsArray; activityHandler = ActivityHandler.getInstance(adjustConfig); } @@ -75,6 +79,131 @@ public void setOfflineMode(boolean enabled) { activityHandler.setOfflineMode(enabled); } + + public void sendFirstPackages() { + if (!checkActivityHandler()) return; + activityHandler.sendFirstPackages(); + } + + public void addSessionCallbackParameter(final String key, final String value) { + if (activityHandler != null) { + activityHandler.addSessionCallbackParameter(key, value); + return; + } + + if (sessionParametersActionsArray == null) { + sessionParametersActionsArray = new ArrayList(); + } + + sessionParametersActionsArray.add(new IRunActivityHandler() { + @Override + public void run(ActivityHandler activityHandler) { + activityHandler.addSessionCallbackParameterI(key, value); + } + }); + } + + public void addSessionPartnerParameter(final String key, final String value) { + if (activityHandler != null) { + activityHandler.addSessionPartnerParameter(key, value); + return; + } + + if (sessionParametersActionsArray == null) { + sessionParametersActionsArray = new ArrayList(); + } + + sessionParametersActionsArray.add(new IRunActivityHandler() { + @Override + public void run(ActivityHandler activityHandler) { + activityHandler.addSessionPartnerParameterI(key, value); + } + }); + } + + public void removeSessionCallbackParameter(final String key) { + if (activityHandler != null) { + activityHandler.removeSessionCallbackParameter(key); + return; + } + + if (sessionParametersActionsArray == null) { + sessionParametersActionsArray = new ArrayList(); + } + + sessionParametersActionsArray.add(new IRunActivityHandler() { + @Override + public void run(ActivityHandler activityHandler) { + activityHandler.removeSessionCallbackParameterI(key); + } + }); + } + + public void removeSessionPartnerParameter(final String key) { + if (activityHandler != null) { + activityHandler.removeSessionPartnerParameter(key); + return; + } + + if (sessionParametersActionsArray == null) { + sessionParametersActionsArray = new ArrayList(); + } + + sessionParametersActionsArray.add(new IRunActivityHandler() { + @Override + public void run(ActivityHandler activityHandler) { + activityHandler.removeSessionPartnerParameterI(key); + } + }); + } + + public void resetSessionCallbackParameters() { + if (activityHandler != null) { + activityHandler.resetSessionCallbackParameters(); + return; + } + + if (sessionParametersActionsArray == null) { + sessionParametersActionsArray = new ArrayList(); + } + + sessionParametersActionsArray.add(new IRunActivityHandler() { + @Override + public void run(ActivityHandler activityHandler) { + activityHandler.resetSessionCallbackParametersI(); + } + }); + } + + public void resetSessionPartnerParameters() { + if (activityHandler != null) { + activityHandler.resetSessionPartnerParameters(); + return; + } + + if (sessionParametersActionsArray == null) { + sessionParametersActionsArray = new ArrayList(); + } + + sessionParametersActionsArray.add(new IRunActivityHandler() { + @Override + public void run(ActivityHandler activityHandler) { + activityHandler.resetSessionPartnerParametersI(); + } + }); + } + + public void teardown(boolean deleteState) { + if (!checkActivityHandler()) return; + activityHandler.teardown(deleteState); + activityHandler = null; + } + + public void setPushToken(String token) { + if (!checkActivityHandler()) return; + activityHandler.setPushToken(token); + } + private boolean checkActivityHandler() { if (activityHandler == null) { getLogger().error("Adjust not initialized correctly"); diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/AttributionHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/AttributionHandler.java index fde9bc26d..a0a8f7e6a 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/AttributionHandler.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/AttributionHandler.java @@ -4,19 +4,16 @@ import org.json.JSONObject; +import java.lang.ref.WeakReference; import java.net.URL; import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; - -import javax.net.ssl.HttpsURLConnection; /** * Created by pfms on 07/11/14. */ public class AttributionHandler implements IAttributionHandler { - private ScheduledExecutorService scheduler; - private IActivityHandler activityHandler; + private CustomScheduledExecutor scheduledExecutor; + private WeakReference activityHandlerWeakRef; private ILogger logger; private ActivityPackage attributionPackage; private TimerOnce timer; @@ -27,18 +24,39 @@ public class AttributionHandler implements IAttributionHandler { public URL lastUrlUsed; + @Override + public void teardown() { + logger.verbose("AttributionHandler teardown"); + if (timer != null) { + timer.teardown(); + } + if (scheduledExecutor != null) { + try { + scheduledExecutor.shutdownNow(); + } catch(SecurityException se) {} + } + if (activityHandlerWeakRef != null) { + activityHandlerWeakRef.clear(); + } + scheduledExecutor = null; + activityHandlerWeakRef = null; + logger = null; + attributionPackage = null; + timer = null; + } + public AttributionHandler(IActivityHandler activityHandler, ActivityPackage attributionPackage, boolean startsSending, boolean hasListener) { - scheduler = Executors.newSingleThreadScheduledExecutor(); + scheduledExecutor = new CustomScheduledExecutor("AttributionHandler"); logger = AdjustFactory.getLogger(); - if (this.scheduler != null) { - timer = new TimerOnce(scheduler, new Runnable() { + if (this.scheduledExecutor != null) { + timer = new TimerOnce(scheduledExecutor, new Runnable() { @Override public void run() { - getAttributionInternal(); + getAttributionI(); } }, ATTRIBUTION_TIMER_NAME); } else { @@ -53,7 +71,7 @@ public void init(IActivityHandler activityHandler, ActivityPackage attributionPackage, boolean startsSending, boolean hasListener) { - this.activityHandler = activityHandler; + this.activityHandlerWeakRef = new WeakReference(activityHandler); this.attributionPackage = attributionPackage; this.paused = !startsSending; this.hasListener = hasListener; @@ -66,24 +84,32 @@ public void getAttribution() { @Override public void checkSessionResponse(final SessionResponseData sessionResponseData) { - scheduler.submit(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - checkSessionResponseInternal(sessionResponseData); + IActivityHandler activityHandler = activityHandlerWeakRef.get(); + if (activityHandler == null) { + return; + } + checkSessionResponseI(activityHandler, sessionResponseData); } }); } - private void checkAttributionResponse(final AttributionResponseData attributionResponseData) { - scheduler.submit(new Runnable() { + public void checkAttributionResponse(final AttributionResponseData attributionResponseData) { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - checkAttributionResponseInternal(attributionResponseData); + IActivityHandler activityHandler = activityHandlerWeakRef.get(); + if (activityHandler == null) { + return; + } + + checkAttributionResponseI(activityHandler, attributionResponseData); } }); } - @Override public void pauseSending() { paused = true; @@ -111,7 +137,7 @@ private void getAttribution(long delayInMilliseconds) { timer.startIn(delayInMilliseconds); } - private void checkAttributionInternal(ResponseData responseData) { + private void checkAttributionI(IActivityHandler activityHandler, ResponseData responseData) { if (responseData.jsonResponse == null) { return; } @@ -131,19 +157,39 @@ private void checkAttributionInternal(ResponseData responseData) { responseData.attribution = AdjustAttribution.fromJson(attributionJson); } - private void checkSessionResponseInternal(SessionResponseData sessionResponseData) { - checkAttributionInternal(sessionResponseData); + private void checkSessionResponseI(IActivityHandler activityHandler, SessionResponseData sessionResponseData) { + checkAttributionI(activityHandler, sessionResponseData); activityHandler.launchSessionResponseTasks(sessionResponseData); } - private void checkAttributionResponseInternal(AttributionResponseData attributionResponseData) { - checkAttributionInternal(attributionResponseData); + private void checkAttributionResponseI(IActivityHandler activityHandler, AttributionResponseData attributionResponseData) { + checkAttributionI(activityHandler, attributionResponseData); + + checkDeeplinkI(attributionResponseData); activityHandler.launchAttributionResponseTasks(attributionResponseData); } - private void getAttributionInternal() { + private void checkDeeplinkI(AttributionResponseData attributionResponseData) { + if (attributionResponseData.jsonResponse == null) { + return; + } + + JSONObject attributionJson = attributionResponseData.jsonResponse.optJSONObject("attribution"); + if (attributionJson == null) { + return; + } + + String deeplinkString = attributionJson.optString("deeplink", null); + if (deeplinkString == null) { + return; + } + + attributionResponseData.deeplink = Uri.parse(deeplinkString); + } + + private void getAttributionI() { if (!hasListener) { return; } @@ -157,7 +203,7 @@ private void getAttributionInternal() { try { AdjustFactory.URLGetConnection urlGetConnection = Util.createGETHttpsURLConnection( - buildUri(attributionPackage.getPath(), attributionPackage.getParameters()).toString(), + buildUriI(attributionPackage.getPath(), attributionPackage.getParameters()).toString(), attributionPackage.getClientSdk()); ResponseData responseData = Util.readHttpResponse(urlGetConnection.httpsURLConnection, attributionPackage); @@ -174,7 +220,7 @@ private void getAttributionInternal() { } } - private Uri buildUri(String path, Map parameters) { + private Uri buildUriI(String path, Map parameters) { Uri.Builder uriBuilder = new Uri.Builder(); uriBuilder.scheme(Constants.SCHEME); @@ -186,7 +232,7 @@ private Uri buildUri(String path, Map parameters) { } long now = System.currentTimeMillis(); - String dateString = Util.dateFormat(now); + String dateString = Util.dateFormatter.format(now); uriBuilder.appendQueryParameter("sent_at", dateString); diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/AttributionResponseData.java b/Adjust/adjust/src/main/java/com/adjust/sdk/AttributionResponseData.java index 77f1b552f..6e76e5072 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/AttributionResponseData.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/AttributionResponseData.java @@ -1,7 +1,10 @@ package com.adjust.sdk; +import android.net.Uri; + /** * Created by pfms on 09/02/16. */ public class AttributionResponseData extends ResponseData { + public Uri deeplink; } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/Constants.java b/Adjust/adjust/src/main/java/com/adjust/sdk/Constants.java index 70ee70a93..7d3f0af88 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/Constants.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/Constants.java @@ -29,13 +29,17 @@ public interface Constants { String BASE_URL = "https://app.adjust.com"; String SCHEME = "https"; String AUTHORITY = "app.adjust.com"; - String CLIENT_SDK = "android4.7.0"; + String CLIENT_SDK = "android4.10.0"; String LOGTAG = "Adjust"; String REFTAG = "reftag"; String DEEPLINK = "deeplink"; + String PUSH = "push"; + String THREAD_PREFIX = "Adjust-"; String ACTIVITY_STATE_FILENAME = "AdjustIoActivityState"; String ATTRIBUTION_FILENAME = "AdjustAttribution"; + String SESSION_CALLBACK_PARAMETERS_FILENAME = "AdjustSessionCallbackParameters"; + String SESSION_PARTNER_PARAMETERS_FILENAME = "AdjustSessionPartnerParameters"; String MALFORMED = "malformed"; String SMALL = "small"; @@ -52,6 +56,9 @@ public interface Constants { String MD5 = "MD5"; String SHA1 = "SHA-1"; + String CALLBACK_PARAMETERS = "callback_params"; + String PARTNER_PARAMETERS = "partner_params"; + // List of known plugins, possibly not active List PLUGINS = Arrays.asList(); } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/CustomScheduledExecutor.java b/Adjust/adjust/src/main/java/com/adjust/sdk/CustomScheduledExecutor.java new file mode 100644 index 000000000..09ed02fd7 --- /dev/null +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/CustomScheduledExecutor.java @@ -0,0 +1,94 @@ +package com.adjust.sdk; + +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +//import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by pfms on 05/08/2016. + */ +public class CustomScheduledExecutor { + private ScheduledThreadPoolExecutor executor; + private String source; +// private AtomicInteger threadCounter = new AtomicInteger(1); + + public CustomScheduledExecutor(final String source) { + executor = new ScheduledThreadPoolExecutor(1, // Single thread + new ThreadFactory() { // Creator of daemon threads + @Override + public Thread newThread(Runnable runnable) { + Thread thread = Executors.defaultThreadFactory().newThread(runnable); + thread.setPriority(Thread.MIN_PRIORITY); + thread.setName(Constants.THREAD_PREFIX + thread.getName() + source); + thread.setDaemon(true); + thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread th, Throwable tr) { + AdjustFactory.getLogger().error("Thread %s with error %s", th.getName(), tr.getMessage()); + } + }); +// AdjustFactory.getLogger().verbose("Thread %s created", thread.getName()); + return thread; + } + }, new RejectedExecutionHandler() { // Logs rejected runnables rejected from the entering the pool + @Override + public void rejectedExecution(Runnable runnable, ThreadPoolExecutor executor) { + AdjustFactory.getLogger().warn("Runnable %s rejected from %s ", runnable.toString(), source); + } + } + ); + this.source = source; + executor.allowCoreThreadTimeOut(true); + } + + public Future submit(Runnable task) { + return executor.submit(task); + } + + public void shutdownNow() { + executor.shutdownNow(); + } + + public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { +// AdjustFactory.getLogger().verbose("CustomScheduledExecutor scheduleWithFixedDelay from %s source, with %d delay and %d initial delay", +// source, delay, initialDelay); + return executor.scheduleWithFixedDelay(new RunnableWrapper(command), initialDelay, delay, unit); + } + + public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { +// AdjustFactory.getLogger().verbose("CustomScheduledExecutor schedule from %s source, with %d delay", source, delay); + return executor.schedule(new RunnableWrapper(command), delay, unit); + } + + private class RunnableWrapper implements Runnable { + private Runnable runnable; +// private long created; +// private int threadNumber; + + public RunnableWrapper(Runnable runnable) { + this.runnable = runnable; +// created = System.currentTimeMillis(); +// threadNumber = threadCounter.getAndIncrement(); +// AdjustFactory.getLogger().verbose("RunnableWrapper %d from %s created at %d", threadNumber, source, created); + } + + @Override + public void run() { + try { +// long before = System.currentTimeMillis(); +// AdjustFactory.getLogger().verbose("RunnableWrapper %d from %s source, before running at %d", threadNumber, source, before); + runnable.run(); +// long after = System.currentTimeMillis(); +// AdjustFactory.getLogger().verbose("RunnableWrapper %d from %s source, after running at %d", threadNumber, source, after); + } catch (Throwable t) { + AdjustFactory.getLogger().error("Runnable error %s", t.getMessage()); + } + } + } +} diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/IActivityHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/IActivityHandler.java index 8c7225dfa..a242a6942 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/IActivityHandler.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/IActivityHandler.java @@ -22,7 +22,7 @@ public interface IActivityHandler { void readOpenUrl(Uri url, long clickTime); - boolean updateAttribution(AdjustAttribution attribution); + boolean updateAttributionI(AdjustAttribution attribution); void launchEventResponseTasks(EventResponseData eventResponseData); @@ -35,4 +35,22 @@ public interface IActivityHandler { void setOfflineMode(boolean enabled); void setAskingAttribution(boolean askingAttribution); + + void sendFirstPackages(); + + void addSessionCallbackParameter(String key, String value); + + void addSessionPartnerParameter(String key, String value); + + void removeSessionCallbackParameter(String key); + + void removeSessionPartnerParameter(String key); + + void resetSessionCallbackParameters(); + + void resetSessionPartnerParameters(); + + void teardown(boolean deleteState); + + void setPushToken(String token); } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/IAttributionHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/IAttributionHandler.java index 91ddee11f..786ef8065 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/IAttributionHandler.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/IAttributionHandler.java @@ -4,16 +4,18 @@ * Created by pfms on 15/12/14. */ public interface IAttributionHandler { - public void init(IActivityHandler activityHandler, + void init(IActivityHandler activityHandler, ActivityPackage attributionPackage, boolean startsSending, boolean hasListener); - public void getAttribution(); + void getAttribution(); - public void checkSessionResponse(SessionResponseData responseData); + void checkSessionResponse(SessionResponseData responseData); - public void pauseSending(); + void pauseSending(); - public void resumeSending(); + void resumeSending(); + + void teardown(); } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/ILogger.java b/Adjust/adjust/src/main/java/com/adjust/sdk/ILogger.java index 28f92af4b..035a73942 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/ILogger.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/ILogger.java @@ -1,20 +1,21 @@ package com.adjust.sdk; public interface ILogger { - public void setLogLevel(LogLevel logLevel); + void setLogLevel(LogLevel logLevel); - public void setLogLevelString(String logLevelString); + void setLogLevelString(String logLevelString); - public void verbose(String message, Object... parameters); + void verbose(String message, Object... parameters); - public void debug(String message, Object... parameters); + void debug(String message, Object... parameters); - public void info(String message, Object... parameters); + void info(String message, Object... parameters); - public void warn(String message, Object... parameters); + void warn(String message, Object... parameters); - public void error(String message, Object... parameters); + void error(String message, Object... parameters); - public void Assert(String message, Object... parameters); + void Assert(String message, Object... parameters); + void lockLogLevel(); } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/IPackageHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/IPackageHandler.java index 149e07240..f1304ed66 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/IPackageHandler.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/IPackageHandler.java @@ -3,17 +3,21 @@ import android.content.Context; public interface IPackageHandler { - public void init(IActivityHandler activityHandler, Context context, boolean startsSending); + void init(IActivityHandler activityHandler, Context context, boolean startsSending); - public void addPackage(ActivityPackage activityPackage); + void addPackage(ActivityPackage activityPackage); - public void sendFirstPackage(); + void sendFirstPackage(); - public void sendNextPackage(ResponseData responseData); + void sendNextPackage(ResponseData responseData); - public void closeFirstPackage(ResponseData responseData, ActivityPackage activityPackage); + void closeFirstPackage(ResponseData responseData, ActivityPackage activityPackage); - public void pauseSending(); + void pauseSending(); - public void resumeSending(); + void resumeSending(); + + void updatePackages(SessionParameters sessionParameters); + + void teardown(boolean deleteState); } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/IRequestHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/IRequestHandler.java index 4a5611a43..771cf36a6 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/IRequestHandler.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/IRequestHandler.java @@ -1,7 +1,9 @@ package com.adjust.sdk; public interface IRequestHandler { - public void init(IPackageHandler packageHandler); + void init(IPackageHandler packageHandler); - public void sendPackage(ActivityPackage activityPackage, int queueSize); + void sendPackage(ActivityPackage activityPackage, int queueSize); + + void teardown(); } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/IRunActivityHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/IRunActivityHandler.java new file mode 100644 index 000000000..99526f320 --- /dev/null +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/IRunActivityHandler.java @@ -0,0 +1,8 @@ +package com.adjust.sdk; + +/** + * Created by pfms on 29/07/2016. + */ +public interface IRunActivityHandler { + void run(ActivityHandler activityHandler); +} diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/ISdkClickHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/ISdkClickHandler.java index d3a05145d..61c48a677 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/ISdkClickHandler.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/ISdkClickHandler.java @@ -8,4 +8,5 @@ public interface ISdkClickHandler { void pauseSending(); void resumeSending(); void sendSdkClick(ActivityPackage sdkClick); + void teardown(); } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/LogLevel.java b/Adjust/adjust/src/main/java/com/adjust/sdk/LogLevel.java index 5c0b410c2..ea9a1eb23 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/LogLevel.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/LogLevel.java @@ -6,7 +6,7 @@ * Created by pfms on 11/03/15. */ public enum LogLevel { - VERBOSE(Log.VERBOSE), DEBUG(Log.DEBUG), INFO(Log.INFO), WARN(Log.WARN), ERROR(Log.ERROR), ASSERT(Log.ASSERT); + VERBOSE(Log.VERBOSE), DEBUG(Log.DEBUG), INFO(Log.INFO), WARN(Log.WARN), ERROR(Log.ERROR), ASSERT(Log.ASSERT), SUPRESS(8); final int androidLogLevel; LogLevel(final int androidLogLevel) { diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/Logger.java b/Adjust/adjust/src/main/java/com/adjust/sdk/Logger.java index 6354efbd4..6c232437f 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/Logger.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/Logger.java @@ -19,14 +19,19 @@ public class Logger implements ILogger { private LogLevel logLevel; + private boolean logLevelLocked; private static String formatErrorMessage = "Error formating log message: %s, with params: %s"; public Logger() { setLogLevel(LogLevel.INFO); + logLevelLocked = false; } @Override public void setLogLevel(LogLevel logLevel) { + if (logLevelLocked) { + return; + } this.logLevel = logLevel; } @@ -98,10 +103,17 @@ public void error(String message, Object... parameters) { @Override public void Assert(String message, Object... parameters) { - try { - Log.println(Log.ASSERT, LOGTAG, String.format(Locale.US, message, parameters)); - } catch (Exception e) { - Log.e(LOGTAG, String.format(Locale.US, formatErrorMessage, message, Arrays.toString(parameters))); + if(logLevel.androidLogLevel <= Log.ASSERT) { + try { + Log.println(Log.ASSERT, LOGTAG, String.format(Locale.US, message, parameters)); + } catch (Exception e) { + Log.e(LOGTAG, String.format(Locale.US, formatErrorMessage, message, Arrays.toString(parameters))); + } } } + + @Override + public void lockLogLevel() { + logLevelLocked = true; + } } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/PackageBuilder.java b/Adjust/adjust/src/main/java/com/adjust/sdk/PackageBuilder.java index 538405209..c98987b26 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/PackageBuilder.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/PackageBuilder.java @@ -16,11 +16,13 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; +import static com.adjust.sdk.Constants.CALLBACK_PARAMETERS; +import static com.adjust.sdk.Constants.PARTNER_PARAMETERS; class PackageBuilder { private AdjustConfig adjustConfig; private DeviceInfo deviceInfo; - private ActivityState activityState; + private ActivityStateCopy activityStateCopy; private long createdAt; // reattributions @@ -29,23 +31,53 @@ class PackageBuilder { String reftag; String referrer; String deeplink; + String pushToken; + long clickTime; private static ILogger logger = AdjustFactory.getLogger(); + private class ActivityStateCopy { + long lastInterval = -1; + int eventCount = -1; + String uuid = null; + int sessionCount = -1; + int subsessionCount = -1; + long sessionLength = -1; + long timeSpent = -1; + + ActivityStateCopy(ActivityState activityState) { + if (activityState == null) { + return; + } + this.lastInterval = activityState.lastInterval; + this.eventCount = activityState.eventCount; + this.uuid = activityState.uuid; + this.sessionCount = activityState.sessionCount; + this.subsessionCount = activityState.subsessionCount; + this.sessionLength = activityState.sessionLength; + this.timeSpent = activityState.timeSpent; + } + } + public PackageBuilder(AdjustConfig adjustConfig, DeviceInfo deviceInfo, ActivityState activityState, long createdAt) { this.adjustConfig = adjustConfig; this.deviceInfo = deviceInfo; - this.activityState = activityState == null ? null : activityState.shallowCopy(); + this.activityStateCopy = new ActivityStateCopy(activityState); this.createdAt = createdAt; } - public ActivityPackage buildSessionPackage() { + public ActivityPackage buildSessionPackage(SessionParameters sessionParameters, boolean isInDelay) { Map parameters = getDefaultParameters(); - addDuration(parameters, "last_interval", activityState.lastInterval); - addString(parameters, "default_tracker", adjustConfig.defaultTracker); + PackageBuilder.addDuration(parameters, "last_interval", activityStateCopy.lastInterval); + PackageBuilder.addString(parameters, "default_tracker", adjustConfig.defaultTracker); + + if (!isInDelay) { + PackageBuilder.addMapJson(parameters, CALLBACK_PARAMETERS, sessionParameters.callbackParameters); + PackageBuilder.addMapJson(parameters, PARTNER_PARAMETERS, sessionParameters.partnerParameters); + } ActivityPackage sessionPackage = getDefaultActivityPackage(ActivityKind.SESSION); sessionPackage.setPath("/session"); @@ -55,32 +87,45 @@ public ActivityPackage buildSessionPackage() { return sessionPackage; } - public ActivityPackage buildEventPackage(AdjustEvent event) { + public ActivityPackage buildEventPackage(AdjustEvent event, + SessionParameters sessionParameters, + boolean isInDelay) + { Map parameters = getDefaultParameters(); - addInt(parameters, "event_count", activityState.eventCount); - addString(parameters, "event_token", event.eventToken); - addDouble(parameters, "revenue", event.revenue); - addString(parameters, "currency", event.currency); - addMapJson(parameters, "callback_params", event.callbackParameters); - addMapJson(parameters, "partner_params", event.partnerParameters); - + PackageBuilder.addInt(parameters, "event_count", activityStateCopy.eventCount); + PackageBuilder.addString(parameters, "event_token", event.eventToken); + PackageBuilder.addDouble(parameters, "revenue", event.revenue); + PackageBuilder.addString(parameters, "currency", event.currency); + + if (!isInDelay) { + PackageBuilder.addMapJson(parameters, CALLBACK_PARAMETERS, + Util.mergeParameters(sessionParameters.callbackParameters, event.callbackParameters, "Callback")); + PackageBuilder.addMapJson(parameters, PARTNER_PARAMETERS, + Util.mergeParameters(sessionParameters.partnerParameters, event.partnerParameters, "Partner")); + } ActivityPackage eventPackage = getDefaultActivityPackage(ActivityKind.EVENT); eventPackage.setPath("/event"); eventPackage.setSuffix(getEventSuffix(event)); eventPackage.setParameters(parameters); + if (isInDelay) { + eventPackage.setCallbackParameters(event.callbackParameters); + eventPackage.setPartnerParameters(event.partnerParameters); + } + return eventPackage; } - public ActivityPackage buildClickPackage(String source, long clickTime) { + public ActivityPackage buildClickPackage(String source) { Map parameters = getIdsParameters(); - addString(parameters, "source", source); - addDate(parameters, "click_time", clickTime); - addString(parameters, "reftag", reftag); - addMapJson(parameters, "params", extraParameters); - addString(parameters, "referrer", referrer); - addString(parameters, "deeplink", deeplink); + PackageBuilder.addString(parameters, "source", source); + PackageBuilder.addDate(parameters, "click_time", clickTime); + PackageBuilder.addString(parameters, "reftag", reftag); + PackageBuilder.addMapJson(parameters, "params", extraParameters); + PackageBuilder.addString(parameters, "referrer", referrer); + PackageBuilder.addString(parameters, "deeplink", deeplink); + PackageBuilder.addString(parameters, "push_token", pushToken); injectAttribution(parameters); ActivityPackage clickPackage = getDefaultActivityPackage(ActivityKind.CLICK); @@ -114,7 +159,7 @@ private Map getDefaultParameters() { injectDeviceInfo(parameters); injectConfig(parameters); injectActivityState(parameters); - injectCreatedAt(parameters); + injectCommonParameters(parameters); // general checkDeviceIds(parameters); @@ -127,7 +172,7 @@ private Map getIdsParameters() { injectDeviceInfoIds(parameters); injectConfig(parameters); - injectCreatedAt(parameters); + injectCommonParameters(parameters); checkDeviceIds(parameters); @@ -136,66 +181,67 @@ private Map getIdsParameters() { private void injectDeviceInfo(Map parameters) { injectDeviceInfoIds(parameters); - addString(parameters, "fb_id", deviceInfo.fbAttributionId); - addString(parameters, "package_name", deviceInfo.packageName); - addString(parameters, "app_version", deviceInfo.appVersion); - addString(parameters, "device_type", deviceInfo.deviceType); - addString(parameters, "device_name", deviceInfo.deviceName); - addString(parameters, "device_manufacturer", deviceInfo.deviceManufacturer); - addString(parameters, "os_name", deviceInfo.osName); - addString(parameters, "os_version", deviceInfo.osVersion); - addString(parameters, "api_level", deviceInfo.apiLevel); - addString(parameters, "language", deviceInfo.language); - addString(parameters, "country", deviceInfo.country); - addString(parameters, "screen_size", deviceInfo.screenSize); - addString(parameters, "screen_format", deviceInfo.screenFormat); - addString(parameters, "screen_density", deviceInfo.screenDensity); - addString(parameters, "display_width", deviceInfo.displayWidth); - addString(parameters, "display_height", deviceInfo.displayHeight); - addString(parameters, "hardware_name", deviceInfo.hardwareName); - addString(parameters, "cpu_type", deviceInfo.abi); + PackageBuilder.addString(parameters, "fb_id", deviceInfo.fbAttributionId); + PackageBuilder.addString(parameters, "package_name", deviceInfo.packageName); + PackageBuilder.addString(parameters, "app_version", deviceInfo.appVersion); + PackageBuilder.addString(parameters, "device_type", deviceInfo.deviceType); + PackageBuilder.addString(parameters, "device_name", deviceInfo.deviceName); + PackageBuilder.addString(parameters, "device_manufacturer", deviceInfo.deviceManufacturer); + PackageBuilder.addString(parameters, "os_name", deviceInfo.osName); + PackageBuilder.addString(parameters, "os_version", deviceInfo.osVersion); + PackageBuilder.addString(parameters, "api_level", deviceInfo.apiLevel); + PackageBuilder.addString(parameters, "language", deviceInfo.language); + PackageBuilder.addString(parameters, "country", deviceInfo.country); + PackageBuilder.addString(parameters, "screen_size", deviceInfo.screenSize); + PackageBuilder.addString(parameters, "screen_format", deviceInfo.screenFormat); + PackageBuilder.addString(parameters, "screen_density", deviceInfo.screenDensity); + PackageBuilder.addString(parameters, "display_width", deviceInfo.displayWidth); + PackageBuilder.addString(parameters, "display_height", deviceInfo.displayHeight); + PackageBuilder.addString(parameters, "hardware_name", deviceInfo.hardwareName); + PackageBuilder.addString(parameters, "cpu_type", deviceInfo.abi); fillPluginKeys(parameters); } private void injectDeviceInfoIds(Map parameters) { - addString(parameters, "mac_sha1", deviceInfo.macSha1); - addString(parameters, "mac_md5", deviceInfo.macShortMd5); - addString(parameters, "android_id", deviceInfo.androidId); + PackageBuilder.addString(parameters, "mac_sha1", deviceInfo.macSha1); + PackageBuilder.addString(parameters, "mac_md5", deviceInfo.macShortMd5); + PackageBuilder.addString(parameters, "android_id", deviceInfo.androidId); } private void injectConfig(Map parameters) { - addString(parameters, "app_token", adjustConfig.appToken); - addString(parameters, "environment", adjustConfig.environment); - addBoolean(parameters, "device_known", adjustConfig.deviceKnown); - addBoolean(parameters, "needs_response_details", adjustConfig.hasListener()); + PackageBuilder.addString(parameters, "app_token", adjustConfig.appToken); + PackageBuilder.addString(parameters, "environment", adjustConfig.environment); + PackageBuilder.addBoolean(parameters, "device_known", adjustConfig.deviceKnown); + PackageBuilder.addBoolean(parameters, "needs_response_details", adjustConfig.hasListener()); String playAdId = Util.getPlayAdId(adjustConfig.context); - addString(parameters, "gps_adid", playAdId); + PackageBuilder.addString(parameters, "gps_adid", playAdId); Boolean isTrackingEnabled = Util.isPlayTrackingEnabled(adjustConfig.context); - addBoolean(parameters, "tracking_enabled", isTrackingEnabled); - addBoolean(parameters, "event_buffering_enabled", adjustConfig.eventBufferingEnabled); + PackageBuilder.addBoolean(parameters, "tracking_enabled", isTrackingEnabled); + PackageBuilder.addBoolean(parameters, "event_buffering_enabled", adjustConfig.eventBufferingEnabled); } private void injectActivityState(Map parameters) { - addString(parameters, "android_uuid", activityState.uuid); - addInt(parameters, "session_count", activityState.sessionCount); - addInt(parameters, "subsession_count", activityState.subsessionCount); - addDuration(parameters, "session_length", activityState.sessionLength); - addDuration(parameters, "time_spent", activityState.timeSpent); + PackageBuilder.addString(parameters, "android_uuid", activityStateCopy.uuid); + PackageBuilder.addInt(parameters, "session_count", activityStateCopy.sessionCount); + PackageBuilder.addInt(parameters, "subsession_count", activityStateCopy.subsessionCount); + PackageBuilder.addDuration(parameters, "session_length", activityStateCopy.sessionLength); + PackageBuilder.addDuration(parameters, "time_spent", activityStateCopy.timeSpent); } - private void injectCreatedAt(Map parameters) { - addDate(parameters, "created_at", createdAt); + private void injectCommonParameters(Map parameters) { + PackageBuilder.addDate(parameters, "created_at", createdAt); + PackageBuilder.addBoolean(parameters, "attribution_deeplink", true); } private void injectAttribution(Map parameters) { if (attribution == null) { return; } - addString(parameters, "tracker", attribution.trackerName); - addString(parameters, "campaign", attribution.campaign); - addString(parameters, "adgroup", attribution.adgroup); - addString(parameters, "creative", attribution.creative); + PackageBuilder.addString(parameters, "tracker", attribution.trackerName); + PackageBuilder.addString(parameters, "campaign", attribution.campaign); + PackageBuilder.addString(parameters, "adgroup", attribution.adgroup); + PackageBuilder.addString(parameters, "creative", attribution.creative); } private void checkDeviceIds(Map parameters) { @@ -213,7 +259,7 @@ private void fillPluginKeys(Map parameters) { } for (Map.Entry entry : deviceInfo.pluginKeys.entrySet()) { - addString(parameters, entry.getKey(), entry.getValue()); + PackageBuilder.addString(parameters, entry.getKey(), entry.getValue()); } } @@ -225,7 +271,7 @@ private String getEventSuffix(AdjustEvent event) { } } - private void addString(Map parameters, String key, String value) { + public static void addString(Map parameters, String key, String value) { if (TextUtils.isEmpty(value)) { return; } @@ -233,34 +279,34 @@ private void addString(Map parameters, String key, String value) parameters.put(key, value); } - private void addInt(Map parameters, String key, long value) { + public static void addInt(Map parameters, String key, long value) { if (value < 0) { return; } String valueString = Long.toString(value); - addString(parameters, key, valueString); + PackageBuilder.addString(parameters, key, valueString); } - private void addDate(Map parameters, String key, long value) { + public static void addDate(Map parameters, String key, long value) { if (value < 0) { return; } - String dateString = Util.dateFormat(value); - addString(parameters, key, dateString); + String dateString = Util.dateFormatter.format(value); + PackageBuilder.addString(parameters, key, dateString); } - private void addDuration(Map parameters, String key, long durationInMilliSeconds) { + public static void addDuration(Map parameters, String key, long durationInMilliSeconds) { if (durationInMilliSeconds < 0) { return; } long durationInSeconds = (durationInMilliSeconds + 500) / 1000; - addInt(parameters, key, durationInSeconds); + PackageBuilder.addInt(parameters, key, durationInSeconds); } - private void addMapJson(Map parameters, String key, Map map) { + public static void addMapJson(Map parameters, String key, Map map) { if (map == null) { return; } @@ -272,24 +318,24 @@ private void addMapJson(Map parameters, String key, Map parameters, String key, Boolean value) { + public static void addBoolean(Map parameters, String key, Boolean value) { if (value == null) { return; } int intValue = value ? 1 : 0; - addInt(parameters, key, intValue); + PackageBuilder.addInt(parameters, key, intValue); } - private void addDouble(Map parameters, String key, Double value) { + public static void addDouble(Map parameters, String key, Double value) { if (value == null) return; String doubleString = String.format(Locale.US, "%.5f", value); - addString(parameters, key, doubleString); + PackageBuilder.addString(parameters, key, doubleString); } } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/PackageHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/PackageHandler.java index a7e776dae..0f652a78d 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/PackageHandler.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/PackageHandler.java @@ -10,28 +10,25 @@ package com.adjust.sdk; import android.content.Context; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.os.SystemClock; import java.lang.ref.WeakReference; -import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; -import java.util.Random; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import static com.adjust.sdk.Constants.CALLBACK_PARAMETERS; +import static com.adjust.sdk.Constants.PARTNER_PARAMETERS; + // persistent -public class PackageHandler extends HandlerThread implements IPackageHandler { +public class PackageHandler implements IPackageHandler { private static final String PACKAGE_QUEUE_FILENAME = "AdjustIoPackageQueue"; private static final String PACKAGE_QUEUE_NAME = "Package queue"; - private Handler internalHandler; + private CustomScheduledExecutor scheduledExecutor; private IRequestHandler requestHandler; - private IActivityHandler activityHandler; + private WeakReference activityHandlerWeakRef; private List packageQueue; private AtomicBoolean isSending; private boolean paused; @@ -39,29 +36,56 @@ public class PackageHandler extends HandlerThread implements IPackageHandler { private ILogger logger; private BackoffStrategy backoffStrategy; + @Override + public void teardown(boolean deleteState) { + logger.verbose("PackageHandler teardown"); + if (scheduledExecutor != null) { + try { + scheduledExecutor.shutdownNow(); + } catch(SecurityException se) {} + } + if (activityHandlerWeakRef != null) { + activityHandlerWeakRef.clear(); + } + if (requestHandler != null) { + requestHandler.teardown(); + } + if (packageQueue != null) { + packageQueue.clear(); + } + if (deleteState && context != null) { + deletePackageQueue(context); + } + scheduledExecutor = null; + requestHandler = null; + activityHandlerWeakRef = null; + packageQueue = null; + isSending = null; + context = null; + logger = null; + backoffStrategy = null; + } + public PackageHandler(IActivityHandler activityHandler, Context context, boolean startsSending) { - super(Constants.LOGTAG, MIN_PRIORITY); - setDaemon(true); - start(); - this.internalHandler = new Handler(getLooper()); + this.scheduledExecutor = new CustomScheduledExecutor("PackageHandler"); this.logger = AdjustFactory.getLogger(); this.backoffStrategy = AdjustFactory.getPackageHandlerBackoffStrategy(); init(activityHandler, context, startsSending); - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - initInternal(); + initI(); } }); } @Override public void init(IActivityHandler activityHandler, Context context, boolean startsSending) { - this.activityHandler = activityHandler; + this.activityHandlerWeakRef = new WeakReference(activityHandler); this.context = context; this.paused = !startsSending; } @@ -69,10 +93,10 @@ public void init(IActivityHandler activityHandler, Context context, boolean star // add a package to the queue @Override public void addPackage(final ActivityPackage activityPackage) { - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - addInternal(activityPackage); + addI(activityPackage); } }); } @@ -80,10 +104,10 @@ public void run() { // try to send the oldest package @Override public void sendFirstPackage() { - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - sendFirstInternal(); + sendFirstI(); } }); } @@ -92,39 +116,54 @@ public void run() { // (after success or possibly permanent failure) @Override public void sendNextPackage(ResponseData responseData) { - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - sendNextInternal(); + sendNextI(); } }); - activityHandler.finishedTrackingActivity(responseData); + IActivityHandler activityHandler = activityHandlerWeakRef.get(); + if (activityHandler != null) { + activityHandler.finishedTrackingActivity(responseData); + } } // close the package to retry in the future (after temporary failure) @Override public void closeFirstPackage(ResponseData responseData, ActivityPackage activityPackage) { responseData.willRetry = true; - activityHandler.finishedTrackingActivity(responseData); - if (activityPackage != null) { - int retries = activityPackage.increaseRetries(); + IActivityHandler activityHandler = activityHandlerWeakRef.get(); + if (activityHandler != null) { + activityHandler.finishedTrackingActivity(responseData); + } - long waitTime = Util.getWaitingTime(retries, backoffStrategy); + Runnable runnable = new Runnable() { + @Override + public void run() { + logger.verbose("Package handler can send"); + isSending.set(false); - double waitTimeSeconds = waitTime / 1000.0; - String secondsString = Util.SecondsDisplayFormat.format(waitTimeSeconds); + // Try to send the same package after sleeping + sendFirstPackage(); + } + }; - logger.verbose("Sleeping for %s seconds before retrying the %d time", secondsString, retries); - SystemClock.sleep(waitTime); + if (activityPackage == null) { + runnable.run(); + return; } - logger.verbose("Package handler can send"); - isSending.set(false); + int retries = activityPackage.increaseRetries(); + + long waitTimeMilliSeconds = Util.getWaitingTime(retries, backoffStrategy); - // Try to send the same package after sleeping - sendFirstPackage(); + double waitTimeSeconds = waitTimeMilliSeconds / 1000.0; + String secondsString = Util.SecondsDisplayFormat.format(waitTimeSeconds); + + logger.verbose("Waiting for %s seconds before retrying the %d time", secondsString, retries); + scheduledExecutor.schedule(runnable, waitTimeMilliSeconds, TimeUnit.MILLISECONDS); } // interrupt the sending loop after the current request has finished @@ -139,25 +178,40 @@ public void resumeSending() { paused = false; } + @Override + public void updatePackages(SessionParameters sessionParameters) { + final SessionParameters sessionParametersCopy; + if (sessionParameters != null) { + sessionParametersCopy = sessionParameters.deepCopy(); + } else { + sessionParametersCopy = null; + } + scheduledExecutor.submit(new Runnable() { + @Override + public void run() { + updatePackagesI(sessionParametersCopy); + } + }); + } // internal methods run in dedicated queue thread - private void initInternal() { + private void initI() { requestHandler = AdjustFactory.getRequestHandler(this); isSending = new AtomicBoolean(); - readPackageQueue(); + readPackageQueueI(); } - private void addInternal(ActivityPackage newPackage) { + private void addI(ActivityPackage newPackage) { packageQueue.add(newPackage); logger.debug("Added package %d (%s)", packageQueue.size(), newPackage); logger.verbose("%s", newPackage.getExtendedString()); - writePackageQueue(); + writePackageQueueI(); } - private void sendFirstInternal() { + private void sendFirstI() { if (packageQueue.isEmpty()) { return; } @@ -175,17 +229,48 @@ private void sendFirstInternal() { requestHandler.sendPackage(firstPackage, packageQueue.size() - 1); } - private void sendNextInternal() { + private void sendNextI() { packageQueue.remove(0); - writePackageQueue(); + writePackageQueueI(); isSending.set(false); logger.verbose("Package handler can send"); - sendFirstInternal(); + sendFirstI(); + } + + public void updatePackagesI(SessionParameters sessionParameters) { + if (sessionParameters == null) { + return; + } + + logger.debug("Updating package handler queue"); + logger.verbose("Session callback parameters: %s", sessionParameters.callbackParameters); + logger.verbose("Session partner parameters: %s", sessionParameters.partnerParameters); + + for (ActivityPackage activityPackage : packageQueue) { + Map parameters = activityPackage.getParameters(); + // callback parameters + Map mergedCallbackParameters = Util.mergeParameters(sessionParameters.callbackParameters, + activityPackage.getCallbackParameters(), + "Callback"); + + PackageBuilder.addMapJson(parameters, CALLBACK_PARAMETERS, mergedCallbackParameters); + // partner parameters + Map mergedPartnerParameters = Util.mergeParameters(sessionParameters.partnerParameters, + activityPackage.getPartnerParameters(), + "Partner"); + + PackageBuilder.addMapJson(parameters, PARTNER_PARAMETERS, mergedPartnerParameters); + } + + writePackageQueueI(); } - private void readPackageQueue() { + private void readPackageQueueI() { try { - packageQueue = Util.readObject(context, PACKAGE_QUEUE_FILENAME, PACKAGE_QUEUE_NAME, (Class>)((Class)List.class)); + packageQueue = Util.readObject(context, + PACKAGE_QUEUE_FILENAME, + PACKAGE_QUEUE_NAME, + (Class>)(Class)List.class); } catch (Exception e) { logger.error("Failed to read %s file (%s)", PACKAGE_QUEUE_NAME, e.getMessage()); packageQueue = null; @@ -198,7 +283,7 @@ private void readPackageQueue() { } } - private void writePackageQueue() { + private void writePackageQueueI() { Util.writeObject(packageQueue, context, PACKAGE_QUEUE_FILENAME, PACKAGE_QUEUE_NAME); logger.debug("Package handler wrote %d packages", packageQueue.size()); } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/RequestHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/RequestHandler.java index bf9eb3dfc..811c86e9c 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/RequestHandler.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/RequestHandler.java @@ -9,52 +9,56 @@ package com.adjust.sdk; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; - -import org.json.JSONException; - import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.ref.WeakReference; import java.net.SocketTimeoutException; -import java.util.Locale; import javax.net.ssl.HttpsURLConnection; -public class RequestHandler extends HandlerThread implements IRequestHandler { - private Handler internalHandler; - private IPackageHandler packageHandler; +public class RequestHandler implements IRequestHandler { + private CustomScheduledExecutor scheduledExecutor; + private WeakReference packageHandlerWeakRef; private ILogger logger; public RequestHandler(IPackageHandler packageHandler) { - super(Constants.LOGTAG, MIN_PRIORITY); - setDaemon(true); - start(); - this.logger = AdjustFactory.getLogger(); - this.internalHandler = new Handler(getLooper()); + this.scheduledExecutor = new CustomScheduledExecutor("RequestHandler"); init(packageHandler); } @Override public void init(IPackageHandler packageHandler) { - this.packageHandler = packageHandler; + this.packageHandlerWeakRef = new WeakReference(packageHandler); } @Override public void sendPackage(final ActivityPackage activityPackage, final int queueSize) { - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - sendInternal(activityPackage, queueSize); + sendI(activityPackage, queueSize); } }); } - private void sendInternal(ActivityPackage activityPackage, int queueSize) { + @Override + public void teardown() { + logger.verbose("RequestHandler teardown"); + if (scheduledExecutor != null) { + try { + scheduledExecutor.shutdownNow(); + } catch(SecurityException se) {} + } + if (packageHandlerWeakRef != null) { + packageHandlerWeakRef.clear(); + } + scheduledExecutor = null; + packageHandlerWeakRef = null; + logger = null; + } + + private void sendI(ActivityPackage activityPackage, int queueSize) { String targetURL = Constants.BASE_URL + activityPackage.getPath(); try { @@ -66,6 +70,11 @@ private void sendInternal(ActivityPackage activityPackage, int queueSize) { ResponseData responseData = Util.readHttpResponse(connection, activityPackage); + IPackageHandler packageHandler = packageHandlerWeakRef.get(); + if (packageHandler == null) { + return; + } + if (responseData.jsonResponse == null) { packageHandler.closeFirstPackage(responseData, activityPackage); return; @@ -74,18 +83,18 @@ private void sendInternal(ActivityPackage activityPackage, int queueSize) { packageHandler.sendNextPackage(responseData); } catch (UnsupportedEncodingException e) { - sendNextPackage(activityPackage, "Failed to encode parameters", e); + sendNextPackageI(activityPackage, "Failed to encode parameters", e); } catch (SocketTimeoutException e) { - closePackage(activityPackage, "Request timed out", e); + closePackageI(activityPackage, "Request timed out", e); } catch (IOException e) { - closePackage(activityPackage, "Request failed", e); + closePackageI(activityPackage, "Request failed", e); } catch (Throwable e) { - sendNextPackage(activityPackage, "Runtime exception", e); + sendNextPackageI(activityPackage, "Runtime exception", e); } } // close current package because it failed - private void closePackage(ActivityPackage activityPackage, String message, Throwable throwable) { + private void closePackageI(ActivityPackage activityPackage, String message, Throwable throwable) { final String packageMessage = activityPackage.getFailureMessage(); final String reasonString = Util.getReasonString(message, throwable); String finalMessage = String.format("%s. (%s) Will retry later", packageMessage, reasonString); @@ -94,11 +103,16 @@ private void closePackage(ActivityPackage activityPackage, String message, Throw ResponseData responseData = ResponseData.buildResponseData(activityPackage); responseData.message = finalMessage; + IPackageHandler packageHandler = packageHandlerWeakRef.get(); + if (packageHandler == null) { + return; + } + packageHandler.closeFirstPackage(responseData, activityPackage); } // send next package because the current package failed - private void sendNextPackage(ActivityPackage activityPackage, String message, Throwable throwable) { + private void sendNextPackageI(ActivityPackage activityPackage, String message, Throwable throwable) { final String failureMessage = activityPackage.getFailureMessage(); final String reasonString = Util.getReasonString(message, throwable); String finalMessage = String.format("%s. (%s)", failureMessage, reasonString); @@ -107,6 +121,11 @@ private void sendNextPackage(ActivityPackage activityPackage, String message, Th ResponseData responseData = ResponseData.buildResponseData(activityPackage); responseData.message = finalMessage; + IPackageHandler packageHandler = packageHandlerWeakRef.get(); + if (packageHandler == null) { + return; + } + packageHandler.sendNextPackage(responseData); } } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/SdkClickHandler.java b/Adjust/adjust/src/main/java/com/adjust/sdk/SdkClickHandler.java index 8cf53dd2d..225af4404 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/SdkClickHandler.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/SdkClickHandler.java @@ -1,10 +1,5 @@ package com.adjust.sdk; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.SystemClock; - import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.SocketTimeoutException; @@ -17,21 +12,35 @@ /** * Created by pfms on 31/03/16. */ -public class SdkClickHandler extends HandlerThread implements ISdkClickHandler { - private Handler internalHandler; +public class SdkClickHandler implements ISdkClickHandler { + private CustomScheduledExecutor scheduledExecutor; private ILogger logger; private boolean paused; private List packageQueue; private BackoffStrategy backoffStrategy; - public SdkClickHandler(boolean startsSending) { - super(Constants.LOGTAG, MIN_PRIORITY); - setDaemon(true); - start(); + @Override + public void teardown() { + logger.verbose("SdkClickHandler teardown"); + if (scheduledExecutor != null) { + try { + scheduledExecutor.shutdownNow(); + } catch(SecurityException se) {} + } + if (packageQueue != null) { + packageQueue.clear(); + } + scheduledExecutor = null; + logger = null; + packageQueue = null; + backoffStrategy = null; + } + + public SdkClickHandler(boolean startsSending) { init(startsSending); this.logger = AdjustFactory.getLogger(); - this.internalHandler = new Handler(getLooper()); + this.scheduledExecutor = new CustomScheduledExecutor("SdkClickHandler"); this.backoffStrategy = AdjustFactory.getSdkClickBackoffStrategy(); } @@ -55,7 +64,7 @@ public void resumeSending() { @Override public void sendSdkClick(final ActivityPackage sdkClick) { - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { packageQueue.add(sdkClick); @@ -67,40 +76,49 @@ public void run() { } private void sendNextSdkClick() { - internalHandler.post(new Runnable() { + scheduledExecutor.submit(new Runnable() { @Override public void run() { - if (paused) { - return; - } + sendNextSdkClickI(); + } + }); + } - if (packageQueue.isEmpty()) { - return; - } + private void sendNextSdkClickI() { + if (paused) { + return; + } - ActivityPackage sdkClickPackage = packageQueue.get(0); + if (packageQueue.isEmpty()) { + return; + } - int retries = sdkClickPackage.getRetries(); + final ActivityPackage sdkClickPackage = packageQueue.remove(0); + int retries = sdkClickPackage.getRetries(); - if (retries > 0) { - long waitTimeMilliSeconds = Util.getWaitingTime(retries, backoffStrategy); + Runnable runnable = new Runnable() { + @Override + public void run() { + sendSdkClickI(sdkClickPackage); + sendNextSdkClick(); + } + }; - double waitTimeSeconds = waitTimeMilliSeconds / 1000.0; - String secondsString = Util.SecondsDisplayFormat.format(waitTimeSeconds); + if (retries <= 0) { + runnable.run(); + return; + } - logger.verbose("Sleeping for %s seconds before retrying sdk_click for the %d time", secondsString, retries); - SystemClock.sleep(waitTimeMilliSeconds); - } + long waitTimeMilliSeconds = Util.getWaitingTime(retries, backoffStrategy); - sendSdkClickInternal(sdkClickPackage); + double waitTimeSeconds = waitTimeMilliSeconds / 1000.0; + String secondsString = Util.SecondsDisplayFormat.format(waitTimeSeconds); - packageQueue.remove(0); - sendNextSdkClick(); - } - }); + logger.verbose("Waiting for %s seconds before retrying sdk_click for the %d time", secondsString, retries); + scheduledExecutor.schedule(runnable, waitTimeMilliSeconds, TimeUnit.MILLISECONDS); } - private void sendSdkClickInternal(ActivityPackage sdkClickPackage) { + private void sendSdkClickI(ActivityPackage sdkClickPackage) { String targetURL = Constants.BASE_URL + sdkClickPackage.getPath(); try { @@ -113,29 +131,29 @@ private void sendSdkClickInternal(ActivityPackage sdkClickPackage) { ResponseData responseData = Util.readHttpResponse(connection, sdkClickPackage); if (responseData.jsonResponse == null) { - retrySending(sdkClickPackage); + retrySendingI(sdkClickPackage); } } catch (UnsupportedEncodingException e) { - logErrorMessage(sdkClickPackage, "Sdk_click failed to encode parameters", e); + logErrorMessageI(sdkClickPackage, "Sdk_click failed to encode parameters", e); } catch (SocketTimeoutException e) { - logErrorMessage(sdkClickPackage, "Sdk_click request timed out. Will retry later", e); - retrySending(sdkClickPackage); + logErrorMessageI(sdkClickPackage, "Sdk_click request timed out. Will retry later", e); + retrySendingI(sdkClickPackage); } catch (IOException e) { - logErrorMessage(sdkClickPackage, "Sdk_click request failed. Will retry later", e); - retrySending(sdkClickPackage); + logErrorMessageI(sdkClickPackage, "Sdk_click request failed. Will retry later", e); + retrySendingI(sdkClickPackage); } catch (Throwable e) { - logErrorMessage(sdkClickPackage, "Sdk_click runtime exception", e); + logErrorMessageI(sdkClickPackage, "Sdk_click runtime exception", e); } } - private void retrySending(ActivityPackage sdkClickPackage) { + private void retrySendingI(ActivityPackage sdkClickPackage) { int retries = sdkClickPackage.increaseRetries(); logger.error("Retrying sdk_click package for the %d time", retries); sendSdkClick(sdkClickPackage); } - private void logErrorMessage(ActivityPackage sdkClickPackage, String message, Throwable throwable) { + private void logErrorMessageI(ActivityPackage sdkClickPackage, String message, Throwable throwable) { final String packageMessage = sdkClickPackage.getFailureMessage(); final String reasonString = Util.getReasonString(message, throwable); String finalMessage = String.format("%s. (%s)", packageMessage, reasonString); diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/SessionParameters.java b/Adjust/adjust/src/main/java/com/adjust/sdk/SessionParameters.java new file mode 100644 index 000000000..16610e391 --- /dev/null +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/SessionParameters.java @@ -0,0 +1,28 @@ +package com.adjust.sdk; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Created by pfms on 29/07/2016. + */ +public class SessionParameters { + Map callbackParameters; + Map partnerParameters; + + public SessionParameters deepCopy() { + SessionParameters newSessionParameters = new SessionParameters(); + if (this.callbackParameters != null) { + newSessionParameters.callbackParameters = new HashMap(this.callbackParameters); + } + if (this.partnerParameters != null) { + newSessionParameters.partnerParameters = new HashMap(this.partnerParameters); + } + return newSessionParameters; + } +} diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/TimerCycle.java b/Adjust/adjust/src/main/java/com/adjust/sdk/TimerCycle.java index 6e302b19e..9dffc356c 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/TimerCycle.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/TimerCycle.java @@ -1,7 +1,6 @@ package com.adjust.sdk; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; +import java.lang.ref.WeakReference; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -9,18 +8,17 @@ * Created by pfms on 08/05/15. */ public class TimerCycle { - private ScheduledExecutorService scheduler; + private WeakReference scheduledExecutorWeakRef; private ScheduledFuture waitingTask; private String name; private Runnable command; private long initialDelay; private long cycleDelay; - private String cycleDelaySeconds; private boolean isPaused; private ILogger logger; - public TimerCycle(Runnable command, long initialDelay, long cycleDelay, String name) { - this.scheduler = Executors.newSingleThreadScheduledExecutor(); + public TimerCycle(CustomScheduledExecutor scheduler, Runnable command, long initialDelay, long cycleDelay, String name) { + this.scheduledExecutorWeakRef = new WeakReference(scheduler); this.name = name; this.command = command; @@ -29,7 +27,11 @@ public TimerCycle(Runnable command, long initialDelay, long cycleDelay, String n this.isPaused = true; this.logger = AdjustFactory.getLogger(); - this.cycleDelaySeconds = Util.SecondsDisplayFormat.format(cycleDelay / 1000.0); + String cycleDelaySecondsString = Util.SecondsDisplayFormat.format(cycleDelay / 1000.0); + + String initialDelaySecondsString = Util.SecondsDisplayFormat.format(initialDelay / 1000.0); + + logger.verbose("%s configured to fire after %s seconds of starting and cycles every %s seconds", name, initialDelaySecondsString, cycleDelaySecondsString); } public void start() { @@ -38,11 +40,17 @@ public void start() { return; } - String initialDelaySeconds = Util.SecondsDisplayFormat.format(initialDelay / 1000.0); + CustomScheduledExecutor scheduledExecutor = scheduledExecutorWeakRef.get(); + if (scheduledExecutor == null) { + return; + } + + //String initialDelaySeconds = Util.SecondsDisplayFormat.format(initialDelay / 1000.0); + //logger.verbose("%s starting in %s seconds and cycle every %s seconds", name, initialDelaySeconds, cycleDelaySeconds); - logger.verbose("%s starting in %s seconds and cycle every %s seconds", name, initialDelaySeconds, cycleDelaySeconds); + logger.verbose("%s starting", name); - waitingTask = scheduler.scheduleWithFixedDelay(new Runnable() { + waitingTask = scheduledExecutor.scheduleWithFixedDelay(new Runnable() { @Override public void run() { logger.verbose("%s fired", name); @@ -64,7 +72,6 @@ public void suspend() { // cancel the timer waitingTask.cancel(false); - waitingTask = null; String initialDelaySeconds = Util.SecondsDisplayFormat.format(initialDelay / 1000.0); @@ -72,4 +79,20 @@ public void suspend() { isPaused = true; } + + private void cancel(boolean mayInterruptIfRunning) { + if (waitingTask != null) { + waitingTask.cancel(mayInterruptIfRunning); + } + + waitingTask = null; + } + + public void teardown() { + cancel(true); + if (scheduledExecutorWeakRef != null) { + scheduledExecutorWeakRef.clear(); + } + scheduledExecutorWeakRef = null; + } } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/TimerOnce.java b/Adjust/adjust/src/main/java/com/adjust/sdk/TimerOnce.java index 7fee60415..1611db2b0 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/TimerOnce.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/TimerOnce.java @@ -1,6 +1,6 @@ package com.adjust.sdk; -import java.util.concurrent.ScheduledExecutorService; +import java.lang.ref.WeakReference; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -8,15 +8,15 @@ * Created by pfms on 08/05/15. */ public class TimerOnce { - private ScheduledExecutorService scheduler; + private WeakReference scheduledExecutorWeakRef; private ScheduledFuture waitingTask; private String name; private Runnable command; private ILogger logger; - public TimerOnce(ScheduledExecutorService scheduler, Runnable command, String name) { + public TimerOnce(CustomScheduledExecutor scheduler, Runnable command, String name) { this.name = name; - this.scheduler = scheduler; + this.scheduledExecutorWeakRef = new WeakReference(scheduler); this.command = command; this.logger = AdjustFactory.getLogger(); } @@ -25,11 +25,16 @@ public void startIn(long fireIn) { // cancel previous cancel(false); + CustomScheduledExecutor scheduledExecutor = scheduledExecutorWeakRef.get(); + if (scheduledExecutor == null) { + return; + } + String fireInSeconds = Util.SecondsDisplayFormat.format(fireIn / 1000.0); logger.verbose("%s starting. Launching in %s seconds", name, fireInSeconds); - waitingTask = scheduler.schedule(new Runnable() { + waitingTask = scheduledExecutor.schedule(new Runnable() { @Override public void run() { logger.verbose("%s fired", name); @@ -46,18 +51,24 @@ public long getFireIn() { return waitingTask.getDelay(TimeUnit.MILLISECONDS); } - private void cancel(boolean log) { + private void cancel(boolean mayInterruptIfRunning) { if (waitingTask != null) { - waitingTask.cancel(false); + waitingTask.cancel(mayInterruptIfRunning); } waitingTask = null; - if (log) { - logger.verbose("%s canceled", name); - } + logger.verbose("%s canceled", name); } public void cancel() { + cancel(false); + } + + public void teardown() { cancel(true); + if (scheduledExecutorWeakRef != null) { + scheduledExecutorWeakRef.clear(); + } + scheduledExecutorWeakRef = null; } } diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/UnitTestActivity.java b/Adjust/adjust/src/main/java/com/adjust/sdk/UnitTestActivity.java deleted file mode 100644 index 799fb8982..000000000 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/UnitTestActivity.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.adjust.sdk; - -import android.app.Activity; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; - -public class UnitTestActivity extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - //setContentView(com.adjust.sdk.test.R.layout.activity_unit_test); - } - - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - //getMenuInflater().inflate(com.adjust.sdk.test.R.menu.menu_unit_test, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. -/* int id = item.getItemId(); - - //noinspection SimplifiableIfStatement - if (id == com.adjust.sdk.test.R.id.action_settings) { - return true; - } -*/ - return super.onOptionsItemSelected(item); - } -} diff --git a/Adjust/adjust/src/main/java/com/adjust/sdk/Util.java b/Adjust/adjust/src/main/java/com/adjust/sdk/Util.java index e0ae3faa0..a853415ef 100644 --- a/Adjust/adjust/src/main/java/com/adjust/sdk/Util.java +++ b/Adjust/adjust/src/main/java/com/adjust/sdk/Util.java @@ -38,6 +38,7 @@ import java.security.MessageDigest; import java.text.DecimalFormat; import java.text.SimpleDateFormat; +import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Random; @@ -55,11 +56,12 @@ * Collects utility functions used by Adjust. */ public class Util { - - private static SimpleDateFormat dateFormat; private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'Z"; private static final String fieldReadErrorMessage = "Unable to read '%s' field in migration device with message (%s)"; public static final DecimalFormat SecondsDisplayFormat = new DecimalFormat("0.0"); + public static final SimpleDateFormat dateFormatter = new SimpleDateFormat(DATE_FORMAT, Locale.US); + + private static String userAgent; private static ILogger getLogger() { return AdjustFactory.getLogger(); @@ -83,13 +85,6 @@ public static String quote(String string) { return String.format(Locale.US, "'%s'", string); } - public static String dateFormat(long date) { - if (dateFormat == null) { - dateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.US); - } - return dateFormat.format(date); - } - public static String getPlayAdId(Context context) { return Reflection.getPlayAdId(context); } @@ -192,6 +187,7 @@ public static void writeObject(T object, Context context, String filename, S try { objectStream.writeObject(object); + getLogger().debug("Wrote %s: %s", objectName, object); } catch (NotSerializableException e) { getLogger().error("Failed to serialize %s", objectName); @@ -213,6 +209,8 @@ public static ResponseData readHttpResponse(HttpsURLConnection connection, Activ ILogger logger = getLogger(); Integer responseCode = null; try { + connection.connect(); + responseCode = connection.getResponseCode(); InputStream inputStream; @@ -286,15 +284,24 @@ public static ResponseData readHttpResponse(HttpsURLConnection connection, Activ public static AdjustFactory.URLGetConnection createGETHttpsURLConnection(String urlString, String clientSdk) throws IOException { - URL url = new URL(urlString); - AdjustFactory.URLGetConnection urlGetConnection = AdjustFactory.getHttpsURLGetConnection(url); + HttpsURLConnection connection = null; + try { + URL url = new URL(urlString); + AdjustFactory.URLGetConnection urlGetConnection = AdjustFactory.getHttpsURLGetConnection(url); - HttpsURLConnection connection = urlGetConnection.httpsURLConnection; - setDefaultHttpsUrlConnectionProperties(connection, clientSdk); + connection = urlGetConnection.httpsURLConnection; + setDefaultHttpsUrlConnectionProperties(connection, clientSdk); - connection.setRequestMethod("GET"); + connection.setRequestMethod("GET"); - return urlGetConnection; + return urlGetConnection; + } catch (IOException e) { + throw e; + } finally { + if (connection != null) { + connection.disconnect(); + } + } } public static HttpsURLConnection createPOSTHttpsURLConnection(String urlString, String clientSdk, @@ -302,22 +309,37 @@ public static HttpsURLConnection createPOSTHttpsURLConnection(String urlString, int queueSize) throws IOException { - URL url = new URL(urlString); - HttpsURLConnection connection = AdjustFactory.getHttpsURLConnection(url); + DataOutputStream wr = null; + HttpsURLConnection connection = null; + try { + URL url = new URL(urlString); + connection = AdjustFactory.getHttpsURLConnection(url); + + setDefaultHttpsUrlConnectionProperties(connection, clientSdk); + connection.setRequestMethod("POST"); - setDefaultHttpsUrlConnectionProperties(connection, clientSdk); - connection.setRequestMethod("POST"); + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(true); - connection.setUseCaches(false); - connection.setDoInput(true); - connection.setDoOutput(true); + wr = new DataOutputStream(connection.getOutputStream()); + wr.writeBytes(getPostDataString(parameters, queueSize)); - DataOutputStream wr = new DataOutputStream(connection.getOutputStream()); - wr.writeBytes(getPostDataString(parameters, queueSize)); - wr.flush(); - wr.close(); + return connection; + } catch (IOException e) { + throw e; + } finally { + try { + if (wr != null) { + wr.flush(); + wr.close(); + } + }catch (Exception e) { } - return connection; + if (connection != null) { + connection.disconnect(); + } + } } private static String getPostDataString(Map body, int queueSize) throws UnsupportedEncodingException { @@ -337,18 +359,19 @@ private static String getPostDataString(Map body, int queueSize) } long now = System.currentTimeMillis(); - String dateString = Util.dateFormat(now); + String dateString = Util.dateFormatter.format(now); result.append("&"); result.append(URLEncoder.encode("sent_at", Constants.ENCODING)); result.append("="); result.append(URLEncoder.encode(dateString, Constants.ENCODING)); - result.append("&"); - result.append(URLEncoder.encode("queue_size", Constants.ENCODING)); - result.append("="); - result.append(URLEncoder.encode("" + queueSize, Constants.ENCODING)); - + if (queueSize > 0) { + result.append("&"); + result.append(URLEncoder.encode("queue_size", Constants.ENCODING)); + result.append("="); + result.append(URLEncoder.encode("" + queueSize, Constants.ENCODING)); + } return result.toString(); } @@ -357,6 +380,9 @@ public static void setDefaultHttpsUrlConnectionProperties(HttpsURLConnection con connection.setRequestProperty("Client-SDK", clientSdk); connection.setConnectTimeout(Constants.ONE_MINUTE); connection.setReadTimeout(Constants.ONE_MINUTE); + if (userAgent != null) { + connection.setRequestProperty("User-Agent", userAgent); + } } public static boolean checkPermission(Context context, String permission) { @@ -411,13 +437,6 @@ public static boolean equalObject(Object first, Object second) { return first.equals(second); } - public static boolean equalsMap(Map first, Map second) { - if (first == null || second == null) { - return first == null && second == null; - } - return first.entrySet().equals(second.entrySet()); - } - public static boolean equalsDouble(Double first, Double second) { if (first == null || second == null) { return first == null && second == null; @@ -473,11 +492,11 @@ public static int hashEnum(Enum value) { return value.hashCode(); } - public static int hashMap(Map value) { + public static int hashObject(Object value) { if (value == null) { return 0; } - return value.entrySet().hashCode(); + return value.hashCode(); } public static String sha1(final String text) { @@ -547,4 +566,45 @@ private static double randomInRange(double minRange, double maxRange) { double shifted = scaled + minRange; return shifted; } + + public static boolean isValidParameter(String attribute, String attributeType, String parameterName) { + if (attribute == null) { + getLogger().error("%s parameter %s is missing", parameterName, attributeType); + return false; + } + if (attribute.equals("")) { + getLogger().error("%s parameter %s is empty", parameterName, attributeType); + return false; + } + + return true; + } + + public static Map mergeParameters(Map target, + Map source, + String parameterName) { + if (target == null) { + return source; + } + if (source == null) { + return target; + } + Map mergedParameters = new HashMap(target); + ILogger logger = getLogger(); + for (Map.Entry parameterSourceEntry : source.entrySet()) { + String oldValue = mergedParameters.put(parameterSourceEntry.getKey(), parameterSourceEntry.getValue()); + if (oldValue != null) { + logger.warn("Key %s with value %s from %s parameter was replaced by value %s", + parameterSourceEntry.getKey(), + oldValue, + parameterName, + parameterSourceEntry.getValue()); + } + } + return mergedParameters; + } + + public static void setUserAgent(String userAgent) { + Util.userAgent = userAgent; + } } diff --git a/Adjust/adjust/src/main/res/values/strings.xml b/Adjust/adjust/src/main/res/values/strings.xml index 5cf65ffdb..854200555 100644 --- a/Adjust/adjust/src/main/res/values/strings.xml +++ b/Adjust/adjust/src/main/res/values/strings.xml @@ -1,3 +1,2 @@ - adjust diff --git a/Adjust/build.gradle b/Adjust/build.gradle index e220f0b80..d14039de2 100644 --- a/Adjust/build.gradle +++ b/Adjust/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' + classpath 'com.android.tools.build:gradle:2.1.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/Adjust/example/build.gradle b/Adjust/example/build.gradle index 2924dcad8..b6f025a4c 100644 --- a/Adjust/example/build.gradle +++ b/Adjust/example/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 24 - buildToolsVersion "24.0.1" + buildToolsVersion "24.0.2" defaultConfig { applicationId "com.adjust.example" @@ -28,5 +28,10 @@ dependencies { // running mvn package //compile fileTree(dir: '../target', include: ['*.jar']) // using maven repository - //compile 'com.adjust.sdk:adjust-android:4.7.0' + //compile 'com.adjust.sdk:adjust-android:4.10.0' + + debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2' + releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2' + testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2' + } diff --git a/Adjust/example/src/main/java/com/adjust/example/GlobalApplication.java b/Adjust/example/src/main/java/com/adjust/example/GlobalApplication.java index 7db52925d..dd2777e3c 100644 --- a/Adjust/example/src/main/java/com/adjust/example/GlobalApplication.java +++ b/Adjust/example/src/main/java/com/adjust/example/GlobalApplication.java @@ -4,6 +4,7 @@ import android.app.Application; import android.net.Uri; import android.os.Bundle; +import android.os.StrictMode; import android.util.Log; import com.adjust.sdk.Adjust; @@ -20,6 +21,7 @@ import com.adjust.sdk.OnSessionTrackingSucceededListener; import com.adjust.sdk.AdjustSessionFailure; import com.adjust.sdk.AdjustSessionSuccess; +import com.squareup.leakcanary.LeakCanary; /** * Created by pfms on 17/12/14. @@ -27,9 +29,22 @@ public class GlobalApplication extends Application { @Override public void onCreate() { + LeakCanary.install(this); + + StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() + .detectAll() + .penaltyLog() + .penaltyDialog() + .build()); + StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() + .detectAll() + .penaltyLog() + .build()); + super.onCreate(); // configure Adjust - String appToken = "rb4g27fje5ej"; + String appToken = "qwerty123456"; + String environment = AdjustConfig.ENVIRONMENT_SANDBOX; AdjustConfig config = new AdjustConfig(this, appToken, environment); diff --git a/Adjust/gradle.properties b/Adjust/gradle.properties index 1d3591c8a..87bd032ce 100644 --- a/Adjust/gradle.properties +++ b/Adjust/gradle.properties @@ -15,4 +15,8 @@ # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true \ No newline at end of file +# org.gradle.parallel=true +org.gradle.jvmargs=-Xmx2048M +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.configureondemand=true diff --git a/Adjust/gradle/wrapper/gradle-wrapper.properties b/Adjust/gradle/wrapper/gradle-wrapper.properties index 2fa296fec..49001fcef 100644 --- a/Adjust/gradle/wrapper/gradle-wrapper.properties +++ b/Adjust/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Jul 22 12:08:05 CEST 2016 +#Tue Aug 16 15:21:14 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/Adjust/pom.xml b/Adjust/pom.xml index 566f9e473..8b43f372d 100644 --- a/Adjust/pom.xml +++ b/Adjust/pom.xml @@ -5,7 +5,7 @@ 4.0.0 adjust-android com.adjust.sdk - 4.7.0 + 4.10.0 jar Adjust Android SDK https://github.com/adjust/android_sdk diff --git a/Adjust/pom_criteo.xml b/Adjust/pom_criteo.xml index b66f98c42..c80ecdf7a 100644 --- a/Adjust/pom_criteo.xml +++ b/Adjust/pom_criteo.xml @@ -5,7 +5,7 @@ 4.0.0 adjust-android-criteo com.adjust.sdk - 4.7.0 + 4.10.0 jar Adjust Android SDK https://github.com/adjust/android_sdk diff --git a/Adjust/pom_sociomantic.xml b/Adjust/pom_sociomantic.xml index 39ff95238..9bbfa9e61 100644 --- a/Adjust/pom_sociomantic.xml +++ b/Adjust/pom_sociomantic.xml @@ -5,7 +5,7 @@ 4.0.0 adjust-android-sociomantic com.adjust.sdk - 4.7.0 + 4.10.0 jar Adjust Android SDK https://github.com/adjust/android_sdk diff --git a/Adjust/pom_trademob.xml b/Adjust/pom_trademob.xml index 7546ca782..1f1596ab5 100644 --- a/Adjust/pom_trademob.xml +++ b/Adjust/pom_trademob.xml @@ -5,7 +5,7 @@ 4.0.0 adjust-android-trademob com.adjust.sdk - 4.7.0 + 4.10.0 jar Adjust Android SDK https://github.com/adjust/android_sdk diff --git a/Adjust/test/build.gradle b/Adjust/test/build.gradle index ce9fce844..6db3d4b4d 100644 --- a/Adjust/test/build.gradle +++ b/Adjust/test/build.gradle @@ -2,12 +2,15 @@ apply plugin: 'com.android.library' android { compileSdkVersion 24 - buildToolsVersion "24.0.1" + buildToolsVersion "24.0.2" defaultConfig { minSdkVersion 9 targetSdkVersion 24 } + defaultConfig { + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } } repositories { @@ -19,4 +22,7 @@ dependencies { compile 'com.google.android.gms:play-services-analytics:8.4.0' compile fileTree(dir: 'libs', include: ['*.jar']) compile project(':adjust') + androidTestCompile 'com.android.support.test:runner:0.4' + // Set this dependency to use JUnit 4 rules + androidTestCompile 'com.android.support.test:rules:0.4' } diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/ApplicationTest.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/ApplicationTest.java similarity index 91% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/ApplicationTest.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/ApplicationTest.java index 790870026..77072ab2c 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/ApplicationTest.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/ApplicationTest.java @@ -1,4 +1,4 @@ -package com.adjust.sdk.test; +package com.adjust.sdk; import android.app.Application; import android.test.ApplicationTestCase; diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/AssertUtil.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/AssertUtil.java similarity index 95% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/AssertUtil.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/AssertUtil.java index c6638fb3c..b3a637dcf 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/AssertUtil.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/AssertUtil.java @@ -1,4 +1,6 @@ -package com.adjust.sdk.test; +package com.adjust.sdk; + +import android.net.Uri; import junit.framework.Assert; @@ -126,7 +128,16 @@ public void isEqual(int expected, int actual) { Assert.assertEquals(mockLogger.toString(), expected, actual); } + public void isEqual(Uri expected, Uri actual) { + Assert.assertEquals(mockLogger.toString(), expected, actual); + } + public void fail() { Assert.fail(mockLogger.toString()); } + + public void fail(String extraMessage) { + Assert.fail(extraMessage + "\n" + mockLogger.toString()); + } + } diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockActivityHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockActivityHandler.java similarity index 64% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockActivityHandler.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/MockActivityHandler.java index 47fd6cd21..52159e83a 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockActivityHandler.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockActivityHandler.java @@ -1,17 +1,7 @@ -package com.adjust.sdk.test; +package com.adjust.sdk; import android.net.Uri; -import com.adjust.sdk.ActivityPackage; -import com.adjust.sdk.AdjustAttribution; -import com.adjust.sdk.AdjustConfig; -import com.adjust.sdk.AdjustEvent; -import com.adjust.sdk.IActivityHandler; -import com.adjust.sdk.ResponseData; -import com.adjust.sdk.EventResponseData; -import com.adjust.sdk.SessionResponseData; -import com.adjust.sdk.AttributionResponseData; - /** * Created by pfms on 09/01/15. @@ -70,8 +60,8 @@ public void readOpenUrl(Uri url, long clickTime) { } @Override - public boolean updateAttribution(AdjustAttribution attribution) { - testLogger.test(prefix + "updateAttribution, " + attribution); + public boolean updateAttributionI(AdjustAttribution attribution) { + testLogger.test(prefix + "updateAttributionI, " + attribution); return false; } @@ -107,4 +97,49 @@ public void setOfflineMode(boolean enabled) { public void setAskingAttribution(boolean askingAttribution) { testLogger.test(prefix + "setAskingAttribution, " + askingAttribution); } + + @Override + public void sendFirstPackages() { + testLogger.test(prefix + "sendFirstPackages"); + } + + @Override + public void addSessionCallbackParameter(String key, String value) { + testLogger.test(prefix + "addSessionCallbackParameter key, " + key + ", value, " + value); + } + + @Override + public void addSessionPartnerParameter(String key, String value) { + testLogger.test(prefix + "addSessionPartnerParameter key, " + key + ", value, " + value); + } + + @Override + public void removeSessionCallbackParameter(String key) { + testLogger.test(prefix + "removeSessionCallbackParameter, " + key); + } + + @Override + public void removeSessionPartnerParameter(String key) { + testLogger.test(prefix + "removeSessionPartnerParameter, " + key); + } + + @Override + public void resetSessionCallbackParameters() { + testLogger.test(prefix + "resetSessionCallbackParameters"); + } + + @Override + public void resetSessionPartnerParameters() { + testLogger.test(prefix + "resetSessionPartnerParameters"); + } + + @Override + public void teardown(boolean deleteState) { + testLogger.test(prefix + "teardown deleteState, " + deleteState); + } + + @Override + public void setPushToken(String token) { + testLogger.test(prefix + "setPushToken token, " + token); + } } diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockAttributionHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockAttributionHandler.java similarity index 87% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockAttributionHandler.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/MockAttributionHandler.java index 81a8ee01e..03b8ad5a4 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockAttributionHandler.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockAttributionHandler.java @@ -1,9 +1,4 @@ -package com.adjust.sdk.test; - -import com.adjust.sdk.ActivityPackage; -import com.adjust.sdk.IActivityHandler; -import com.adjust.sdk.IAttributionHandler; -import com.adjust.sdk.SessionResponseData; +package com.adjust.sdk; /** * Created by pfms on 09/01/15. @@ -51,4 +46,9 @@ public void pauseSending() { public void resumeSending() { testLogger.test(prefix + "resumeSending"); } + + @Override + public void teardown() { + testLogger.test(prefix + "teardown"); + } } diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockHttpsURLConnection.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockHttpsURLConnection.java similarity index 99% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockHttpsURLConnection.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/MockHttpsURLConnection.java index b2e5cbbaa..8d6910692 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockHttpsURLConnection.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockHttpsURLConnection.java @@ -1,4 +1,4 @@ -package com.adjust.sdk.test; +package com.adjust.sdk; import android.os.SystemClock; diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockLogger.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockLogger.java similarity index 98% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockLogger.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/MockLogger.java index e477fcb58..41ef98295 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockLogger.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockLogger.java @@ -1,4 +1,4 @@ -package com.adjust.sdk.test; +package com.adjust.sdk; import android.util.Log; import android.util.SparseArray; @@ -136,6 +136,11 @@ public void Assert(String message, Object... parameters) { Log.ASSERT); } + @Override + public void lockLogLevel() { + test("MockLogger lockLogLevel"); + } + public void test(String message) { logMessage(message, TEST_LEVEL, "t", Log.VERBOSE); } diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockPackageHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockPackageHandler.java similarity index 81% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockPackageHandler.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/MockPackageHandler.java index 5e8ef21b0..2c9742f40 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockPackageHandler.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockPackageHandler.java @@ -1,12 +1,7 @@ -package com.adjust.sdk.test; +package com.adjust.sdk; import android.content.Context; -import com.adjust.sdk.ActivityPackage; -import com.adjust.sdk.IActivityHandler; -import com.adjust.sdk.IPackageHandler; -import com.adjust.sdk.ResponseData; - import java.util.ArrayList; import java.util.List; @@ -19,7 +14,6 @@ public class MockPackageHandler implements IPackageHandler { public MockPackageHandler(MockLogger testLogger) { this.testLogger = testLogger; - queue = new ArrayList(); } @Override @@ -27,6 +21,7 @@ public void init(IActivityHandler activityHandler, Context context, boolean star testLogger.test(prefix + "init, startsSending: " + startsSending); this.activityHandler = activityHandler; this.context = context; + this.queue = new ArrayList(); } @Override @@ -65,4 +60,14 @@ public void pauseSending() { public void resumeSending() { testLogger.test(prefix + "resumeSending"); } + + @Override + public void updatePackages(SessionParameters sessionParameters) { + testLogger.test(prefix + "updatePackages, sessionParameters" + sessionParameters); + } + + @Override + public void teardown(boolean deleteState) { + testLogger.test(prefix + "teardown deleteState, " + deleteState); + } } diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockRequestHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockRequestHandler.java similarity index 88% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockRequestHandler.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/MockRequestHandler.java index 9944b70a4..6605fa762 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockRequestHandler.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockRequestHandler.java @@ -1,8 +1,4 @@ -package com.adjust.sdk.test; - -import com.adjust.sdk.ActivityPackage; -import com.adjust.sdk.IPackageHandler; -import com.adjust.sdk.IRequestHandler; +package com.adjust.sdk; public class MockRequestHandler implements IRequestHandler { private MockLogger testLogger; @@ -36,4 +32,9 @@ public void sendPackage(ActivityPackage activityPackage, int queueSize) { } */ } + + @Override + public void teardown() { + testLogger.test(prefix + "teardown"); + } } diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockSdkClickHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockSdkClickHandler.java similarity index 88% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockSdkClickHandler.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/MockSdkClickHandler.java index df4a65ba2..dd64352d0 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/MockSdkClickHandler.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/MockSdkClickHandler.java @@ -1,7 +1,4 @@ -package com.adjust.sdk.test; - -import com.adjust.sdk.ActivityPackage; -import com.adjust.sdk.ISdkClickHandler; +package com.adjust.sdk; import java.util.ArrayList; import java.util.List; @@ -38,4 +35,9 @@ public void sendSdkClick(ActivityPackage sdkClick) { testLogger.test(prefix + "sendSdkClick"); queue.add(sdkClick); } + + @Override + public void teardown() { + testLogger.test(prefix + "teardown"); + } } diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/ResponseType.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/ResponseType.java similarity index 84% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/ResponseType.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/ResponseType.java index dbd770524..0f95969de 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/ResponseType.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/ResponseType.java @@ -1,4 +1,4 @@ -package com.adjust.sdk.test; +package com.adjust.sdk; /** * Created by pfms on 28/01/15. diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/StateActivityHandlerConstructor.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/StateActivityHandlerConstructor.java new file mode 100644 index 000000000..96f8be57f --- /dev/null +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/StateActivityHandlerConstructor.java @@ -0,0 +1,21 @@ +package com.adjust.sdk; + +import com.adjust.sdk.ActivityHandler; +import com.adjust.sdk.AdjustConfig; +import com.adjust.sdk.IRunActivityHandler; +import com.adjust.sdk.LogLevel; + +import java.util.List; + +/** + * Created by pfms on 09/08/2016. + */ +public class StateActivityHandlerConstructor { + AdjustConfig config; + boolean startEnabled = true; + boolean isToUpdatePackages = false; + + StateActivityHandlerConstructor(AdjustConfig config) { + this.config = config; + } +} diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/StateActivityHandlerInit.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/StateActivityHandlerInit.java new file mode 100644 index 000000000..149938b08 --- /dev/null +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/StateActivityHandlerInit.java @@ -0,0 +1,30 @@ +package com.adjust.sdk; + +import com.adjust.sdk.ActivityHandler; + +/** + * Created by pfms on 09/08/2016. + */ +public class StateActivityHandlerInit { + ActivityHandler.InternalState internalState; + boolean startEnabled = true; + boolean updatePackages = false; + boolean startsSending = false; + boolean sdkClickHandlerAlsoStartsPaused = true; + String defaultTracker = null; + boolean eventBufferingIsEnabled = false; + boolean sendInBackgroundConfigured = false; + boolean delayStartConfigured = false; + boolean activityStateAlreadyCreated = false; + String sendReferrer = null; + String readActivityState = null; + String readAttribution = null; + String readCallbackParameters = null; + String readPartnerParameters = null; + int foregroundTimerStart = 60; + int foregroundTimerCycle = 60; + + StateActivityHandlerInit(ActivityHandler activityHandler) { + internalState = activityHandler.getInternalState(); + } +} diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/StateDelegates.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/StateDelegates.java new file mode 100644 index 000000000..cbe6fb746 --- /dev/null +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/StateDelegates.java @@ -0,0 +1,12 @@ +package com.adjust.sdk; + +/** + * Created by pfms on 15/08/2016. + */ +public class StateDelegates { + boolean attributionDelegatePresent; + boolean eventSuccessDelegatePresent; + boolean eventFailureDelegatePresent; + boolean sessionSuccessDelegatePresent; + boolean sessionFailureDelegatePresent; +} diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/StateEndSession.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/StateEndSession.java new file mode 100644 index 000000000..d0a4e71a6 --- /dev/null +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/StateEndSession.java @@ -0,0 +1,13 @@ +package com.adjust.sdk; + +/** + * Created by pfms on 11/08/2016. + */ +public class StateEndSession { + boolean pausing = true; + boolean updateActivityState = true; + boolean eventBufferingEnabled = false; + boolean checkOnPause = false; + boolean foregroundAlreadySuspended = false; + boolean backgroundTimerStarts = false; +} diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/StateEvent.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/StateEvent.java new file mode 100644 index 000000000..57933092c --- /dev/null +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/StateEvent.java @@ -0,0 +1,13 @@ +package com.adjust.sdk; + +/** + * Created by pfms on 11/08/2016. + */ +public class StateEvent { + String bufferedSuffix = null; + Integer backgroundTimerStarts = null; + String activityStateSuffix = null; + String orderId = null; + boolean duplicatedOrderId = false; + boolean disabled = false; +} diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/StateSession.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/StateSession.java new file mode 100644 index 000000000..b081aefa6 --- /dev/null +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/StateSession.java @@ -0,0 +1,34 @@ +package com.adjust.sdk; + +/** + * Created by pfms on 09/08/2016. + */ +public class StateSession { + boolean toSend = true; + int sessionCount = 1; + int subsessionCount = 1; + SessionType sessionType = null; + int eventCount = 0; + Boolean getAttributionIsCalled = null; + boolean eventBufferingIsEnabled = false; + boolean foregroundTimerStarts = true; + boolean foregroundTimerAlreadyStarted = false; + boolean sendInBackgroundConfigured = false; + boolean sdkClickHandlerAlsoStartsPaused = true; + boolean startSubsession = true; + boolean disabled = false; + String delayStart = null; + boolean activityStateAlreadyCreated = false; + + public enum SessionType { + NEW_SESSION, + NEW_SUBSESSION, + TIME_TRAVEL, + NONSESSION, + DISABLED, + } + + StateSession(SessionType sessionType) { + this.sessionType = sessionType; + } +} diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestActivityHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/TestActivityHandler.java similarity index 52% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestActivityHandler.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/TestActivityHandler.java index 3ca078b37..df396da3e 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestActivityHandler.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/TestActivityHandler.java @@ -1,62 +1,46 @@ -package com.adjust.sdk.test; +package com.adjust.sdk; import android.content.Context; import android.content.pm.PackageManager; import android.net.Uri; import android.os.SystemClock; -import android.test.ActivityInstrumentationTestCase2; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; import android.test.mock.MockContext; - -import com.adjust.sdk.ActivityHandler; -import com.adjust.sdk.ActivityHandler.InternalState; -import com.adjust.sdk.ActivityPackage; -import com.adjust.sdk.Adjust; -import com.adjust.sdk.AdjustAttribution; -import com.adjust.sdk.AdjustConfig; -import com.adjust.sdk.AdjustEvent; -import com.adjust.sdk.AdjustEventFailure; -import com.adjust.sdk.AdjustEventSuccess; -import com.adjust.sdk.AdjustFactory; -import com.adjust.sdk.AdjustSessionFailure; -import com.adjust.sdk.AdjustSessionSuccess; -import com.adjust.sdk.AttributionResponseData; -import com.adjust.sdk.ClickResponseData; -import com.adjust.sdk.Constants; -import com.adjust.sdk.EventResponseData; -import com.adjust.sdk.LogLevel; -import com.adjust.sdk.OnAttributionChangedListener; -import com.adjust.sdk.OnDeeplinkResponseListener; -import com.adjust.sdk.OnEventTrackingFailedListener; -import com.adjust.sdk.OnEventTrackingSucceededListener; -import com.adjust.sdk.OnSessionTrackingFailedListener; -import com.adjust.sdk.OnSessionTrackingSucceededListener; -import com.adjust.sdk.ResponseData; -import com.adjust.sdk.SessionResponseData; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; import org.json.JSONException; import org.json.JSONObject; - - -public class TestActivityHandler extends ActivityInstrumentationTestCase2 { +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Created by pfms on 08/08/2016. + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class TestActivityHandler { protected MockLogger mockLogger; protected MockPackageHandler mockPackageHandler; protected MockAttributionHandler mockAttributionHandler; protected MockSdkClickHandler mockSdkClickHandler; - protected UnitTestActivity activity; + protected com.adjust.sdk.test.UnitTestActivity activity; protected Context context; protected AssertUtil assertUtil; - public TestActivityHandler() { - super(UnitTestActivity.class); - } - - public TestActivityHandler(Class mainActivity) { - super(mainActivity); - } + @Rule + public ActivityTestRule mActivityRule = new ActivityTestRule(com.adjust.sdk.test.UnitTestActivity.class); - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() { mockLogger = new MockLogger(); mockPackageHandler = new MockPackageHandler(mockLogger); mockAttributionHandler = new MockAttributionHandler(mockLogger); @@ -68,26 +52,22 @@ protected void setUp() throws Exception { AdjustFactory.setAttributionHandler(mockAttributionHandler); AdjustFactory.setSdkClickHandler(mockSdkClickHandler); - activity = getActivity(); + //activity = launchActivity(null); + activity = mActivityRule.getActivity(); context = activity.getApplicationContext(); - // deleting the activity state file to simulate a first session - boolean activityStateDeleted = ActivityHandler.deleteActivityState(context); - boolean attributionDeleted = ActivityHandler.deleteAttribution(context); - - mockLogger.test("Was AdjustActivityState deleted? " + activityStateDeleted); - - // deleting the attribution file to simulate a first session - mockLogger.test("Was Attribution deleted? " + attributionDeleted); + // deleting state to simulate fresh install + mockLogger.test("Was AdjustActivityState deleted? " + ActivityHandler.deleteActivityState(context)); + mockLogger.test("Was Attribution deleted? " + ActivityHandler.deleteAttribution(context)); + mockLogger.test("Was Session Callback Parameters deleted? " + ActivityHandler.deleteSessionCallbackParameters(context)); + mockLogger.test("Was Session Partner Parameters deleted? " + ActivityHandler.deleteSessionPartnerParameters(context)); // check the server url - assertEquals(Constants.BASE_URL, "https://app.adjust.com"); + assertUtil.isEqual(Constants.BASE_URL, "https://app.adjust.com"); } - @Override - protected void tearDown() throws Exception { - super.tearDown(); - + @After + public void tearDown() { AdjustFactory.setPackageHandler(null); AdjustFactory.setAttributionHandler(null); AdjustFactory.setSdkClickHandler(null); @@ -101,6 +81,7 @@ protected void tearDown() throws Exception { context = null; } + @Test public void testFirstSession() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testFirstSession"); @@ -113,7 +94,7 @@ public void testFirstSession() { // checking the default values of the first session package // should only have one package - assertEquals(1, mockPackageHandler.queue.size()); + assertUtil.isEqual(1, mockPackageHandler.queue.size()); ActivityPackage activityPackage = mockPackageHandler.queue.get(0); @@ -124,36 +105,35 @@ public void testFirstSession() { testActivityPackage.testSessionPackage(1); } + @Test public void testEventsBuffered() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testEventsBuffered"); // create the config to start the session - AdjustConfig config = getConfig(LogLevel.VERBOSE); + AdjustConfig config = getConfig(); // buffer events config.setEventBufferingEnabled(true); - // start activity handler with config - ActivityHandler activityHandler = getFirstActivityHandler(config, LogLevel.VERBOSE); + // set default tracker + config.setDefaultTracker("default1234tracker"); - SystemClock.sleep(2000); + // create handler and start the first session + ActivityHandler activityHandler = getActivityHandler(config); - // test init values - checkInitTests( - true, // eventBuffering - null, // defaultTracker - false); // startsSending + SystemClock.sleep(1500); - startActivity(activityHandler); + // test init values + StateActivityHandlerInit initState = new StateActivityHandlerInit(activityHandler); - SystemClock.sleep(2000); + initState.eventBufferingIsEnabled = true; + initState.defaultTracker = "default1234tracker"; - SessionState newSessionState = new SessionState(SessionType.NEW_SESSION); - newSessionState.eventBufferingIsEnabled = true; + StateSession stateSession = new StateSession(StateSession.SessionType.NEW_SESSION); + stateSession.eventBufferingIsEnabled = true; - // test first session start - checkStartInternal(newSessionState); + checkInitAndStart(activityHandler, initState, stateSession); // create the first Event AdjustEvent firstEvent = new AdjustEvent("event1"); @@ -168,92 +148,92 @@ public void testEventsBuffered() { firstEvent.addPartnerParameter("keyPartner", "valuePartner2"); firstEvent.addPartnerParameter("fooPartner", "barPartner"); + // check that callback parameter was overwritten + assertUtil.warn("Key keyCall was overwritten"); + + // check that partner parameter was overwritten + assertUtil.warn("Key keyPartner was overwritten"); + // add revenue firstEvent.setRevenue(0.001, "EUR"); + // set order id + firstEvent.setOrderId("orderIdTest"); + // track event activityHandler.trackEvent(firstEvent); + SystemClock.sleep(1500); + + StateEvent stateEvent1 = new StateEvent(); + stateEvent1.orderId = "orderIdTest"; + stateEvent1.bufferedSuffix = "(0.00100 EUR, 'event1')"; + stateEvent1.activityStateSuffix = "ec:1"; + + checkEvent(stateEvent1); + // create the second Event AdjustEvent secondEvent = new AdjustEvent("event2"); - // add empty revenue - secondEvent.setRevenue(0, "USD"); + // set order id + secondEvent.setOrderId("orderIdTest"); // track second event activityHandler.trackEvent(secondEvent); - // create third Event - AdjustEvent thirdEvent = new AdjustEvent("event3"); - - // track third event - activityHandler.trackEvent(thirdEvent); - - SystemClock.sleep(3000); - - // test first event - // check that callback parameter was overwritten - assertUtil.warn("key keyCall was overwritten"); - - // check that partner parameter was overwritten - assertUtil.warn("key keyPartner was overwritten"); - - // check that event package was added - assertUtil.test("PackageHandler addPackage"); + SystemClock.sleep(1500); - // check that event was buffered - assertUtil.info("Buffered event (0.00100 EUR, 'event1')"); + StateEvent stateEvent2 = new StateEvent(); + stateEvent2.duplicatedOrderId = true; + stateEvent2.orderId = "orderIdTest"; - // and not sent to package handler - assertUtil.notInTest("PackageHandler sendFirstPackage"); + checkEvent(stateEvent2); - // does not fire background timer - assertUtil.notInVerbose("Background timer starting"); + // create third Event + AdjustEvent thirdEvent = new AdjustEvent("event3"); - // after tracking the event it should write the activity state - assertUtil.debug("Wrote Activity state"); + // set order id + thirdEvent.setOrderId("otherOrderId"); - // test second event - // check that event package was added - assertUtil.test("PackageHandler addPackage"); + // add empty revenue + thirdEvent.setRevenue(0, "USD"); - // check that event was buffered - assertUtil.info("Buffered event (0.00000 USD, 'event2')"); + // track third event + activityHandler.trackEvent(thirdEvent); - // and not sent to package handler - assertUtil.notInTest("PackageHandler sendFirstPackage"); + SystemClock.sleep(1500); - // does not fire background timer - assertUtil.notInVerbose("Background timer starting"); + StateEvent stateEvent3 = new StateEvent(); + stateEvent3.orderId = "otherOrderId"; + stateEvent3.bufferedSuffix = "(0.00000 USD, 'event3')"; + stateEvent3.activityStateSuffix = "ec:2"; - // after tracking the event it should write the activity state - assertUtil.debug("Wrote Activity state"); + checkEvent(stateEvent3); - // test third event - // check that event package was added - assertUtil.test("PackageHandler addPackage"); + // create a forth Event object without revenue + AdjustEvent forthEvent = new AdjustEvent("event4"); - // check that event was buffered - assertUtil.info("Buffered event 'event3'"); + // track third event + activityHandler.trackEvent(forthEvent); - // and not sent to package handler - assertUtil.notInTest("PackageHandler sendFirstPackage"); + SystemClock.sleep(1500); - // does not fire background timer - assertUtil.notInVerbose("Background timer starting"); + StateEvent stateEvent4 = new StateEvent(); + stateEvent4.bufferedSuffix = "'event4'"; + stateEvent4.activityStateSuffix = "ec:3"; - // after tracking the event it should write the activity state - assertUtil.debug("Wrote Activity state"); + checkEvent(stateEvent4); // check the number of activity packages // 1 session + 3 events - assertEquals(4, mockPackageHandler.queue.size()); + assertUtil.isEqual(4, mockPackageHandler.queue.size()); ActivityPackage firstSessionPackage = mockPackageHandler.queue.get(0); // create activity package test TestActivityPackage testFirstSessionPackage = new TestActivityPackage(firstSessionPackage); testFirstSessionPackage.eventBufferingEnabled = true; + testFirstSessionPackage.defaultTracker = "default1234tracker"; // set first session testFirstSessionPackage.testSessionPackage(1); @@ -284,13 +264,13 @@ public void testEventsBuffered() { // set event test parameters testSecondEventPackage.eventCount = "2"; - testSecondEventPackage.suffix = "(0.00000 USD, 'event2')"; + testSecondEventPackage.suffix = "(0.00000 USD, 'event3')"; testSecondEventPackage.revenueString = "0.00000"; testSecondEventPackage.currency = "USD"; testSecondEventPackage.eventBufferingEnabled = true; // test second event - testSecondEventPackage.testEventPackage("event2"); + testSecondEventPackage.testEventPackage("event3"); // third event ActivityPackage thirdEventPackage = mockPackageHandler.queue.get(3); @@ -300,63 +280,80 @@ public void testEventsBuffered() { // set event test parameters testThirdEventPackage.eventCount = "3"; - testThirdEventPackage.suffix = "'event3'"; + testThirdEventPackage.suffix = "'event4'"; testThirdEventPackage.eventBufferingEnabled = true; // test third event - testThirdEventPackage.testEventPackage("event3"); + testThirdEventPackage.testEventPackage("event4"); } + @Test public void testEventsNotBuffered() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testEventsNotBuffered"); // create the config to start the session - AdjustConfig config = getConfig(LogLevel.DEBUG); + AdjustConfig config = getConfig(); // start activity handler with config - ActivityHandler activityHandler = getFirstActivityHandler(config, LogLevel.DEBUG); + ActivityHandler activityHandler = startAndCheckFirstSession(config); - SystemClock.sleep(2000); + // create the first Event + AdjustEvent firstEvent = new AdjustEvent("event1"); - // test init values - checkInitTests(); + // track event + activityHandler.trackEvent(firstEvent); - startActivity(activityHandler); + SystemClock.sleep(1500); - SystemClock.sleep(2000); + StateEvent stateEvent = new StateEvent(); + checkEvent(stateEvent); + } - // test session - checkFirstSession(); + @Test + public void testEventBeforeStart() { + // assert test name to read better in logcat + mockLogger.Assert("TestActivityHandler testEventBeforeStart"); + + // create the config to start the session + AdjustConfig config = getConfig(); // create the first Event AdjustEvent firstEvent = new AdjustEvent("event1"); + // create handler and start the first session + ActivityHandler activityHandler = getActivityHandler(config); + // track event activityHandler.trackEvent(firstEvent); - SystemClock.sleep(2000); + SystemClock.sleep(1500); - // check that event package was added - assertUtil.test("PackageHandler addPackage"); + // test init values + checkInitTests(activityHandler); - // check that event was sent to package handler - assertUtil.test("PackageHandler sendFirstPackage"); + // does not start the activity because it was started by the track event - // and not buffered - assertUtil.notInInfo("Buffered event"); + // test session + StateSession stateSession= new StateSession(StateSession.SessionType.NEW_SESSION); + // does not start session + stateSession.startSubsession = false; + stateSession.toSend = false; - // does not fire background timer - assertUtil.notInVerbose("Background timer starting"); + checkStartInternal(stateSession); - // after tracking the event it should write the activity state - assertUtil.debug("Wrote Activity state"); + StateEvent stateEvent = new StateEvent(); + checkEvent(stateEvent); } + @Test public void testChecks() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testChecks"); + // assert test name to read better in logcat + mockLogger.Assert("TestActivityHandler testChecks"); + // config with null app token AdjustConfig nullAppTokenConfig = new AdjustConfig(context, null, AdjustConfig.ENVIRONMENT_SANDBOX); @@ -502,10 +499,10 @@ public int checkCallingOrSelfPermission(String permission) { assertUtil.notInInfo("Skipping initialization in background process"); // create the config to start the session - AdjustConfig config = getConfig(LogLevel.WARN); + AdjustConfig config = getConfig(); // create handler and start the first session - ActivityHandler activityHandler = startAndCheckFirstSession(config, LogLevel.WARN); + ActivityHandler activityHandler = startAndCheckFirstSession(config); // track null event activityHandler.trackEvent(null); @@ -517,36 +514,89 @@ public int checkCallingOrSelfPermission(String permission) { SystemClock.sleep(1000); assertUtil.error("Event not initialized correctly"); + + activityHandler.resetSessionCallbackParameters(); + activityHandler.resetSessionPartnerParameters(); + + activityHandler.removeSessionCallbackParameter(null); + activityHandler.removeSessionCallbackParameter(""); + activityHandler.removeSessionCallbackParameter("nonExistent"); + + activityHandler.removeSessionPartnerParameter(null); + activityHandler.removeSessionPartnerParameter(""); + activityHandler.removeSessionPartnerParameter("nonExistent"); + + activityHandler.addSessionCallbackParameter(null, "value"); + activityHandler.addSessionCallbackParameter("", "value"); + + activityHandler.addSessionCallbackParameter("key", null); + activityHandler.addSessionCallbackParameter("key", ""); + + activityHandler.addSessionPartnerParameter(null, "value"); + activityHandler.addSessionPartnerParameter("", "value"); + + activityHandler.addSessionPartnerParameter("key", null); + activityHandler.addSessionPartnerParameter("key", ""); + + activityHandler.removeSessionCallbackParameter("nonExistent"); + activityHandler.removeSessionPartnerParameter("nonExistent"); + + SystemClock.sleep(1500); + + assertUtil.warn("Session Callback parameters are not set"); + assertUtil.warn("Session Partner parameters are not set"); + + assertUtil.error("Session Callback parameter key is missing"); + assertUtil.error("Session Callback parameter key is empty"); + assertUtil.warn("Session Callback parameters are not set"); + + assertUtil.error("Session Partner parameter key is missing"); + assertUtil.error("Session Partner parameter key is empty"); + assertUtil.warn("Session Partner parameters are not set"); + + assertUtil.error("Session Callback parameter key is missing"); + assertUtil.error("Session Callback parameter key is empty"); + assertUtil.error("Session Callback parameter value is missing"); + assertUtil.error("Session Callback parameter value is empty"); + + assertUtil.error("Session Partner parameter key is missing"); + assertUtil.error("Session Partner parameter key is empty"); + assertUtil.error("Session Partner parameter value is missing"); + assertUtil.error("Session Partner parameter value is empty"); + + assertUtil.warn("Session Callback parameters are not set"); + assertUtil.warn("Session Partner parameters are not set"); } + @Test public void testSessions() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testSessions"); // adjust the session intervals for testing AdjustFactory.setSessionInterval(4000); - AdjustFactory.setSubsessionInterval(1000); // create the config to start the session - AdjustConfig config = getConfig(LogLevel.INFO); + AdjustConfig config = getConfig(); // start activity handler with config - ActivityHandler activityHandler = startAndCheckFirstSession(config, LogLevel.INFO); + ActivityHandler activityHandler = startAndCheckFirstSession(config); // end subsession stopActivity(activityHandler); - SystemClock.sleep(1000); + SystemClock.sleep(2000); // test the end of the subsession checkEndSession(); - startActivity(activityHandler); + // start a subsession + resumeActivity(activityHandler); - SystemClock.sleep(1000); + SystemClock.sleep(2000); // test the new sub session - SessionState secondSubsession = new SessionState(SessionType.NEW_SUBSESSION); + StateSession secondSubsession = new StateSession(StateSession.SessionType.NEW_SUBSESSION); secondSubsession.subsessionCount = 2; checkStartInternal(secondSubsession); @@ -560,30 +610,32 @@ public void testSessions() { // trigger a new session activityHandler.onResume(); - SystemClock.sleep(1000); + SystemClock.sleep(1500); // new session - SessionState secondSession = new SessionState(SessionType.NEW_SESSION); + StateSession secondSession = new StateSession(StateSession.SessionType.NEW_SESSION); secondSession.sessionCount = 2; - secondSession.timerAlreadyStarted = true; checkStartInternal(secondSession); // stop and start the activity with little interval // so it won't trigger a sub session stopActivity(activityHandler); - startActivity(activityHandler); + resumeActivity(activityHandler); - SystemClock.sleep(1000); + SystemClock.sleep(1500); // test the end of the subsession - checkEndSession(false, true, false); + StateEndSession stateEndSession = new StateEndSession(); + stateEndSession.pausing = false; + + checkEndSession(stateEndSession); // test non sub session - SessionState nonSessionState = new SessionState(SessionType.NONSESSION); + StateSession nonSessionState = new StateSession(StateSession.SessionType.NONSESSION); checkStartInternal(nonSessionState); // 2 session packages - assertEquals(2, mockPackageHandler.queue.size()); + assertUtil.isEqual(2, mockPackageHandler.queue.size()); ActivityPackage firstSessionActivityPackage = mockPackageHandler.queue.get(0); @@ -606,84 +658,101 @@ public void testSessions() { testSecondSessionActivityPackage.testSessionPackage(2); } + @Test public void testDisable() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testDisable"); // adjust the session intervals for testing AdjustFactory.setSessionInterval(4000); - AdjustFactory.setSubsessionInterval(1000); // create the config to start the session - AdjustConfig config = getConfig(LogLevel.ERROR); + AdjustConfig config = getConfig(); - // start activity handler with config - ActivityHandler activityHandler = getFirstActivityHandler(config, LogLevel.ERROR); + // create handler and start the first session + ActivityHandler activityHandler = getActivityHandler(config); + + // check that it is enabled + assertUtil.isTrue(activityHandler.isEnabled()); - // disable sdk while it has not started yet + // disable sdk activityHandler.setEnabled(false); // check that it is disabled assertUtil.isFalse(activityHandler.isEnabled()); - SystemClock.sleep(2000); + SystemClock.sleep(1500); // not writing activity state because it set enable does not start the sdk assertUtil.notInDebug("Wrote Activity state"); // check if message the disable of the SDK - assertUtil.info("Package handler and attribution handler will start as paused due to the SDK being disabled"); + assertUtil.info("Handlers will start as paused due to the SDK being disabled"); + + StateActivityHandlerInit stateActivityHandlerInit = new StateActivityHandlerInit(activityHandler); + stateActivityHandlerInit.startsSending = false; + stateActivityHandlerInit.startEnabled = false; - checkInitTests(false); + checkInitTests(stateActivityHandlerInit); checkHandlerStatus(true); // start the sdk // foreground timer does not start because it's paused - startActivity(activityHandler); + resumeActivity(activityHandler); + AdjustEvent firstEvent = new AdjustEvent("event1"); - SystemClock.sleep(2000); + activityHandler.trackEvent(firstEvent); + SystemClock.sleep(1500); - SessionState sessionStartsPaused = new SessionState(SessionType.NEW_SESSION); - sessionStartsPaused.paused = true; + // check initial created session + StateSession sessionStartsPaused = new StateSession(StateSession.SessionType.NEW_SESSION); sessionStartsPaused.toSend = false; sessionStartsPaused.foregroundTimerStarts = false; - sessionStartsPaused.foregroundTimerAlreadyStarted = false; - // check session that is paused checkStartInternal(sessionStartsPaused); + // and failed event + StateEvent stateFailedEvent = new StateEvent(); + stateFailedEvent.disabled = true; + + checkEvent(stateFailedEvent); + + // try to pause session stopActivity(activityHandler); + SystemClock.sleep(1500); - SystemClock.sleep(1000); + StateEndSession stateEndSession = new StateEndSession(); + stateEndSession.checkOnPause = true; + stateEndSession.foregroundAlreadySuspended = true; - // test end session of disable - checkEndSession(true, //pausing - true, // updateActivityState - false); // eventBufferingEnabled + checkEndSession(stateEndSession); + SystemClock.sleep(4000); - SystemClock.sleep(1000); + // try to generate a new session + resumeActivity(activityHandler); - // try to do activities while SDK disabled - activityHandler.onResume(); - activityHandler.trackEvent(new AdjustEvent("event1")); + SystemClock.sleep(1500); - SystemClock.sleep(3000); + StateSession sessionDisabled = new StateSession(StateSession.SessionType.DISABLED); + sessionDisabled.toSend = false; + sessionDisabled.foregroundTimerStarts = false; + sessionDisabled.disabled = true; - checkStartDisable(); + checkStartInternal(sessionDisabled); // only the first session package should be sent - assertEquals(1, mockPackageHandler.queue.size()); + assertUtil.isEqual(1, mockPackageHandler.queue.size()); // put in offline mode activityHandler.setOfflineMode(true); // pausing due to offline mode - assertUtil.info("Pausing package and attribution handler to put SDK offline mode"); + assertUtil.info("Pausing handlers to put SDK offline mode"); // wait to update status - SystemClock.sleep(5000); + SystemClock.sleep(1500); // after pausing, even when it's already paused // tries to update the status @@ -696,45 +765,38 @@ public void testDisable() { assertUtil.isTrue(activityHandler.isEnabled()); // check message of SDK still paused - assertUtil.info("Package and attribution handler remain paused due to SDK being offline"); - - SystemClock.sleep(1000); + assertUtil.info("Handlers remain paused"); - // due to the fact it will remained paused, - // there is no need to try to update the status - checkHandlerStatus(null); + // wait to generate a new session + SystemClock.sleep(5000); - // start the sdk - // foreground timer does not start because it's offline - startActivity(activityHandler); + // even though it will remained paused, + // it will update the status to paused + checkHandlerStatus(true); - SystemClock.sleep(1000); + // generate a new session + resumeActivity(activityHandler); + AdjustEvent secondEvent = new AdjustEvent("event2"); - SessionState SecondPausedSession = new SessionState(SessionType.NEW_SESSION); - SecondPausedSession.toSend = false; - SecondPausedSession.paused = true; - SecondPausedSession.sessionCount = 2; - SecondPausedSession.timerAlreadyStarted = null; - SecondPausedSession.foregroundTimerStarts = false; - sessionStartsPaused.foregroundTimerAlreadyStarted = false; - checkStartInternal(SecondPausedSession); + activityHandler.trackEvent(secondEvent); - // track an event - activityHandler.trackEvent(new AdjustEvent("event1")); + SystemClock.sleep(1500); - SystemClock.sleep(5000); + // difference from the first session is that now the foreground timer starts + StateSession sessionOffline = new StateSession(StateSession.SessionType.NEW_SESSION); + sessionOffline.toSend = false; + sessionOffline.foregroundTimerStarts = true; + sessionOffline.foregroundTimerAlreadyStarted = false; - // check that it did add the event package - assertUtil.test("PackageHandler addPackage"); + checkStartInternal(sessionOffline); - // and send it - assertUtil.test("PackageHandler sendFirstPackage"); + // and the event does not fail + StateEvent stateEvent = new StateEvent(); - // does not fire background timer - assertUtil.notInVerbose("Background timer starting"); + checkEvent(stateEvent); // it should have the second session and the event - assertEquals(3, mockPackageHandler.queue.size()); + assertUtil.isEqual(3, mockPackageHandler.queue.size()); ActivityPackage secondSessionPackage = mockPackageHandler.queue.get(1); @@ -752,15 +814,15 @@ public void testDisable() { // create activity package test TestActivityPackage testEventPackage = new TestActivityPackage(eventPackage); - testEventPackage.suffix = "'event1'"; + testEventPackage.suffix = "'event2'"; // test event - testEventPackage.testEventPackage("event1"); + testEventPackage.testEventPackage("event2"); // end the session stopActivity(activityHandler); - SystemClock.sleep(1000); + SystemClock.sleep(1500); checkEndSession(); @@ -768,34 +830,34 @@ public void testDisable() { activityHandler.setOfflineMode(false); // message that is finally resuming - assertUtil.info("Resuming package handler and attribution handler to put SDK in online mode"); + assertUtil.info("Resuming handlers to put SDK in online mode"); - SystemClock.sleep(1000); + SystemClock.sleep(5000); // after un-pausing the sdk, tries to update the handlers // it is still paused because it's on the background checkHandlerStatus(true); - startActivity(activityHandler); + resumeActivity(activityHandler); - SystemClock.sleep(1000); + SystemClock.sleep(1500); - SessionState ThirdSessionStarting = new SessionState(SessionType.NEW_SESSION); - ThirdSessionStarting.sessionCount = 3; - ThirdSessionStarting.eventCount = 1; - ThirdSessionStarting.timerAlreadyStarted = false; - checkStartInternal(ThirdSessionStarting); + StateSession thirdSessionStarting = new StateSession(StateSession.SessionType.NEW_SESSION); + thirdSessionStarting.sessionCount = 3; + thirdSessionStarting.eventCount = 1; + checkStartInternal(thirdSessionStarting); } + @Test public void testOpenUrl() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testOpenUrl"); // create the config to start the session - AdjustConfig config = getConfig(LogLevel.ASSERT); + AdjustConfig config = getConfig(); // start activity handler with config - ActivityHandler activityHandler = startAndCheckFirstSession(config, LogLevel.ASSERT); + ActivityHandler activityHandler = startAndCheckFirstSession(config); Uri attributions = Uri.parse("AdjustTests://example.com/path/inApp?adjust_tracker=trackerValue&other=stuff&adjust_campaign=campaignValue&adjust_adgroup=adgroupValue&adjust_creative=creativeValue"); Uri extraParams = Uri.parse("AdjustTests://example.com/path/inApp?adjust_foo=bar&other=stuff&adjust_key=value"); @@ -829,7 +891,7 @@ public void testOpenUrl() { assertUtil.notInTest("SdkClickHandler sendSdkClick"); // 7 clicks - assertEquals(7, mockSdkClickHandler.queue.size()); + assertUtil.isEqual(7, mockSdkClickHandler.queue.size()); // get the click package ActivityPackage attributionClickPackage = mockSdkClickHandler.queue.get(0); @@ -930,12 +992,13 @@ public void testOpenUrl() { testIncompleteClickPackage.testClickPackage("deeplink"); } + @Test public void testAttributionDelegate() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testAttributionDelegate"); // create the config to start the session - AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX); + AdjustConfig config = getConfig(); config.setOnAttributionChangedListener(new OnAttributionChangedListener() { @Override @@ -944,17 +1007,18 @@ public void onAttributionChanged(AdjustAttribution attribution) { } }); - DelegatesPresent attributionDelegatePresent = new DelegatesPresent(); + StateDelegates attributionDelegatePresent = new StateDelegates(); attributionDelegatePresent.attributionDelegatePresent = true; checkFinishTasks(config, attributionDelegatePresent); } + @Test public void testSuccessDelegates() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testSuccessDelegates"); // create the config to start the session - AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX); + AdjustConfig config = getConfig(); config.setOnEventTrackingSucceededListener(new OnEventTrackingSucceededListener() { @Override @@ -970,19 +1034,20 @@ public void onFinishedSessionTrackingSucceeded(AdjustSessionSuccess sessionSucce } }); - DelegatesPresent successDelegatesPresent = new DelegatesPresent(); + StateDelegates successDelegatesPresent = new StateDelegates(); successDelegatesPresent.eventSuccessDelegatePresent = true; successDelegatesPresent.sessionSuccessDelegatePresent = true; checkFinishTasks(config, successDelegatesPresent); } + @Test public void testFailureDelegates() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testFailureDelegates"); // create the config to start the session - AdjustConfig config = new AdjustConfig(context, "123456789012", AdjustConfig.ENVIRONMENT_SANDBOX); + AdjustConfig config = getConfig(); config.setOnEventTrackingFailedListener(new OnEventTrackingFailedListener() { @Override @@ -998,121 +1063,240 @@ public void onFinishedSessionTrackingFailed(AdjustSessionFailure failureResponse } }); - DelegatesPresent failureDelegatesPresent = new DelegatesPresent(); + StateDelegates failureDelegatesPresent = new StateDelegates(); failureDelegatesPresent.sessionFailureDelegatePresent = true; failureDelegatesPresent.eventFailureDelegatePresent = true; checkFinishTasks(config, failureDelegatesPresent); } - public void testLaunchDeepLink() { - // assert test name to read better in logcat - mockLogger.Assert("TestActivityHandler testLaunchDeepLink"); + private void checkFinishTasks(AdjustConfig config, + StateDelegates stateDelegates) + { + ActivityHandler activityHandler = startAndCheckFirstSession(config); - // create the config to start the session - AdjustConfig config = getConfig(LogLevel.VERBOSE, AdjustConfig.ENVIRONMENT_PRODUCTION, "123456789012", context); + // test first session package + ActivityPackage firstSessionPackage = mockPackageHandler.queue.get(0); - // start activity handler with config - ActivityHandler activityHandler = getActivityHandler(config, - true, // startEnabled, - null, // readActivityState, - null, // readAttribution, - true, // isProductionEnvironment, - LogLevel.VERBOSE); // logLevel + // create activity package test + TestActivityPackage testActivityPackage = new TestActivityPackage(firstSessionPackage); - SystemClock.sleep(2000); + testActivityPackage.needsResponseDetails = + stateDelegates.attributionDelegatePresent || + stateDelegates.eventSuccessDelegatePresent || + stateDelegates.eventFailureDelegatePresent || + stateDelegates.sessionSuccessDelegatePresent || + stateDelegates.sessionFailureDelegatePresent; - checkInitTests( - false, // eventBuffering - null, // defaultTracker - false); // startsSending + // set first session + testActivityPackage.testSessionPackage(1); - startActivity(activityHandler); + // simulate a successful session + SessionResponseData successSessionResponseData = (SessionResponseData) ResponseData.buildResponseData(firstSessionPackage); + successSessionResponseData.success = true; - SystemClock.sleep(2000); + activityHandler.finishedTrackingActivity(successSessionResponseData); + SystemClock.sleep(1000); - // test first session start - checkFirstSession(); + // attribution handler should always receive the session response + assertUtil.test("AttributionHandler checkSessionResponse"); + // the first session does not trigger the event response delegate - ResponseData responseDataNull = null; + assertUtil.notInDebug("Launching success event tracking listener"); + assertUtil.notInDebug("Launching failed event tracking listener"); - //activityHandler.finishedTrackingActivity(responseDataNull); + activityHandler.launchSessionResponseTasks(successSessionResponseData); SystemClock.sleep(1000); - // if the response is null - assertUtil.notInTest("AttributionHandler checkAttribution"); - assertUtil.notInError("Unable to open deep link"); - assertUtil.notInInfo("Open deep link"); - - // set package handler to respond with a valid attribution - ResponseData wrongDeeplinkResponseData = ResponseData.buildResponseData(mockPackageHandler.queue.get(0)); - try { - wrongDeeplinkResponseData.jsonResponse = new JSONObject("{ " + - "\"deeplink\" : \"wrongDeeplink://\" }"); - } catch (JSONException e) { - fail(e.getMessage()); + // if present, the first session triggers the success session delegate + if (stateDelegates.sessionSuccessDelegatePresent) { + assertUtil.debug("Launching success session tracking listener"); + } else { + assertUtil.notInDebug("Launching success session tracking delegate"); } + // it doesn't trigger the failure session delegate + assertUtil.notInDebug("Launching failed session tracking listener"); - activityHandler.launchSessionResponseTasks((SessionResponseData) wrongDeeplinkResponseData); - SystemClock.sleep(2000); + // simulate a failure session + SessionResponseData failureSessionResponseData = (SessionResponseData)ResponseData.buildResponseData(firstSessionPackage); + failureSessionResponseData.success = false; - // check that it was unable to open the url - assertUtil.error("Unable to open deep link (wrongDeeplink://)"); + activityHandler.launchSessionResponseTasks(failureSessionResponseData); + SystemClock.sleep(1000); - // TODO add test that opens url + // it doesn't trigger the success session delegate + assertUtil.notInDebug("Launching success session tracking listener"); - // checking the default values of the first session package - // should only have one package - assertEquals(1, mockPackageHandler.queue.size()); + // if present, the first session triggers the failure session delegate + if (stateDelegates.sessionFailureDelegatePresent) { + assertUtil.debug("Launching failed session tracking listener"); + } else { + assertUtil.notInDebug("Launching failed session tracking listener"); + } - ActivityPackage activityPackage = mockPackageHandler.queue.get(0); + // test success event response data + activityHandler.trackEvent(new AdjustEvent("abc123")); + SystemClock.sleep(1000); - // create activity package test - TestActivityPackage testActivityPackage = new TestActivityPackage(activityPackage); + ActivityPackage eventPackage = mockPackageHandler.queue.get(1); + EventResponseData eventSuccessResponseData = (EventResponseData)ResponseData.buildResponseData(eventPackage); + eventSuccessResponseData.success = true; - testActivityPackage.environment = AdjustConfig.ENVIRONMENT_PRODUCTION; + activityHandler.finishedTrackingActivity(eventSuccessResponseData); + SystemClock.sleep(1000); - // set first session - testActivityPackage.testSessionPackage(1); - } + // attribution handler should never receive the event response + assertUtil.notInTest("AttributionHandler checkSessionResponse"); - public void testNotLaunchDeeplinkCallback() { - // assert test name to read better in logcat - mockLogger.Assert("TestActivityHandler testNotLaunchDeeplinkCallback"); + // if present, the success event triggers the success event delegate + if (stateDelegates.eventSuccessDelegatePresent) { + assertUtil.debug("Launching success event tracking listener"); + } else { + assertUtil.notInDebug("Launching success event tracking listener"); + } + // it doesn't trigger the failure event delegate + assertUtil.notInDebug("Launching failed event tracking listener"); - // create the config to start the session - AdjustConfig config = getConfig(); + // test failure event response data + EventResponseData eventFailureResponseData = (EventResponseData)ResponseData.buildResponseData(eventPackage); + eventFailureResponseData.success = false; - config.setOnDeeplinkResponseListener(new OnDeeplinkResponseListener() { - @Override - public boolean launchReceivedDeeplink(Uri deeplink) { - mockLogger.test("launchReceivedDeeplink, " + deeplink); - return false; - } - }); + activityHandler.finishedTrackingActivity(eventFailureResponseData); + SystemClock.sleep(1000); + + // attribution handler should never receive the event response + assertUtil.notInTest("AttributionHandler checkSessionResponse"); + + // if present, the failure event triggers the failure event delegate + if (stateDelegates.eventFailureDelegatePresent) { + assertUtil.debug("Launching failed event tracking listener"); + } else { + assertUtil.notInDebug("Launching failed event tracking listener"); + } + // it doesn't trigger the success event delegate + assertUtil.notInDebug("Launching success event tracking listener"); + + // test click + Uri attributions = Uri.parse("AdjustTests://example.com/path/inApp?adjust_tracker=trackerValue&other=stuff&adjust_campaign=campaignValue&adjust_adgroup=adgroupValue&adjust_creative=creativeValue"); + long now = System.currentTimeMillis(); + + activityHandler.readOpenUrl(attributions, now); + SystemClock.sleep(1000); + + assertUtil.test("SdkClickHandler sendSdkClick"); + + // test sdk_click response data + ActivityPackage sdkClickPackage = mockSdkClickHandler.queue.get(0); + ClickResponseData clickResponseData = (ClickResponseData)ResponseData.buildResponseData(sdkClickPackage); + + activityHandler.finishedTrackingActivity(clickResponseData); + SystemClock.sleep(1000); + + // attribution handler should never receive the click response + assertUtil.notInTest("AttributionHandler checkSessionResponse"); + // it doesn't trigger the any event delegate + assertUtil.notInDebug("Launching success event tracking listener"); + assertUtil.notInDebug("Launching failed event tracking listener"); + } + + @Test + public void testLaunchDeepLink() { + // assert test name to read better in logcat + mockLogger.Assert("TestActivityHandler testLaunchDeepLink"); + + // create the config to start the session + AdjustConfig config = getConfig(); // start activity handler with config ActivityHandler activityHandler = startAndCheckFirstSession(config); - // set package handler to respond with a valid attribution - ResponseData wrongDeeplinkResponseData = ResponseData.buildResponseData(mockPackageHandler.queue.get(0)); + ResponseData responseDataNull = null; + + activityHandler.finishedTrackingActivity(responseDataNull); + SystemClock.sleep(1500); + + // if the response is null + assertUtil.notInTest("AttributionHandler checkAttribution"); + assertUtil.notInError("Unable to open deferred deep link"); + assertUtil.notInInfo("Open deferred deep link"); + + // test success session response data + SessionResponseData sessionResponseDeeplink = (SessionResponseData) ResponseData.buildResponseData(mockPackageHandler.queue.get(0)); try { - wrongDeeplinkResponseData.jsonResponse = new JSONObject("{ " + - "\"deeplink\" : \"wrongDeeplink://\" }"); + sessionResponseDeeplink.jsonResponse = new JSONObject("{ " + + "\"deeplink\" : \"adjustTestSchema://\" }"); } catch (JSONException e) { - fail(e.getMessage()); + assertUtil.fail(e.getMessage()); } - activityHandler.launchSessionResponseTasks((SessionResponseData) wrongDeeplinkResponseData); - SystemClock.sleep(2000); + activityHandler.launchSessionResponseTasks((SessionResponseData) sessionResponseDeeplink); + SystemClock.sleep(1500); + + // check that it was unable to open the url + assertUtil.notInError("Unable to open deferred deep link"); + assertUtil.notInInfo("Open deferred deep link"); + + // test attribution response + AttributionResponseData attributionResponseDeeplink = (AttributionResponseData)ResponseData.buildResponseData(mockAttributionHandler.attributionPackage); + attributionResponseDeeplink.deeplink = Uri.parse("adjustTestSchema://"); + + activityHandler.launchAttributionResponseTasks(attributionResponseDeeplink); + SystemClock.sleep(1500); + + assertUtil.info("Deferred deeplink received (adjustTestSchema://)"); + assertUtil.notInError("Unable to open deferred deep link (adjustTestSchema://)"); + assertUtil.info("Open deferred deep link (adjustTestSchema://)"); + + // checking the default values of the first session package + // should only have one package + assertUtil.isEqual(1, mockPackageHandler.queue.size()); + + AttributionResponseData attributionResponseWrongDeeplink = (AttributionResponseData)ResponseData.buildResponseData(mockAttributionHandler.attributionPackage); + attributionResponseWrongDeeplink.deeplink = Uri.parse("wrongDeeplink://"); + + activityHandler.launchAttributionResponseTasks(attributionResponseWrongDeeplink); + SystemClock.sleep(1500); + + assertUtil.info("Deferred deeplink received (wrongDeeplink://)"); + assertUtil.error("Unable to open deferred deep link (wrongDeeplink://)"); + assertUtil.notInInfo("Open deferred deep link (wrongDeeplink://)"); + } + + @Test + public void testNotLaunchDeeplinkCallback() { + // assert test name to read better in logcat + mockLogger.Assert("TestActivityHandler testNotLaunchDeeplinkCallback"); + + // create the config to start the session + AdjustConfig config = getConfig(); + + config.setOnDeeplinkResponseListener(new OnDeeplinkResponseListener() { + @Override + public boolean launchReceivedDeeplink(Uri deeplink) { + mockLogger.test("launchReceivedDeeplink, " + deeplink); + return false; + } + }); + + // start activity handler with config + ActivityHandler activityHandler = startAndCheckFirstSession(config); - // callback called - assertUtil.test("launchReceivedDeeplink, wrongDeeplink://"); + // test attribution response + AttributionResponseData attributionResponseDeeplink = (AttributionResponseData)ResponseData.buildResponseData(mockAttributionHandler.attributionPackage); + attributionResponseDeeplink.deeplink = Uri.parse("adjustTestSchema://"); - // but deeplink not launched - assertUtil.notInError("Unable to open deep link"); + activityHandler.launchAttributionResponseTasks(attributionResponseDeeplink); + SystemClock.sleep(1500); + + // received the deferred deeplink + assertUtil.info("Deferred deeplink received (adjustTestSchema://)"); + // but it did not try to launch it + assertUtil.test("launchReceivedDeeplink, adjustTestSchema://"); + assertUtil.notInError("Unable to open deferred deep link (adjustTestSchema://)"); + assertUtil.notInInfo("Open deferred deep link (adjustTestSchema://)"); } + @Test public void testDeeplinkCallback() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testDeeplinkCallback"); @@ -1132,24 +1316,21 @@ public boolean launchReceivedDeeplink(Uri deeplink) { ActivityHandler activityHandler = startAndCheckFirstSession(config); // set package handler to respond with a valid attribution - ResponseData wrongDeeplinkResponseData = ResponseData.buildResponseData(mockPackageHandler.queue.get(0)); - try { - wrongDeeplinkResponseData.jsonResponse = new JSONObject("{ " + - "\"deeplink\" : \"wrongDeeplink://\" }"); - } catch (JSONException e) { - fail(e.getMessage()); - } + AttributionResponseData attributionResponseDeeplink = (AttributionResponseData)ResponseData.buildResponseData(mockAttributionHandler.attributionPackage); + attributionResponseDeeplink.deeplink = Uri.parse("adjustTestSchema://"); - activityHandler.launchSessionResponseTasks((SessionResponseData) wrongDeeplinkResponseData); + activityHandler.launchAttributionResponseTasks(attributionResponseDeeplink); SystemClock.sleep(2000); - // callback called - assertUtil.test("launchReceivedDeeplink, wrongDeeplink://"); - - // but deeplink not launched - assertUtil.error("Unable to open deep link"); + // received the deferred deeplink + assertUtil.info("Deferred deeplink received (adjustTestSchema://)"); + // and it did launch it + assertUtil.test("launchReceivedDeeplink, adjustTestSchema://"); + assertUtil.notInError("Unable to open deferred deep link (adjustTestSchema://)"); + assertUtil.info("Open deferred deep link (adjustTestSchema://)"); } + @Test public void testUpdateAttribution() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testUpdateAttribution"); @@ -1167,19 +1348,19 @@ public void testUpdateAttribution() { assertUtil.isNull(nullAttribution); // check that it does not update a null attribution - assertUtil.isFalse(firstActivityHandler.updateAttribution(nullAttribution)); + assertUtil.isFalse(firstActivityHandler.updateAttributionI(nullAttribution)); // create an empty attribution JSONObject emptyJsonResponse = null; try { emptyJsonResponse = new JSONObject("{ }"); } catch (JSONException e) { - fail(e.getMessage()); + assertUtil.fail(e.getMessage()); } AdjustAttribution emptyAttribution = AdjustAttribution.fromJson(emptyJsonResponse); // check that updates attribution - assertUtil.isTrue(firstActivityHandler.updateAttribution(emptyAttribution)); + assertUtil.isTrue(firstActivityHandler.updateAttributionI(emptyAttribution)); assertUtil.debug("Wrote Attribution: tt:null tn:null net:null cam:null adg:null cre:null cl:null"); emptyAttribution = AdjustAttribution.fromJson(emptyJsonResponse); @@ -1212,29 +1393,29 @@ public void onAttributionChanged(AdjustAttribution attribution) { } }); - ActivityHandler restartActivityHandler = getActivityHandler (config, - true, // startEnabled - "ec:0 sc:1 ssc:1", // readActivityState - "tt:null tn:null net:null cam:null adg:null cre:null cl:null", // readAttribution - false, // isProductionEnvironment - LogLevel.INFO); // loglevel + ActivityHandler restartActivityHandler = getActivityHandler(config); - SystemClock.sleep(2000); + SystemClock.sleep(1500); + + StateActivityHandlerInit restartActivityHandlerInit = new StateActivityHandlerInit(restartActivityHandler); + restartActivityHandlerInit.activityStateAlreadyCreated = true; + restartActivityHandlerInit.readActivityState = "ec:0 sc:1 ssc:1"; + restartActivityHandlerInit.readAttribution = "tt:null tn:null net:null cam:null adg:null cre:null cl:null"; // test init values - checkInitTests(); + checkInitTests(restartActivityHandlerInit); - startActivity(restartActivityHandler); + resumeActivity(restartActivityHandler); SystemClock.sleep(2000); - SessionState firstRestart = new SessionState(SessionType.NEW_SUBSESSION); + StateSession firstRestart = new StateSession(StateSession.SessionType.NEW_SUBSESSION); firstRestart.subsessionCount = 2; - firstRestart.timerAlreadyStarted = false; + firstRestart.foregroundTimerAlreadyStarted = false; checkStartInternal(firstRestart); // check that it does not update the attribution after the restart - assertUtil.isFalse(restartActivityHandler.updateAttribution(emptyAttribution)); + assertUtil.isFalse(restartActivityHandler.updateAttributionI(emptyAttribution)); assertUtil.notInDebug("Wrote Attribution"); // new attribution @@ -1249,7 +1430,7 @@ public void onAttributionChanged(AdjustAttribution attribution) { "\"creative\" : \"ctValue\" , " + "\"click_label\" : \"clValue\" }"); } catch (JSONException e) { - fail(e.getMessage()); + assertUtil.fail(e.getMessage()); } AdjustAttribution firstAttribution = AdjustAttribution.fromJson(firstAttributionJson); @@ -1288,30 +1469,32 @@ public void onAttributionChanged(AdjustAttribution attribution) { } }); - ActivityHandler secondRestartActivityHandler = getActivityHandler(config, - true, // startEnabled - "ec:0 sc:1 ssc:2", // readActivityState - "tt:ttValue tn:tnValue net:nValue cam:cpValue adg:aValue cre:ctValue cl:clValue", // readAttribution - false, // isProductionEnvironment - LogLevel.INFO); // loglevel + ActivityHandler secondRestartActivityHandler = getActivityHandler(config);; + + SystemClock.sleep(1500); + + StateActivityHandlerInit secondrestartActivityHandlerInit = new StateActivityHandlerInit(secondRestartActivityHandler); + secondrestartActivityHandlerInit.activityStateAlreadyCreated = true; + secondrestartActivityHandlerInit.readActivityState = "ec:0 sc:1 ssc:2"; + secondrestartActivityHandlerInit.readAttribution = "tt:ttValue tn:tnValue net:nValue cam:cpValue adg:aValue cre:ctValue cl:clValue"; SystemClock.sleep(2000); // test init values - checkInitTests(); + checkInitTests(secondrestartActivityHandlerInit); - startActivity(secondRestartActivityHandler); + resumeActivity(secondRestartActivityHandler); SystemClock.sleep(2000); - SessionState secondRestart = new SessionState(SessionType.NEW_SUBSESSION); + StateSession secondRestart = new StateSession(StateSession.SessionType.NEW_SUBSESSION); secondRestart.subsessionCount = 3; - secondRestart.timerAlreadyStarted = false; + secondRestart.foregroundTimerAlreadyStarted = false; checkStartInternal(secondRestart); // check that it does not update the attribution after the restart - assertUtil.isFalse(secondRestartActivityHandler.updateAttribution(firstAttribution)); + assertUtil.isFalse(secondRestartActivityHandler.updateAttributionI(firstAttribution)); assertUtil.notInDebug("Wrote Attribution"); // new attribution @@ -1326,7 +1509,7 @@ public void onAttributionChanged(AdjustAttribution attribution) { "\"creative\" : \"ctValue2\" , " + "\"click_label\" : \"clValue2\" }"); } catch (JSONException e) { - fail(e.getMessage()); + assertUtil.fail(e.getMessage()); } AdjustAttribution secondAttribution = AdjustAttribution.fromJson(secondAttributionJson); @@ -1341,10 +1524,11 @@ public void onAttributionChanged(AdjustAttribution attribution) { assertUtil.test("secondRestartActivityHandler onAttributionChanged: tt:ttValue2 tn:tnValue2 net:nValue2 cam:cpValue2 adg:aValue2 cre:ctValue2 cl:clValue2"); // check that it does not update the attribution - assertUtil.isFalse(secondRestartActivityHandler.updateAttribution(secondAttribution)); + assertUtil.isFalse(secondRestartActivityHandler.updateAttributionI(secondAttribution)); assertUtil.notInDebug("Wrote Attribution"); } + @Test public void testOfflineMode() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testOfflineMode"); @@ -1357,55 +1541,54 @@ public void testOfflineMode() { AdjustConfig config = getConfig(); // start activity handler with config - ActivityHandler activityHandler = getFirstActivityHandler(config); + ActivityHandler activityHandler = getActivityHandler(config); // put SDK offline activityHandler.setOfflineMode(true); - InternalState internalState = activityHandler.getInternalState(); + ActivityHandler.InternalState internalState = activityHandler.getInternalState(); // check if it's offline before the sdk starts assertUtil.isTrue(internalState.isOffline()); - SystemClock.sleep(2000); + SystemClock.sleep(1500); // not writing activity state because it set enable does not start the sdk assertUtil.notInDebug("Wrote Activity state"); // check if message the disable of the SDK - assertUtil.info("Package handler and attribution handler will start paused due to SDK being offline"); + assertUtil.info("Handlers will start paused due to SDK being offline"); // test init values - checkInitTests(false); + checkInitTests(activityHandler); + // check change from set offline mode checkHandlerStatus(true); // start the sdk - // foreground timer does not start because it's paused - startActivity(activityHandler); + // foreground timer starts because it's offline, not disabled + resumeActivity(activityHandler); - SystemClock.sleep(2000); + SystemClock.sleep(2500); // test first session start - SessionState firstSessionStartPaused = new SessionState(SessionType.NEW_SESSION); - firstSessionStartPaused.paused = true; + StateSession firstSessionStartPaused = new StateSession(StateSession.SessionType.NEW_SESSION); firstSessionStartPaused.toSend = false; - firstSessionStartPaused.foregroundTimerStarts = false; - firstSessionStartPaused.foregroundTimerAlreadyStarted = false; + firstSessionStartPaused.foregroundTimerStarts = true; // check session that is paused checkStartInternal(firstSessionStartPaused); stopActivity(activityHandler); + SystemClock.sleep(1500); - SystemClock.sleep(1000); - - // test end session of disable - checkEndSession(true, //pausing - false, // updateActivityState - false); // eventBufferingEnabled + // test end session of offline + StateEndSession stateOfflineEndSession = new StateEndSession(); + stateOfflineEndSession.checkOnPause = true; + stateOfflineEndSession.updateActivityState = false; // update too late on the session + checkEndSession(stateOfflineEndSession); - // disable the SDK + // disable the SDK in the background activityHandler.setEnabled(false); // check that it is disabled @@ -1415,43 +1598,60 @@ public void testOfflineMode() { assertUtil.debug("Wrote Activity state: ec:0 sc:1 ssc:1"); // check if message the disable of the SDK - assertUtil.info("Pausing package handler and attribution handler due to SDK being disabled"); + assertUtil.info("Pausing handlers due to SDK being disabled"); - SystemClock.sleep(1000); + // start a session while offline and disabled + SystemClock.sleep(2500); + // check handler status update of disable checkHandlerStatus(true); + // try to start new session disabled + resumeActivity(activityHandler); + SystemClock.sleep(1500); + + // session not created for being disabled + // foreground timer does not start because it's offline, not disabled + StateSession sessionDisabled = new StateSession(StateSession.SessionType.DISABLED); + sessionDisabled.toSend = false; + sessionDisabled.foregroundTimerStarts = false; + sessionDisabled.disabled = true; + + checkStartInternal(sessionDisabled); + // put SDK back online activityHandler.setOfflineMode(false); - assertUtil.info("Package and attribution handler remain paused due to SDK being disabled"); + assertUtil.info("Handlers remain paused"); - SystemClock.sleep(1000); + SystemClock.sleep(1500); // test the update status, still paused - checkHandlerStatus(null); + checkHandlerStatus(true); // try to do activities while SDK disabled - activityHandler.onResume(); + resumeActivity(activityHandler); activityHandler.trackEvent(new AdjustEvent("event1")); - SystemClock.sleep(3000); + SystemClock.sleep(2500); // check that timer was not executed checkForegroundTimerFired(false); - // check that it did not wrote activity state from new session or subsession - assertUtil.notInDebug("Wrote Activity state"); - - // check that it did not add any package - assertUtil.notInTest("PackageHandler addPackage"); + // session not created for being disabled + // foreground timer does not start because it's offline, not disabled + checkStartInternal(sessionDisabled); // end the session stopActivity(activityHandler); SystemClock.sleep(1000); - checkEndSession(false); + StateEndSession stateDisableEndSession = new StateEndSession(); + stateDisableEndSession.checkOnPause = true; + stateDisableEndSession.foregroundAlreadySuspended = true; // did not start timer disabled + stateDisableEndSession.updateActivityState = false; // update too late on the session + checkEndSession(stateDisableEndSession); // enable the SDK again activityHandler.setEnabled(true); @@ -1461,23 +1661,24 @@ public void testOfflineMode() { assertUtil.debug("Wrote Activity state"); - assertUtil.info("Resuming package handler and attribution handler due to SDK being enabled"); + assertUtil.info("Resuming handlers due to SDK being enabled"); - SystemClock.sleep(1000); + SystemClock.sleep(2500); // it is still paused because it's on the background checkHandlerStatus(true); - startActivity(activityHandler); + resumeActivity(activityHandler); - SystemClock.sleep(1000); + SystemClock.sleep(1500); - SessionState secondSessionState = new SessionState(SessionType.NEW_SESSION); + StateSession secondSessionState = new StateSession(StateSession.SessionType.NEW_SESSION); secondSessionState.sessionCount = 2; // test that is not paused anymore checkStartInternal(secondSessionState); } + @Test public void testSendReferrer() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testSendReferrer"); @@ -1485,10 +1686,28 @@ public void testSendReferrer() { // create the config to start the session AdjustConfig config = getConfig(); + long now = System.currentTimeMillis(); + + String referrerBeforeLaunch = "referrerBeforeLaunch"; + + config.referrer = referrerBeforeLaunch; + config.referrerClickTime = now; // start activity handler with config - ActivityHandler activityHandler = startAndCheckFirstSession(config); + ActivityHandler activityHandler = getActivityHandler(config); - long now = System.currentTimeMillis(); + SystemClock.sleep(1500); + + // test init values + StateActivityHandlerInit stateActivityHandlerInit = new StateActivityHandlerInit(activityHandler); + stateActivityHandlerInit.sendReferrer = referrerBeforeLaunch; + checkInitTests(stateActivityHandlerInit); + + resumeActivity(activityHandler); + + SystemClock.sleep(1500); + + // test session + checkFirstSession(); String reftag = "adjust_reftag=referrerValue"; String extraParams = "adjust_foo=bar&other=stuff&adjust_key=value"; @@ -1500,34 +1719,48 @@ public void testSendReferrer() { String incomplete = "adjust_foo="; activityHandler.sendReferrer(reftag, now); - SystemClock.sleep(1000); activityHandler.sendReferrer(extraParams, now); - SystemClock.sleep(1000); activityHandler.sendReferrer(mixed, now); - SystemClock.sleep(1000); activityHandler.sendReferrer(empty, now); - SystemClock.sleep(1000); activityHandler.sendReferrer(nullString, now); - SystemClock.sleep(1000); activityHandler.sendReferrer(single, now); - SystemClock.sleep(1000); activityHandler.sendReferrer(prefix, now); - SystemClock.sleep(1000); activityHandler.sendReferrer(incomplete, now); - SystemClock.sleep(1000); + SystemClock.sleep(2000); - // three click packages: reftag, extraParams, mixed, empty, single, prefix and incomplete - for (int i = 6; i > 0; i--) { - assertUtil.test("SdkClickHandler sendSdkClick"); - } + assertUtil.verbose("Reading query string (%s)", reftag); + assertUtil.test("SdkClickHandler sendSdkClick"); + + assertUtil.verbose("Reading query string (%s)", extraParams); + assertUtil.test("SdkClickHandler sendSdkClick"); + + assertUtil.verbose("Reading query string (%s)", mixed); + assertUtil.test("SdkClickHandler sendSdkClick"); + + assertUtil.verbose("Reading query string (%s)", single); + assertUtil.test("SdkClickHandler sendSdkClick"); + + assertUtil.verbose("Reading query string (%s)", prefix); + assertUtil.test("SdkClickHandler sendSdkClick"); + + assertUtil.verbose("Reading query string (%s)", incomplete); + assertUtil.test("SdkClickHandler sendSdkClick"); // check that it did not send any other click package assertUtil.notInTest("SdkClickHandler sendSdkClick"); - // 6 click - assertEquals(6, mockSdkClickHandler.queue.size()); + // 7 click + assertUtil.isEqual(7, mockSdkClickHandler.queue.size()); + + ActivityPackage referrerBeforeLaunchPacakge = mockSdkClickHandler.queue.get(0); + + TestActivityPackage referrerBeforeLaunchTest = new TestActivityPackage(referrerBeforeLaunchPacakge); - ActivityPackage reftagClickPackage = mockSdkClickHandler.queue.get(0); + referrerBeforeLaunchTest.referrer = referrerBeforeLaunch; + + referrerBeforeLaunchTest.testClickPackage("reftag"); + + ActivityPackage reftagClickPackage = mockSdkClickHandler.queue.get(1); TestActivityPackage reftagClickPackageTest = new TestActivityPackage(reftagClickPackage); @@ -1537,7 +1770,7 @@ public void testSendReferrer() { reftagClickPackageTest.testClickPackage("reftag"); // get the click package - ActivityPackage extraParamsClickPackage = mockSdkClickHandler.queue.get(1); + ActivityPackage extraParamsClickPackage = mockSdkClickHandler.queue.get(2); // create activity package test TestActivityPackage testExtraParamsClickPackage = new TestActivityPackage(extraParamsClickPackage); @@ -1551,7 +1784,7 @@ public void testSendReferrer() { testExtraParamsClickPackage.testClickPackage("reftag"); // get the click package - ActivityPackage mixedClickPackage = mockSdkClickHandler.queue.get(2); + ActivityPackage mixedClickPackage = mockSdkClickHandler.queue.get(3); // create activity package test TestActivityPackage testMixedClickPackage = new TestActivityPackage(mixedClickPackage); @@ -1566,7 +1799,7 @@ public void testSendReferrer() { testMixedClickPackage.testClickPackage("reftag"); // get the click package - ActivityPackage singleClickPackage = mockSdkClickHandler.queue.get(3); + ActivityPackage singleClickPackage = mockSdkClickHandler.queue.get(4); // create activity package test TestActivityPackage testSingleClickPackage = new TestActivityPackage(singleClickPackage); @@ -1576,7 +1809,7 @@ public void testSendReferrer() { testSingleClickPackage.testClickPackage("reftag"); // get the click package - ActivityPackage prefixClickPackage = mockSdkClickHandler.queue.get(4); + ActivityPackage prefixClickPackage = mockSdkClickHandler.queue.get(5); // create activity package test TestActivityPackage testPrefixClickPackage = new TestActivityPackage(prefixClickPackage); @@ -1586,7 +1819,7 @@ public void testSendReferrer() { testPrefixClickPackage.testClickPackage("reftag"); // get the click package - ActivityPackage incompleteClickPackage = mockSdkClickHandler.queue.get(5); + ActivityPackage incompleteClickPackage = mockSdkClickHandler.queue.get(6); // create activity package test TestActivityPackage testIncompleteClickPackage = new TestActivityPackage(incompleteClickPackage); @@ -1596,6 +1829,7 @@ public void testSendReferrer() { testIncompleteClickPackage.testClickPackage("reftag"); } + @Test public void testGetAttribution() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testGetAttribution"); @@ -1620,16 +1854,16 @@ public void onAttributionChanged(AdjustAttribution attribution) { }); // start activity handler with config - ActivityHandler activityHandler = getFirstActivityHandler(config); + ActivityHandler activityHandler = getActivityHandler(config); - SystemClock.sleep(2000); + SystemClock.sleep(1500); // test init values - checkInitTests(); + checkInitTests(activityHandler); - startActivity(activityHandler); + resumeActivity(activityHandler); - SystemClock.sleep(2000); + SystemClock.sleep(1500); // subsession count is 1 // attribution is null, @@ -1637,7 +1871,7 @@ public void onAttributionChanged(AdjustAttribution attribution) { // -> Not called // test first session start - SessionState newSessionState = new SessionState(SessionType.NEW_SESSION); + StateSession newSessionState = new StateSession(StateSession.SessionType.NEW_SESSION); newSessionState.getAttributionIsCalled = false; checkStartInternal(newSessionState); @@ -1695,12 +1929,12 @@ public void onAttributionChanged(AdjustAttribution attribution) { "\"creative\" : \"ctValue\" , " + "\"click_label\" : \"clValue\" }"); } catch (JSONException e) { - fail(e.getMessage()); + assertUtil.fail(e.getMessage()); } AdjustAttribution attribution = AdjustAttribution.fromJson(jsonAttribution); // update the attribution - activityHandler.updateAttribution(attribution); + activityHandler.updateAttributionI(attribution); // attribution was updated assertUtil.debug("Wrote Attribution: tt:ttValue tn:tnValue net:nValue cam:cpValue adg:aValue cre:ctValue cl:clValue"); @@ -1746,6 +1980,7 @@ public void onAttributionChanged(AdjustAttribution attribution) { checkFurtherSessions(4, false); } + @Test public void testForegroundTimer() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testForegroundTimer"); @@ -1757,7 +1992,22 @@ public void testForegroundTimer() { AdjustConfig config = getConfig(); // start activity handler with config - ActivityHandler activityHandler = startAndCheckFirstSession(config); + ActivityHandler activityHandler = getActivityHandler(config); + + SystemClock.sleep(1500); + + // test init values + StateActivityHandlerInit stateActivityHandlerInit = new StateActivityHandlerInit(activityHandler); + stateActivityHandlerInit.foregroundTimerStart = 4; + stateActivityHandlerInit.foregroundTimerCycle = 4; + checkInitTests(stateActivityHandlerInit); + + resumeActivity(activityHandler); + + SystemClock.sleep(1500); + + // test session + checkFirstSession(); // wait enough to fire the first cycle SystemClock.sleep(3000); @@ -1779,6 +2029,7 @@ public void testForegroundTimer() { checkForegroundTimerFired(false); } + @Test public void testSendBackground() { // assert test name to read better in logcat mockLogger.Assert("TestActivityHandler testSendBackground"); @@ -1792,55 +2043,52 @@ public void testSendBackground() { config.setSendInBackground(true); // create activity handler without starting - ActivityHandler activityHandler = getActivityHandler(config, - true, // startEnabled - null, // readActivityState - null, // readAttribution - false, // isProductionEnvironment - LogLevel.INFO); // logLevel + ActivityHandler activityHandler = getActivityHandler(config); - SystemClock.sleep(2000); + SystemClock.sleep(1500); // handlers start sending - checkInitTests( - false, // eventBuffering - null, // defaultTracker - true); // startsSending + StateActivityHandlerInit stateActivityHandlerInit = new StateActivityHandlerInit(activityHandler); + stateActivityHandlerInit.startsSending = true; + stateActivityHandlerInit.sendInBackgroundConfigured = true; + stateActivityHandlerInit.foregroundTimerCycle = 4; + checkInitTests(stateActivityHandlerInit); - startActivity(activityHandler); + resumeActivity(activityHandler); - SystemClock.sleep(2000); + SystemClock.sleep(1500); // test session - checkFirstSession(); + StateSession newStateSession= new StateSession(StateSession.SessionType.NEW_SESSION); + newStateSession.sendInBackgroundConfigured = true; + newStateSession.toSend = true; + checkStartInternal(newStateSession); // end subsession // background timer starts stopActivity(activityHandler); - SystemClock.sleep(1000); + SystemClock.sleep(1500); // session end does not pause the handlers - checkEndSession(false, //pausing - true, // updateActivityState - false, // eventBufferingEnabled - true, // checkOnPause, - false, // forgroundAlreadySuspended - true); // backgroundTimerStarts + StateEndSession stateEndSession1 = new StateEndSession(); + stateEndSession1.pausing = false; + stateEndSession1.checkOnPause = true; + stateEndSession1.backgroundTimerStarts = true; + checkEndSession(stateEndSession1); // end subsession again // to test if background timer starts again stopActivity(activityHandler); - SystemClock.sleep(1000); + SystemClock.sleep(1500); // session end does not pause the handlers - checkEndSession(false, //pausing - true, // updateActivityState - false, // eventBufferingEnabled - true, // checkOnPause, - true, // forgroundAlreadySuspended - false); // backgroundTimerStarts + StateEndSession stateEndSession2 = new StateEndSession(); + stateEndSession2.pausing = false; + stateEndSession2.checkOnPause = true; + stateEndSession2.foregroundAlreadySuspended = true; + checkEndSession(stateEndSession2); // wait for background timer launch SystemClock.sleep(3000); @@ -1858,20 +2106,9 @@ public void testSendBackground() { SystemClock.sleep(1000); - // check that event package was added - assertUtil.test("PackageHandler addPackage"); - - // check that event was sent to package handler - assertUtil.test("PackageHandler sendFirstPackage"); - - // and not buffered - assertUtil.notInInfo("Buffered event"); - - // does fire background timer - assertUtil.verbose("Background timer starting. Launching in 4.0 seconds"); - - // after tracking the event it should write the activity state - assertUtil.debug("Wrote Activity state"); + StateEvent stateEvent = new StateEvent(); + stateEvent.backgroundTimerStarts = 4; + checkEvent(stateEvent); // disable and enable the sdk while in the background activityHandler.setEnabled(false); @@ -1880,7 +2117,7 @@ public void testSendBackground() { assertUtil.isFalse(activityHandler.isEnabled()); // check if message the disable of the SDK - assertUtil.info("Pausing package handler and attribution handler due to SDK being disabled"); + assertUtil.info("Pausing handlers due to SDK being disabled"); SystemClock.sleep(1000); @@ -1893,7 +2130,7 @@ public void testSendBackground() { assertUtil.isTrue(activityHandler.isEnabled()); // check if message the enable of the SDK - assertUtil.info("Resuming package handler and attribution handler due to SDK being enabled"); + assertUtil.info("Resuming handlers due to SDK being enabled"); SystemClock.sleep(1000); @@ -1904,13 +2141,13 @@ public void testSendBackground() { // set offline and online the sdk while in the background activityHandler.setOfflineMode(true); - InternalState internalState = activityHandler.getInternalState(); + ActivityHandler.InternalState internalState = activityHandler.getInternalState(); // check that it is offline assertUtil.isTrue(internalState.isOffline()); // check if message the offline of the SDK - assertUtil.info("Pausing package and attribution handler to put SDK offline mode"); + assertUtil.info("Pausing handlers to put SDK offline mode"); SystemClock.sleep(1000); @@ -1923,7 +2160,7 @@ public void testSendBackground() { assertUtil.isTrue(internalState.isOnline()); // check if message the online of the SDK - assertUtil.info("Resuming package handler and attribution handler to put SDK in online mode"); + assertUtil.info("Resuming handlers to put SDK in online mode"); SystemClock.sleep(1000); @@ -1932,182 +2169,729 @@ public void testSendBackground() { checkHandlerStatus(false); } - public void checkFinishTasks(AdjustConfig config, - DelegatesPresent delegatesPresent) - { - ActivityHandler activityHandler = startAndCheckFirstSession(config); + @Test + public void testSessionParameters() { + // assert test name to read better in logcat + mockLogger.Assert("TestActivityHandler testSessionParameters"); - // test first session package - ActivityPackage firstSessionPackage = mockPackageHandler.queue.get(0); + // create the config to start the session + AdjustConfig config = getConfig(); - // create activity package test - TestActivityPackage testActivityPackage = new TestActivityPackage(firstSessionPackage); + // create handler and start the first session + config.sessionParametersActionsArray = new ArrayList(); + config.sessionParametersActionsArray.add(new IRunActivityHandler() { + @Override + public void run(ActivityHandler activityHandler) { + // + activityHandler.addSessionCallbackParameterI("cKey", "cValue"); + activityHandler.addSessionCallbackParameterI("cFoo", "cBar"); - testActivityPackage.needsResponseDetails = - delegatesPresent.attributionDelegatePresent || - delegatesPresent.eventSuccessDelegatePresent || - delegatesPresent.eventFailureDelegatePresent || - delegatesPresent.sessionSuccessDelegatePresent || - delegatesPresent.sessionFailureDelegatePresent; + activityHandler.addSessionCallbackParameterI("cKey", "cValue2"); + activityHandler.resetSessionCallbackParametersI(); - // set first session - testActivityPackage.testSessionPackage(1); + activityHandler.addSessionCallbackParameterI("cKey", "cValue"); + activityHandler.addSessionCallbackParameterI("cFoo", "cBar"); + activityHandler.removeSessionCallbackParameterI("cKey"); - // simulate a successful session - SessionResponseData successSessionResponseData = (SessionResponseData) ResponseData.buildResponseData(firstSessionPackage); - successSessionResponseData.success = true; + // + activityHandler.addSessionPartnerParameterI("pKey", "pValue"); + activityHandler.addSessionPartnerParameterI("pFoo", "pBar"); - activityHandler.finishedTrackingActivity(successSessionResponseData); - SystemClock.sleep(1000); + activityHandler.addSessionPartnerParameterI("pKey", "pValue2"); + activityHandler.resetSessionPartnerParametersI(); - // attribution handler should always receive the session response - assertUtil.test("AttributionHandler checkSessionResponse"); - // the first session does not trigger the event response delegate + activityHandler.addSessionPartnerParameterI("pKey", "pValue"); + activityHandler.addSessionPartnerParameterI("pFoo", "pBar"); + activityHandler.removeSessionPartnerParameterI("pKey"); + } + }); - assertUtil.notInDebug("Launching success event tracking listener"); - assertUtil.notInDebug("Launching failed event tracking listener"); + ActivityHandler activityHandler = getActivityHandler(config); - activityHandler.launchSessionResponseTasks(successSessionResponseData); - SystemClock.sleep(1000); + SystemClock.sleep(1500); - // if present, the first session triggers the success session delegate - if (delegatesPresent.sessionSuccessDelegatePresent) { - assertUtil.debug("Launching success session tracking listener"); - } else { - assertUtil.notInDebug("Launching success session tracking delegate"); - } - // it doesn't trigger the failure session delegate - assertUtil.notInDebug("Launching failed session tracking listener"); + // test init values + checkInitTests(activityHandler); - // simulate a failure session - SessionResponseData failureSessionResponseData = (SessionResponseData)ResponseData.buildResponseData(firstSessionPackage); - failureSessionResponseData.success = false; + checkSessionParameters(); - activityHandler.launchSessionResponseTasks(failureSessionResponseData); - SystemClock.sleep(1000); + resumeActivity(activityHandler); - // it doesn't trigger the success session delegate - assertUtil.notInDebug("Launching success session tracking listener"); + AdjustEvent firstEvent = new AdjustEvent("event1"); + activityHandler.trackEvent(firstEvent); - // if present, the first session triggers the failure session delegate - if (delegatesPresent.sessionFailureDelegatePresent) { - assertUtil.debug("Launching failed session tracking listener"); - } else { - assertUtil.notInDebug("Launching failed session tracking listener"); - } + SystemClock.sleep(1500); - // test success event response data - activityHandler.trackEvent(new AdjustEvent("abc123")); - SystemClock.sleep(1000); + // test session + checkFirstSession(); - ActivityPackage eventPackage = mockPackageHandler.queue.get(1); - EventResponseData eventSuccessResponseData = (EventResponseData)ResponseData.buildResponseData(eventPackage); - eventSuccessResponseData.success = true; + StateEvent stateEvent1 = new StateEvent(); + checkEvent(stateEvent1); - activityHandler.finishedTrackingActivity(eventSuccessResponseData); - SystemClock.sleep(1000); + // 1 session + 1 event + assertUtil.isEqual(2, mockPackageHandler.queue.size()); - // attribution handler should never receive the event response - assertUtil.notInTest("AttributionHandler checkSessionResponse"); + // get the session package + ActivityPackage firstSessionPackage = mockPackageHandler.queue.get(0); - // if present, the success event triggers the success event delegate - if (delegatesPresent.eventSuccessDelegatePresent) { - assertUtil.debug("Launching success event tracking listener"); - } else { - assertUtil.notInDebug("Launching success event tracking listener"); - } - // it doesn't trigger the failure event delegate - assertUtil.notInDebug("Launching failed event tracking listener"); + // create event package test + TestActivityPackage testFirstSessionPackage = new TestActivityPackage(firstSessionPackage); - // test failure event response data - EventResponseData eventFailureResponseData = (EventResponseData)ResponseData.buildResponseData(eventPackage); - eventFailureResponseData.success = false; + // set event test parameters + testFirstSessionPackage.callbackParams = "{\"cFoo\":\"cBar\"}"; + testFirstSessionPackage.partnerParams = "{\"pFoo\":\"pBar\"}"; - activityHandler.finishedTrackingActivity(eventFailureResponseData); - SystemClock.sleep(1000); + testFirstSessionPackage.testSessionPackage(1); - // attribution handler should never receive the event response - assertUtil.notInTest("AttributionHandler checkSessionResponse"); + // get the event + ActivityPackage firstEventPackage = mockPackageHandler.queue.get(1); - // if present, the failure event triggers the failure event delegate - if (delegatesPresent.eventFailureDelegatePresent) { - assertUtil.debug("Launching failed event tracking listener"); - } else { - assertUtil.notInDebug("Launching failed event tracking listener"); - } - // it doesn't trigger the success event delegate - assertUtil.notInDebug("Launching success event tracking listener"); + // create event package test + TestActivityPackage testFirstEventPackage = new TestActivityPackage(firstEventPackage); - // test click - Uri attributions = Uri.parse("AdjustTests://example.com/path/inApp?adjust_tracker=trackerValue&other=stuff&adjust_campaign=campaignValue&adjust_adgroup=adgroupValue&adjust_creative=creativeValue"); - long now = System.currentTimeMillis(); + // set event test parameters + testFirstEventPackage.eventCount = "1"; + testFirstEventPackage.suffix = "'event1'"; + testFirstEventPackage.callbackParams = "{\"cFoo\":\"cBar\"}"; + testFirstEventPackage.partnerParams = "{\"pFoo\":\"pBar\"}"; - activityHandler.readOpenUrl(attributions, now); + testFirstEventPackage.testEventPackage("event1"); + + // end current session + stopActivity(activityHandler); + SystemClock.sleep(1000); + + checkEndSession(); + activityHandler.teardown(false); + activityHandler = null; + + AdjustConfig newConfig = getConfig(); + ActivityHandler restartActivityHandler = getActivityHandler(newConfig); + + SystemClock.sleep(1500); + + // start new one + // delay start not configured because activity state is already created + StateActivityHandlerInit restartActivityHandlerInit = new StateActivityHandlerInit(restartActivityHandler); + restartActivityHandlerInit.activityStateAlreadyCreated = true; + restartActivityHandlerInit.readActivityState = "ec:1 sc:1"; + restartActivityHandlerInit.readCallbackParameters = "{cFoo=cBar}"; + restartActivityHandlerInit.readPartnerParameters = "{pFoo=pBar}"; + + // test init values + checkInitTests(restartActivityHandlerInit); + + resumeActivity(restartActivityHandler); + + SystemClock.sleep(1500); + + StateSession stateRestartSession = new StateSession(StateSession.SessionType.NEW_SUBSESSION); + stateRestartSession.subsessionCount = 2; + stateRestartSession.eventCount = 1; + + checkStartInternal(stateRestartSession); + + // create the second Event + AdjustEvent secondEvent = new AdjustEvent("event2"); + secondEvent.addCallbackParameter("ceFoo", "ceBar"); + secondEvent.addPartnerParameter("peFoo", "peBar"); + + restartActivityHandler.trackEvent(secondEvent); + + AdjustEvent thirdEvent = new AdjustEvent("event3"); + thirdEvent.addCallbackParameter("cFoo", "ceBar"); + thirdEvent.addPartnerParameter("pFoo", "peBar"); + + restartActivityHandler.trackEvent(thirdEvent); + + SystemClock.sleep(1500); + + // 2 events + assertUtil.isEqual(2, mockPackageHandler.queue.size()); + + // get the event + ActivityPackage secondEventPackage = mockPackageHandler.queue.get(0); + + // create event package test + TestActivityPackage testSecondEventPackage = new TestActivityPackage(secondEventPackage); + + // set event test parameters + testSecondEventPackage.eventCount = "2"; + testSecondEventPackage.suffix = "'event2'"; + testSecondEventPackage.callbackParams = "{\"ceFoo\":\"ceBar\",\"cFoo\":\"cBar\"}"; + testSecondEventPackage.partnerParams = "{\"peFoo\":\"peBar\",\"pFoo\":\"pBar\"}"; + + testSecondEventPackage.testEventPackage("event2"); + + // get the event + ActivityPackage thirdEventPackage = mockPackageHandler.queue.get(1); + + // create event package test + TestActivityPackage testThirdEventPackage = new TestActivityPackage(thirdEventPackage); + + // set event test parameters + testThirdEventPackage.eventCount = "3"; + testThirdEventPackage.suffix = "'event3'"; + testThirdEventPackage.callbackParams = "{\"cFoo\":\"ceBar\"}"; + testThirdEventPackage.partnerParams = "{\"pFoo\":\"peBar\"}"; + + testThirdEventPackage.testEventPackage("event3"); + } + + private void checkSessionParameters() { + // + assertUtil.debug("Wrote Session Callback parameters: {cKey=cValue}"); + assertUtil.debug("Wrote Session Callback parameters: {cKey=cValue, cFoo=cBar}"); + + assertUtil.warn("Key cKey will be overwritten"); + assertUtil.debug("Wrote Session Callback parameters: {cKey=cValue2, cFoo=cBar}"); + + assertUtil.debug("Wrote Session Callback parameters: null"); + + assertUtil.debug("Wrote Session Callback parameters: {cKey=cValue}"); + assertUtil.debug("Wrote Session Callback parameters: {cKey=cValue, cFoo=cBar}"); + + assertUtil.debug("Key cKey will be removed"); + assertUtil.debug("Wrote Session Callback parameters: {cFoo=cBar}"); + + // + assertUtil.debug("Wrote Session Partner parameters: {pKey=pValue}"); + assertUtil.debug("Wrote Session Partner parameters: {pKey=pValue, pFoo=pBar}"); + + assertUtil.warn("Key pKey will be overwritten"); + assertUtil.debug("Wrote Session Partner parameters: {pKey=pValue2, pFoo=pBar}"); + + assertUtil.debug("Wrote Session Partner parameters: null"); + + assertUtil.debug("Wrote Session Partner parameters: {pKey=pValue}"); + assertUtil.debug("Wrote Session Partner parameters: {pKey=pValue, pFoo=pBar}"); + + assertUtil.debug("Key pKey will be removed"); + assertUtil.debug("Wrote Session Partner parameters: {pFoo=pBar}"); + } + + @Test + public void testDelayStartTimerFirst() { + // assert test name to read better in logcat + mockLogger.Assert("TestActivityHandler testDelayStartTimerFirst"); + + // create the config to start the session + AdjustConfig config = getConfig(); + + config.setDelayStart(4); + + config.sessionParametersActionsArray = new ArrayList(); + config.sessionParametersActionsArray.add(new IRunActivityHandler() { + @Override + public void run(ActivityHandler activityHandler) { + activityHandler.addSessionCallbackParameterI("scpKey", "scpValue"); + activityHandler.addSessionPartnerParameterI("sppKey", "sppValue"); + } + }); + + // create handler and start the first session + // start activity handler with config + ActivityHandler activityHandler = getActivityHandler(config); + + SystemClock.sleep(1500); + + // test init values + StateActivityHandlerInit stateActivityHandlerInit = new StateActivityHandlerInit(activityHandler); + stateActivityHandlerInit.delayStartConfigured = true; + checkInitTests(stateActivityHandlerInit); + + resumeActivity(activityHandler); + resumeActivity(activityHandler); + + SystemClock.sleep(1000); + + StateSession newStateSession = new StateSession(StateSession.SessionType.NEW_SESSION); + // delay start means it starts paused + newStateSession.toSend = false; + // sdk click handler does not start paused + newStateSession.sdkClickHandlerAlsoStartsPaused = false; + // delay configured + newStateSession.delayStart = "4.0"; + + checkStartInternal(newStateSession); + + // change state session for non session + StateSession nonSession = new StateSession(StateSession.SessionType.NONSESSION); + // delay already processed + nonSession.delayStart = null; + nonSession.toSend = false; + nonSession.sdkClickHandlerAlsoStartsPaused = false; + nonSession.foregroundTimerAlreadyStarted = true; + + checkStartInternal(nonSession); + + // create the first Event object with callback and partner parameters + AdjustEvent firstEvent = new AdjustEvent("event1"); + + firstEvent.addCallbackParameter("keyCall", "valueCall"); + firstEvent.addPartnerParameter("keyPartner", "valuePartner"); + + activityHandler.trackEvent(firstEvent); + + SystemClock.sleep(1000); + + StateEvent stateEvent = new StateEvent(); + checkEvent(stateEvent); + + SystemClock.sleep(4000); + + assertUtil.verbose("Delay Start timer fired"); + + checkSendFirstPackages(true, activityHandler.getInternalState(), true, false); + + activityHandler.sendFirstPackages(); + SystemClock.sleep(1000); + + checkSendFirstPackages(false, activityHandler.getInternalState(), true, false); + + // 1 session + 1 event + assertUtil.isEqual(2, mockPackageHandler.queue.size()); + + // get the first event + ActivityPackage firstEventPackage = mockPackageHandler.queue.get(1); + + // create event package test + TestActivityPackage testFirstEventPackage = new TestActivityPackage(firstEventPackage); + + // set event test parameters + testFirstEventPackage.eventCount = "1"; + testFirstEventPackage.savedCallbackParameters = new HashMap(1); + testFirstEventPackage.savedCallbackParameters.put("keyCall", "valueCall"); + testFirstEventPackage.savedPartnerParameters = new HashMap(1); + testFirstEventPackage.savedPartnerParameters.put("keyPartner", "valuePartner"); + testFirstEventPackage.suffix = "'event1'"; + + // test first event + testFirstEventPackage.testEventPackage("event1"); + } + + @Test + public void testDelayStartSendFirst() { + // assert test name to read better in logcat + mockLogger.Assert("TestActivityHandler testDelayStartSendFirst"); + + // create the config to start the session + AdjustConfig config = getConfig(); + + config.setDelayStart(5); + + // start activity handler with config + ActivityHandler activityHandler = getActivityHandler(config); + + SystemClock.sleep(1500); + + // test init values + StateActivityHandlerInit stateActivityHandlerInit = new StateActivityHandlerInit(activityHandler); + stateActivityHandlerInit.delayStartConfigured = true; + checkInitTests(stateActivityHandlerInit); + + resumeActivity(activityHandler); + resumeActivity(activityHandler); + + SystemClock.sleep(1000); + + StateSession newStateSession = new StateSession(StateSession.SessionType.NEW_SESSION); + // delay start means it starts paused + newStateSession.toSend = false; + // sdk click handler does not start paused + newStateSession.sdkClickHandlerAlsoStartsPaused = false; + // delay configured + newStateSession.delayStart = "5.0"; + + checkStartInternal(newStateSession); + + // change state session for non session + StateSession nonSession = new StateSession(StateSession.SessionType.NONSESSION); + // delay already processed + nonSession.delayStart = null; + nonSession.toSend = false; + nonSession.sdkClickHandlerAlsoStartsPaused = false; + nonSession.foregroundTimerAlreadyStarted = true; + + checkStartInternal(nonSession); + + activityHandler.sendFirstPackages(); + + SystemClock.sleep(3000); + + assertUtil.notInVerbose("Delay Start timer fired"); + + checkSendFirstPackages(true,activityHandler.getInternalState(), true, false); + + activityHandler.sendFirstPackages(); + SystemClock.sleep(1000); + + checkSendFirstPackages(false,activityHandler.getInternalState(), true, false); + } + + @Test + public void testUpdateStart() { + // assert test name to read better in logcat + mockLogger.Assert("TestActivityHandler testUpdateStart"); + + // create the config to start the session + AdjustConfig config = getConfig(); + + config.setDelayStart(10.1); + + ActivityHandler activityHandler = getActivityHandler(config); + SystemClock.sleep(1500); + + // test init values + StateActivityHandlerInit stateActivityHandlerInit = new StateActivityHandlerInit(activityHandler); + stateActivityHandlerInit.delayStartConfigured = true; + checkInitTests(stateActivityHandlerInit); + + resumeActivity(activityHandler); + SystemClock.sleep(1000); + + StateSession newStateSession = new StateSession(StateSession.SessionType.NEW_SESSION); + // delay start means it starts paused + newStateSession.toSend = false; + // sdk click handler does not start paused + newStateSession.sdkClickHandlerAlsoStartsPaused = false; + // delay configured + newStateSession.delayStart = "10.0"; + + stopActivity(activityHandler); SystemClock.sleep(1000); - assertUtil.test("PackageHandler addPackage"); - assertUtil.test("PackageHandler sendFirstPackage"); + StateEndSession stateEndSession = new StateEndSession(); + checkEndSession(stateEndSession); + + activityHandler.teardown(false); + activityHandler = null; + + SystemClock.sleep(1000); + + ActivityHandler restartActivityHandler = getActivityHandler(config); + + SystemClock.sleep(1500); + + // start new one + // delay start not configured because activity state is already created + StateActivityHandlerInit restartActivityHandlerInit = new StateActivityHandlerInit(restartActivityHandler); + restartActivityHandlerInit.activityStateAlreadyCreated = true; + restartActivityHandlerInit.readActivityState = "ec:0 sc:1"; + restartActivityHandlerInit.updatePackages = true; + + // test init values + checkInitTests(restartActivityHandlerInit); + + resumeActivity(restartActivityHandler); + + SystemClock.sleep(1500); + + StateSession stateRestartSession = new StateSession(StateSession.SessionType.NEW_SUBSESSION); + stateRestartSession.activityStateAlreadyCreated = true; + stateRestartSession.subsessionCount = 2; + + checkStartInternal(stateRestartSession); + } + + @Test + public void testLogLevel() { + // assert test name to read better in logcat + mockLogger.Assert("TestActivityHandler testLogLevel"); + + AdjustConfig config = getConfig(); + + config.setLogLevel(LogLevel.VERBOSE); + config.setLogLevel(LogLevel.DEBUG); + config.setLogLevel(LogLevel.INFO); + config.setLogLevel(LogLevel.WARN); + config.setLogLevel(LogLevel.ERROR); + config.setLogLevel(LogLevel.ASSERT); + + assertUtil.test("MockLogger setLogLevel: " + LogLevel.VERBOSE); + assertUtil.test("MockLogger setLogLevel: " + LogLevel.DEBUG); + assertUtil.test("MockLogger setLogLevel: " + LogLevel.INFO); + assertUtil.test("MockLogger setLogLevel: " + LogLevel.WARN); + assertUtil.test("MockLogger setLogLevel: " + LogLevel.ERROR); + assertUtil.test("MockLogger setLogLevel: " + LogLevel.ASSERT); + + config.setLogLevel(LogLevel.SUPRESS); + + // chooses Assert because config object was not configured to allow suppress + assertUtil.test("MockLogger setLogLevel: " + LogLevel.ASSERT); + + // init log level with assert because it was not configured to allow suppress + config = getConfig(LogLevel.ASSERT, "production", "123456789012", false, context); + + config.setLogLevel(LogLevel.SUPRESS); + // chooses Assert because config object was not configured to allow suppress + assertUtil.test("MockLogger setLogLevel: " + LogLevel.ASSERT); + + // init with info because it's sandbox + config = getConfig(LogLevel.INFO, "sandbox", "123456789012", true, context); + + config.setLogLevel(LogLevel.SUPRESS); + // chooses Supress because config object was configured to allow suppress + assertUtil.test("MockLogger setLogLevel: " + LogLevel.SUPRESS); + + // init with info because it's sandbox + config = getConfig(LogLevel.SUPRESS, "production", "123456789012", true, context); + + config.setLogLevel(LogLevel.ASSERT); + + // chooses Supress because config object was configured to allow suppress + assertUtil.test("MockLogger setLogLevel: " + LogLevel.SUPRESS); + } + + @Test + public void testTeardown() { + // assert test name to read better in logcat + mockLogger.Assert("TestActivityHandler testTeardown"); + + // change the timer defaults + AdjustFactory.setTimerInterval(4000); + + // create the config to start the session + AdjustConfig config = getConfig(); + + config.setDelayStart(4); + + // enable send in the background + config.setSendInBackground(true); + + // start activity handler with config + ActivityHandler activityHandler = getActivityHandler(config); + + SystemClock.sleep(1500); + + // handlers start sending + StateActivityHandlerInit stateActivityHandlerInit = new StateActivityHandlerInit(activityHandler); + stateActivityHandlerInit.startsSending = false; + stateActivityHandlerInit.sendInBackgroundConfigured = true; + stateActivityHandlerInit.foregroundTimerCycle = 4; + stateActivityHandlerInit.delayStartConfigured = true; + stateActivityHandlerInit.sdkClickHandlerAlsoStartsPaused = false; + checkInitTests(stateActivityHandlerInit); + + resumeActivity(activityHandler); + + SystemClock.sleep(1500); + + // test session + StateSession newStateSession= new StateSession(StateSession.SessionType.NEW_SESSION); + newStateSession.sendInBackgroundConfigured = true; + newStateSession.toSend = false; + newStateSession.sdkClickHandlerAlsoStartsPaused = false; + newStateSession.delayStart = "4.0"; + checkStartInternal(newStateSession); + + activityHandler.teardown(false); + + assertUtil.test("PackageHandler teardown deleteState, false"); + assertUtil.test("AttributionHandler teardown"); + assertUtil.test("SdkClickHandler teardown"); + + activityHandler.teardown(false); + + assertUtil.notInTest("PackageHandler teardown deleteState, false"); + assertUtil.notInTest("AttributionHandler teardown"); + assertUtil.notInTest("SdkClickHandler teardown"); + } + + private AdjustConfig getConfig() { + return getConfig(null); + } + + private AdjustConfig getConfig(LogLevel logLevel) { + return getConfig(logLevel, "sandbox", "123456789012", false, context); + } + + private AdjustConfig getConfig(LogLevel initLogLevel, + String environment, + String appToken, + boolean allowSupressLogLevel, + Context context) + { + AdjustConfig adjustConfig = null; + + if (allowSupressLogLevel) { + adjustConfig = new AdjustConfig(context, appToken, environment, allowSupressLogLevel); + } else { + adjustConfig = new AdjustConfig(context, appToken, environment); + } + + if (adjustConfig != null) { + if (initLogLevel != null) { + assertUtil.test("MockLogger setLogLevel: " + initLogLevel); + } + if (environment == "sandbox") { + assertUtil.Assert("SANDBOX: Adjust is running in Sandbox mode. Use this setting for testing. Don't forget to set the environment to `production` before publishing!"); + } else if (environment == "production") { + assertUtil.Assert("PRODUCTION: Adjust is running in Production mode. Use this setting only for the build that you want to publish. Set the environment to `sandbox` if you want to test your app!"); + } else { + assertUtil.fail(); + } + } + + return adjustConfig; + } + + private ActivityHandler startAndCheckFirstSession(AdjustConfig config) { + // start activity handler with config + ActivityHandler activityHandler = getActivityHandler(config); + + SystemClock.sleep(1500); + + startAndCheckFirstSession(activityHandler); + + return activityHandler; + } + + private void startAndCheckFirstSession(ActivityHandler activityHandler) { + // test init values + checkInitTests(activityHandler); + + resumeActivity(activityHandler); + + SystemClock.sleep(1500); + + // test session + checkFirstSession(); + } + + private ActivityHandler getActivityHandler(AdjustConfig config) { + ActivityHandler activityHandler = ActivityHandler.getInstance(config); + + if (activityHandler != null) { + assertUtil.test("MockLogger lockLogLevel"); + + ActivityHandler.InternalState internalState = activityHandler.getInternalState(); + // test default values + assertUtil.isTrue(internalState.isEnabled()); + assertUtil.isTrue(internalState.isOnline()); + assertUtil.isTrue(internalState.isBackground()); + assertUtil.isTrue(internalState.isToStartNow()); + assertUtil.isFalse(internalState.isToUpdatePackages()); + } + + return activityHandler; + } + + private void checkInitAndStart(ActivityHandler activityHandler, StateActivityHandlerInit initState, StateSession stateSession) { + checkInitTests(initState); + + resumeActivity(activityHandler); + + SystemClock.sleep(1500); + + checkStartInternal(stateSession); + } + + private void checkInitTests(ActivityHandler activityHandler) { + StateActivityHandlerInit stateActivityHandlerInit = new StateActivityHandlerInit(activityHandler); + checkInitTests(stateActivityHandlerInit); + } + + private void checkInitTests(StateActivityHandlerInit sInit) { + checkReadFile(sInit.readAttribution, "Attribution"); + checkReadFile(sInit.readActivityState, "Activity state"); + checkReadFile(sInit.readCallbackParameters, "Session Callback parameters"); + checkReadFile(sInit.readPartnerParameters, "Session Partner parameters"); + + // check values read from activity state + assertUtil.isEqual(sInit.internalState.isEnabled(), sInit.startEnabled); + //assertUtil.isEqual(sInit.internalState.isToUpdatePackages(), sInit.updatePackages); + + // check event buffering + if (sInit.eventBufferingIsEnabled) { + assertUtil.info("Event buffering is enabled"); + } else { + assertUtil.notInInfo("Event buffering is enabled"); + } + + // check Google play is set + assertUtil.info("Google Play Services Advertising ID read correctly at start time"); + + // check default tracker + if (sInit.defaultTracker != null) { + assertUtil.info("Default tracker: '%s'", sInit.defaultTracker); + } else { + assertUtil.notInInfo("Default tracker: "); + } + + // check foreground timer was created + assertUtil.verbose("Foreground timer configured to fire after "+ sInit.foregroundTimerStart + + ".0 seconds of starting and cycles every " + sInit.foregroundTimerCycle + ".0 seconds"); + + // check background timer was created + if (sInit.sendInBackgroundConfigured) { + assertUtil.info("Send in background configured"); + } else { + assertUtil.notInInfo("Send in background configured"); + } - // test sdk_click response data - ActivityPackage sdkClickPackage = mockSdkClickHandler.queue.get(0); - ClickResponseData clickResponseData = (ClickResponseData)ResponseData.buildResponseData(sdkClickPackage); + if (sInit.delayStartConfigured) { + assertUtil.info("Delay start configured"); + assertUtil.isTrue(sInit.internalState.isDelayStart()); + } else { + assertUtil.notInInfo("Delay start configured"); + assertUtil.isFalse(sInit.internalState.isDelayStart()); + } - activityHandler.finishedTrackingActivity(clickResponseData); - SystemClock.sleep(1000); + if (sInit.startsSending) { + assertUtil.test("PackageHandler init, startsSending: true"); + assertUtil.test("AttributionHandler init, startsSending: true"); + assertUtil.test("SdkClickHandler init, startsSending: true"); + } else { + assertUtil.test("PackageHandler init, startsSending: false"); + assertUtil.test("AttributionHandler init, startsSending: false"); + if (sInit.sdkClickHandlerAlsoStartsPaused) { + assertUtil.test("SdkClickHandler init, startsSending: false"); + } else { + assertUtil.test("SdkClickHandler init, startsSending: true"); + } + } - // attribution handler should never receive the click response - assertUtil.notInTest("AttributionHandler checkSessionResponse"); - // it doesn't trigger the any event delegate - assertUtil.notInDebug("Launching success event tracking listener"); - assertUtil.notInDebug("Launching failed event tracking listener"); - } + if (sInit.updatePackages) { + checkUpdatePackages(sInit.internalState, sInit.activityStateAlreadyCreated); + } else { + assertUtil.notInTest("PackageHandler updatePackages"); + } - private class DelegatesPresent { - boolean attributionDelegatePresent; - boolean eventSuccessDelegatePresent; - boolean eventFailureDelegatePresent; - boolean sessionSuccessDelegatePresent; - boolean sessionFailureDelegatePresent; + if (sInit.sendReferrer != null) { + assertUtil.verbose("Reading query string (%s)", sInit.sendReferrer); + assertUtil.test("SdkClickHandler sendSdkClick"); + } else { + assertUtil.notInVerbose("Reading query string "); + assertUtil.notInTest("SdkClickHandler sendSdkClick"); + } } - private class SessionState { - boolean toSend = true; - boolean paused = false; - int sessionCount = 1; - int subsessionCount = 1; - SessionType sessionType = null; - int eventCount = 0; - Boolean getAttributionIsCalled = null; - Boolean timerAlreadyStarted = false; - boolean eventBufferingIsEnabled = false; - boolean foregroundTimerStarts = true; - boolean foregroundTimerAlreadyStarted = false; - - SessionState(SessionType sessionType) { - switch (sessionType) { - case NEW_SUBSESSION: - case NONSESSION: - timerAlreadyStarted = true; - break; - } - this.sessionType = sessionType; + private void checkReadFile(String fileLog, String objectName) { + if (fileLog == null) { + assertUtil.debug(objectName + " file not found"); + } else { + assertUtil.debug("Read "+ objectName +": " + fileLog); } } - private enum SessionType { - NEW_SESSION, - NEW_SUBSESSION, - TIME_TRAVEL, - NONSESSION; + private void resumeActivity(ActivityHandler activityHandler) { + // start activity + activityHandler.onResume(); + + ActivityHandler.InternalState internalState = activityHandler.getInternalState(); + + // comes to the foreground + assertUtil.isTrue(internalState.isForeground()); } private void checkFirstSession() { - SessionState newSessionState = new SessionState(SessionType.NEW_SESSION); - checkStartInternal(newSessionState); + StateSession newStateSession= new StateSession(StateSession.SessionType.NEW_SESSION); + checkStartInternal(newStateSession); } private void checkSubSession(int sessionCount, int subsessionCount, boolean getAttributionIsCalled) { - SessionState subSessionState = new SessionState(SessionType.NEW_SUBSESSION); + StateSession subSessionState = new StateSession(StateSession.SessionType.NEW_SUBSESSION); subSessionState.sessionCount = sessionCount; subSessionState.subsessionCount = subsessionCount; subSessionState.getAttributionIsCalled = getAttributionIsCalled; @@ -2116,320 +2900,266 @@ private void checkSubSession(int sessionCount, int subsessionCount, boolean getA } private void checkFurtherSessions(int sessionCount, boolean getAttributionIsCalled) { - SessionState subSessionState = new SessionState(SessionType.NEW_SESSION); + StateSession subSessionState = new StateSession(StateSession.SessionType.NEW_SESSION); subSessionState.sessionCount = sessionCount; - subSessionState.timerAlreadyStarted = true; subSessionState.getAttributionIsCalled = getAttributionIsCalled; subSessionState.foregroundTimerAlreadyStarted = true; checkStartInternal(subSessionState); } - private void checkStartDisable() { - assertUtil.notInTest("AttributionHandler resumeSending"); - assertUtil.notInTest("PackageHandler resumeSending"); - assertUtil.notInTest("SdkClickHandler resumeSending"); - assertUtil.notInTest("AttributionHandler pauseSending"); - assertUtil.notInTest("PackageHandler pauseSending"); - assertUtil.notInTest("SdkClickHandler pauseSending"); - assertUtil.notInTest("PackageHandler addPackage"); - assertUtil.notInTest("PackageHandler sendFirstPackage"); - assertUtil.notInVerbose("Started subsession"); - assertUtil.notInVerbose("Time span since last activity too short for a new subsession"); - assertUtil.notInError("Time travel!"); - assertUtil.notInDebug("Wrote Activity state: "); - assertUtil.notInTest("AttributionHandler getAttribution"); - checkForegroundTimerFired(false); - } - - private void checkStartInternal(SessionState sessionState) + private void checkStartInternal(StateSession stateSession) { + // check delay start + checkDelayStart(stateSession); + // check onResume - checkOnResume(sessionState); + checkOnResume(stateSession); // update Handlers Status - checkHandlerStatus(!sessionState.toSend, sessionState.eventBufferingIsEnabled); + checkHandlerStatus((stateSession.disabled ? null : !stateSession.toSend), stateSession.eventBufferingIsEnabled, stateSession.sdkClickHandlerAlsoStartsPaused); // process Session - switch (sessionState.sessionType) { + switch (stateSession.sessionType) { case NEW_SESSION: // if the package was build, it was sent to the Package Handler assertUtil.test("PackageHandler addPackage"); // after adding, the activity handler ping the Package handler to send the package assertUtil.test("PackageHandler sendFirstPackage"); + + // writes activity state + assertUtil.debug("Wrote Activity state: " + + "ec:" + stateSession.eventCount + " sc:" + stateSession.sessionCount + " ssc:" + stateSession.subsessionCount); break; case NEW_SUBSESSION: // test the subsession message - assertUtil.verbose("Started subsession " + sessionState.subsessionCount + " of session " + sessionState.sessionCount); + assertUtil.verbose("Started subsession " + stateSession.subsessionCount + " of session " + stateSession.sessionCount); + // writes activity state + assertUtil.debug("Wrote Activity state: " + + "ec:" + stateSession.eventCount + " sc:" + stateSession.sessionCount + " ssc:" + stateSession.subsessionCount); break; case NONSESSION: // stopped for a short time, not enough for a new sub subsession assertUtil.verbose("Time span since last activity too short for a new subsession"); + // does not writes activity state + assertUtil.notInDebug("Wrote Activity state: "); break; case TIME_TRAVEL: assertUtil.error("Time travel!"); + // writes activity state + assertUtil.debug("Wrote Activity state: " + + "ec:" + stateSession.eventCount + " sc:" + stateSession.sessionCount + " ssc:" + stateSession.subsessionCount); + break; + case DISABLED: + assertUtil.notInTest("PackageHandler addPackage"); + assertUtil.notInVerbose("Started subsession "); + assertUtil.notInVerbose("Time span since last activity too short for a new subsession"); + assertUtil.notInError("Time travel!"); + // does not writes activity state + assertUtil.notInDebug("Wrote Activity state: "); break; } + /* // after processing the session, writes the activity state - if (sessionState.sessionType != SessionType.NONSESSION) { + if (stateSession.sessionType != stateSession.sessionType.NONSESSION && + stateSession.sessionType != stateSession.sessionType.DISABLED) + { assertUtil.debug("Wrote Activity state: " + - "ec:" + sessionState.eventCount + " sc:" + sessionState.sessionCount + " ssc:" + sessionState.subsessionCount); + "ec:" + stateSession.eventCount + " sc:" + stateSession.sessionCount + " ssc:" + stateSession.subsessionCount); + } else { + } + */ // check Attribution State - if (sessionState.getAttributionIsCalled != null) { - if (sessionState.getAttributionIsCalled) { + if (stateSession.getAttributionIsCalled != null) { + if (stateSession.getAttributionIsCalled) { assertUtil.test("AttributionHandler getAttribution"); } else { assertUtil.notInTest("AttributionHandler getAttribution"); } } - - /* - // start Foreground Timer - if (sessionState.paused) { - // foreground timer doesn't start when it's paused - assertUtil.notInDebug("Foreground timer started"); - checkForegroundTimerFired(false); - } else if (sessionState.timerAlreadyStarted != null) { - checkForegroundTimerFired(!sessionState.timerAlreadyStarted); - } - */ - } -/* - private void checkInitAndFirstSession() { - SessionState newSessionState = new SessionState(SessionType.NEW_SESSION); - checkInitAndFirstSession(newSessionState); } - private void checkInitAndFirstSession(SessionState sessionState) { - checkInitTests(sessionState.toSend); - checkStartInternal(sessionState); - } -*/ - private void checkForegroundTimerFired(boolean timerFired) { - // timer fired - if (timerFired) { - assertUtil.verbose("Foreground timer fired"); - } else { - assertUtil.notInVerbose("Foreground timer fired"); + private void checkDelayStart(StateSession stateSession) { + if (stateSession.delayStart == null) { + assertUtil.notInWarn("Waiting"); + return; } - } - - private void checkInitTests() { - checkInitTests(false); - } - - private void checkInitTests(boolean startsSending) { - checkInitTests(false, null, startsSending); - } - - private void checkInitTests(boolean eventBuffering, - String defaultTracker, - boolean startsSending) - { - // check event buffering - if (eventBuffering) { - assertUtil.info("Event buffering is enabled"); - } else { - assertUtil.notInInfo("Event buffering is enabled"); + if (stateSession.delayStart.equals("10.1")) { + assertUtil.warn("Delay start of 10.1 seconds bigger than max allowed value of 10.0 seconds"); + stateSession.delayStart = "10.0"; } - // check Google play is not set - assertUtil.info("Google Play Services Advertising ID read correctly at start time"); + assertUtil.info("Waiting " + stateSession.delayStart + " seconds before starting first session"); - // check default tracker - if (defaultTracker != null) { - assertUtil.info("Default tracker: '%s'", defaultTracker); - } + assertUtil.verbose("Delay Start timer starting. Launching in " + stateSession.delayStart + " seconds"); - if (startsSending) { - assertUtil.test("PackageHandler init, startsSending: true"); - assertUtil.test("AttributionHandler init, startsSending: true"); - assertUtil.test("SdkClickHandler init, startsSending: true"); - } else { - assertUtil.test("PackageHandler init, startsSending: false"); - assertUtil.test("AttributionHandler init, startsSending: false"); - assertUtil.test("SdkClickHandler init, startsSending: false"); + if (stateSession.activityStateAlreadyCreated) { + assertUtil.verbose("Wrote Activity state"); } } - private void checkEndSession() { - checkEndSession(true); - } - - private void checkEndSession(boolean updateActivityState) { - checkEndSession(true, updateActivityState, false, false, false, false); - } - - private void checkEndSession(boolean pausing, - boolean updateActivityState, - boolean eventBufferingEnabled) { - checkEndSession(pausing, updateActivityState, eventBufferingEnabled, false, false, false); - } - - private void checkEndSession(boolean pausing, - boolean updateActivityState, - boolean eventBufferingEnabled, - boolean checkOnPause, - boolean forgroundAlreadySuspended, - boolean backgroundTimerStarts) - { - if (checkOnPause) { - checkOnPause(forgroundAlreadySuspended, backgroundTimerStarts); - } - - if (pausing) { - checkHandlerStatus(true, eventBufferingEnabled); + private void checkOnResume(StateSession stateSession) { + if (!stateSession.startSubsession) { + assertUtil.notInVerbose("Background timer canceled"); + assertUtil.notInVerbose("Foreground timer is already started"); + assertUtil.notInVerbose("Foreground timer starting"); + assertUtil.notInVerbose("Subsession start"); + return; } + // TODO check delay start - if (updateActivityState) { - assertUtil.debug("Wrote Activity state: "); + // stops background timer + if (stateSession.sendInBackgroundConfigured) { + assertUtil.verbose("Background timer canceled"); } else { - assertUtil.notInDebug("Wrote Activity state: "); + assertUtil.notInVerbose("Background timer canceled"); } - } - - - private AdjustConfig getConfig() { - return getConfig(null); - } - - private AdjustConfig getConfig(LogLevel logLevel) { - return getConfig(logLevel, "sandbox", "123456789012", context); - } - - private AdjustConfig getConfig(LogLevel logLevel, - String environment, - String appToken, - Context context) - { - AdjustConfig adjustConfig = new AdjustConfig(context, appToken, environment); - if (adjustConfig != null) { - if (environment == "sandbox") { - assertUtil.Assert("SANDBOX: Adjust is running in Sandbox mode. Use this setting for testing. Don't forget to set the environment to `production` before publishing!"); - } else if (environment == "production") { - assertUtil.Assert("PRODUCTION: Adjust is running in Production mode. Use this setting only for the build that you want to publish. Set the environment to `sandbox` if you want to test your app!"); + // start foreground timer + if (stateSession.foregroundTimerStarts) { + if (stateSession.foregroundTimerAlreadyStarted) { + assertUtil.verbose("Foreground timer is already started"); } else { - fail(); - } - - if (logLevel != null) { - adjustConfig.setLogLevel(logLevel); + assertUtil.verbose("Foreground timer starting"); } + } else { + assertUtil.notInVerbose("Foreground timer is already started"); + assertUtil.notInVerbose("Foreground timer starting"); } - return adjustConfig; - } - - private ActivityHandler getFirstActivityHandler(AdjustConfig config) { - return getActivityHandler(config, true, null, null, false, LogLevel.INFO); + // starts the subsession + assertUtil.verbose("Subsession start"); } - private ActivityHandler getFirstActivityHandler(AdjustConfig config, - LogLevel logLevel) { - return getActivityHandler(config, true, null, null, false, logLevel); + private void checkHandlerStatus(Boolean pausing) { + checkHandlerStatus(pausing, false, true); } - private ActivityHandler getActivityHandler(AdjustConfig config, - boolean startEnabled, - String readActivityState, - String readAttribution, - boolean isProductionEnvironment, - LogLevel logLevel) { - ActivityHandler activityHandler = ActivityHandler.getInstance(config); - - if (activityHandler != null) { - // check log level - if (isProductionEnvironment) { - assertUtil.test("MockLogger setLogLevel: " + LogLevel.ASSERT); + private void checkHandlerStatus(Boolean pausing, boolean eventBufferingEnabled, boolean sdkClickHandlerAlsoPauses) { + if (pausing == null) { + assertUtil.notInTest("AttributionHandler pauseSending"); + assertUtil.notInTest("PackageHandler pauseSending"); + assertUtil.notInTest("SdkClickHandler pauseSending"); + assertUtil.notInTest("AttributionHandler resumeSending"); + assertUtil.notInTest("PackageHandler resumeSending"); + assertUtil.notInTest("SdkClickHandler resumeSending"); + return; + } + if (pausing) { + assertUtil.test("AttributionHandler pauseSending"); + assertUtil.test("PackageHandler pauseSending"); + if (sdkClickHandlerAlsoPauses) { + assertUtil.test("SdkClickHandler pauseSending"); } else { - assertUtil.test("MockLogger setLogLevel: " + logLevel); + assertUtil.test("SdkClickHandler resumeSending"); + } + } else { + assertUtil.test("AttributionHandler resumeSending"); + assertUtil.test("PackageHandler resumeSending"); + assertUtil.test("SdkClickHandler resumeSending"); + if (!eventBufferingEnabled) { + assertUtil.test("PackageHandler sendFirstPackage"); } - - // check if files are read in constructor - checkReadFiles(readActivityState, readAttribution); - - InternalState internalState = activityHandler.getInternalState(); - // test default values - assertUtil.isEqual(startEnabled, internalState.isEnabled()); - assertUtil.isTrue(internalState.isOnline()); - assertUtil.isTrue(internalState.isBackground()); } - - return activityHandler; - } - - private ActivityHandler startAndCheckFirstSession(AdjustConfig config) { - return startAndCheckFirstSession(config, LogLevel.INFO); } - private ActivityHandler startAndCheckFirstSession(AdjustConfig config, LogLevel logLevel) { - // start activity handler with config - ActivityHandler activityHandler = getFirstActivityHandler(config, logLevel); - - SystemClock.sleep(2000); - - // test init values - checkInitTests(); - - startActivity(activityHandler); - - SystemClock.sleep(2000); - - // test session - checkFirstSession(); - - return activityHandler; - } + private void checkEvent(StateEvent stateEvent) { + if (stateEvent.disabled) { + assertUtil.notInInfo("Skipping duplicated order ID "); + assertUtil.notInVerbose("Added order ID "); + assertUtil.notInTest("PackageHandler addPackage"); + assertUtil.notInInfo("Buffered event "); + assertUtil.notInTest("PackageHandler sendFirstPackage"); + assertUtil.notInDebug("Wrote Activity state"); + return; + } + if (stateEvent.duplicatedOrderId) { + // dropping duplicate transaction id + assertUtil.info("Skipping duplicated order ID '" + stateEvent.orderId + "'"); + // check that event package was not added + assertUtil.notInTest("PackageHandler addPackage"); + return; + } - private void startActivity(ActivityHandler activityHandler) { - // start activity - activityHandler.onResume(); + if (stateEvent.orderId != null) { + // check order id was added + assertUtil.verbose("Added order ID '" + stateEvent.orderId + "'"); + } else { + // check order id was not added + assertUtil.notInVerbose("Added order ID"); + } - InternalState internalState = activityHandler.getInternalState(); + // check that event package was added + assertUtil.test("PackageHandler addPackage"); - // comes to the foreground - assertUtil.isTrue(internalState.isForeground()); - } + if(stateEvent.bufferedSuffix != null) { + // check that event was buffered + assertUtil.info("Buffered event " + stateEvent.bufferedSuffix); - private void checkOnResume(SessionState sessionState) { - // stops background timer - assertUtil.verbose("Background timer canceled"); + // and not sent to package handler + assertUtil.notInTest("PackageHandler sendFirstPackage"); + } else { + // check that event was sent to package handler + assertUtil.test("PackageHandler sendFirstPackage"); + // and not buffered + assertUtil.notInInfo("Buffered event"); + } - // start foreground timer - if (sessionState.foregroundTimerStarts) { - if (sessionState.foregroundTimerAlreadyStarted) { - assertUtil.verbose("Foreground timer is already started"); - } else { - assertUtil.verbose("Foreground timer starting"); - } + if (stateEvent.backgroundTimerStarts != null) { + // does not fire background timer + assertUtil.verbose("Background timer starting. Launching in " + stateEvent.backgroundTimerStarts + ".0 seconds"); } else { - assertUtil.notInVerbose("Foreground timer is already started"); - assertUtil.notInVerbose("Foreground timer starting"); + // does not fire background timer + assertUtil.notInVerbose("Background timer starting"); } - // starts the subsession - assertUtil.verbose("Subsession start"); + // after tracking the event it should write the activity state + if (stateEvent.activityStateSuffix != null) { + assertUtil.debug("Wrote Activity state: " + stateEvent.activityStateSuffix); + } else { + assertUtil.debug("Wrote Activity state"); + } } private void stopActivity(ActivityHandler activityHandler) { // stop activity activityHandler.onPause(); - InternalState internalState = activityHandler.getInternalState(); + ActivityHandler.InternalState internalState = activityHandler.getInternalState(); // goes to the background assertUtil.isTrue(internalState.isBackground()); + } + + private void checkEndSession() { + StateEndSession stateEndSession = new StateEndSession(); + checkEndSession(stateEndSession); + } + + private void checkEndSession(StateEndSession stateEndSession) + { + if (stateEndSession.checkOnPause) { + checkOnPause(stateEndSession.foregroundAlreadySuspended, stateEndSession.backgroundTimerStarts); + } + + if (stateEndSession.pausing) { + checkHandlerStatus(stateEndSession.pausing, stateEndSession.eventBufferingEnabled, true); + } + if (stateEndSession.updateActivityState) { + assertUtil.debug("Wrote Activity state: "); + } else { + assertUtil.notInDebug("Wrote Activity state: "); + } } - private void checkOnPause(boolean forgroundAlreadySuspended, + private void checkOnPause(boolean foregroundAlreadySuspended, boolean backgroundTimerStarts) { // stop foreground timer - if (forgroundAlreadySuspended) { + if (foregroundAlreadySuspended) { assertUtil.verbose("Foreground timer is already suspended"); } else { assertUtil.verbose("Foreground timer suspended"); @@ -2444,51 +3174,52 @@ private void checkOnPause(boolean forgroundAlreadySuspended, // starts the subsession assertUtil.verbose("Subsession end"); - } - private void checkReadFiles(String readActivityState, String readAttribution) { - if (readAttribution == null) { - // test that the attribution file did not exist in the first run of the application - assertUtil.debug("Attribution file not found"); + private void checkForegroundTimerFired(boolean timerFired) { + // timer fired + if (timerFired) { + assertUtil.verbose("Foreground timer fired"); } else { - assertUtil.debug("Read Attribution: " + readAttribution); + assertUtil.notInVerbose("Foreground timer fired"); } + } - if (readActivityState == null) { - // test that the activity state file did not exist in the first run of the application - assertUtil.debug("Activity state file not found"); - } else { - assertUtil.debug("Read Activity state: " + readActivityState); + private void checkSendFirstPackages(boolean delayStart, + ActivityHandler.InternalState internalState, + boolean activityStateCreated, + boolean pausing) + { + if (!delayStart) { + assertUtil.info("Start delay expired or never configured"); + // did not update package + assertUtil.notInTest("PackageHandler updatePackages"); + return; } - } + assertUtil.notInInfo("Start delay expired or never configured"); - private void checkHandlerStatus(Boolean pausing) { - checkHandlerStatus(pausing, false); - } + // update packages + checkUpdatePackages(internalState, activityStateCreated); + // no longer is in delay start + assertUtil.isFalse(internalState.delayStart); - private void checkHandlerStatus(Boolean pausing, boolean eventBufferingEnabled) { - if (pausing == null) { - assertUtil.notInTest("AttributionHandler pauseSending"); - assertUtil.notInTest("PackageHandler pauseSending"); - assertUtil.notInTest("SdkClickHandler pauseSending"); - assertUtil.notInTest("AttributionHandler resumeSending"); - assertUtil.notInTest("PackageHandler resumeSending"); - assertUtil.notInTest("SdkClickHandler resumeSending"); - return; - } - if (pausing) { - assertUtil.test("AttributionHandler pauseSending"); - assertUtil.test("PackageHandler pauseSending"); - assertUtil.test("SdkClickHandler pauseSending"); + // cancel timer + assertUtil.verbose("Delay Start timer canceled"); + + checkHandlerStatus(pausing, false, false); + } + + private void checkUpdatePackages(ActivityHandler.InternalState internalState, + boolean activityStateCreated) { + // update packages + assertUtil.test("PackageHandler updatePackages"); + assertUtil.isFalse(internalState.updatePackages); + if (activityStateCreated) { + assertUtil.debug("Wrote Activity state"); } else { - assertUtil.test("AttributionHandler resumeSending"); - assertUtil.test("PackageHandler resumeSending"); - assertUtil.test("SdkClickHandler resumeSending"); - if (!eventBufferingEnabled) { - assertUtil.test("PackageHandler sendFirstPackage"); - } + assertUtil.notInDebug("Wrote Activity state"); } + } } diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestActivityPackage.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/TestActivityPackage.java similarity index 91% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestActivityPackage.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/TestActivityPackage.java index 25beb5e57..3969bc27e 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestActivityPackage.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/TestActivityPackage.java @@ -1,4 +1,4 @@ -package com.adjust.sdk.test; +package com.adjust.sdk; import com.adjust.sdk.ActivityKind; import com.adjust.sdk.ActivityPackage; @@ -37,6 +37,8 @@ public class TestActivityPackage { public String currency; public String callbackParams; public String partnerParams; + public Map savedCallbackParameters; + public Map savedPartnerParameters; // click public String reftag; public String otherParameters; @@ -51,7 +53,7 @@ public TestActivityPackage(ActivityPackage activityPackage) { // default values appToken = "123456789012"; environment = "sandbox"; - clientSdk = "android4.7.0"; + clientSdk = "android4.10.0"; suffix = ""; attribution = new AdjustAttribution(); playServices = true; @@ -76,6 +78,10 @@ public void testSessionPackage(int sessionCount) { } // default_tracker assertParameterEquals("default_tracker", defaultTracker); + // callback_params + assertJsonParameterEquals("callback_params", callbackParams); + // partner_params + assertJsonParameterEquals("partner_params", partnerParams); } public void testEventPackage(String eventToken) { @@ -114,6 +120,23 @@ public void testEventPackage(String eventToken) { assertJsonParameterEquals("callback_params", callbackParams); // partner_params assertJsonParameterEquals("partner_params", partnerParams); + + // saved callback parameters + if (savedCallbackParameters == null) { + Assert.assertNull(activityPackage.getExtendedString(), + activityPackage.getCallbackParameters()); + } else { + Assert.assertTrue(activityPackage.getExtendedString(), + savedCallbackParameters.equals(activityPackage.getCallbackParameters())); + } + // saved partner parameters + if (savedPartnerParameters == null) { + Assert.assertNull(activityPackage.getExtendedString(), + activityPackage.getPartnerParameters()); + } else { + Assert.assertTrue(activityPackage.getExtendedString(), + savedPartnerParameters.equals(activityPackage.getPartnerParameters())); + } } public void testClickPackage(String source) { diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestAttributionHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/TestAttributionHandler.java similarity index 79% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestAttributionHandler.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/TestAttributionHandler.java index 3373c9b90..e6782666e 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestAttributionHandler.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/TestAttributionHandler.java @@ -1,44 +1,43 @@ -package com.adjust.sdk.test; +package com.adjust.sdk; import android.content.Context; +import android.net.Uri; import android.os.SystemClock; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; -import com.adjust.sdk.ActivityHandler; -import com.adjust.sdk.ActivityPackage; -import com.adjust.sdk.AdjustConfig; -import com.adjust.sdk.AdjustFactory; -import com.adjust.sdk.AttributionHandler; -import com.adjust.sdk.ResponseData; -import com.adjust.sdk.SessionResponseData; +import com.adjust.sdk.test.*; import org.json.JSONException; import org.json.JSONObject; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; /** * Created by pfms on 28/01/15. */ -public class TestAttributionHandler extends ActivityInstrumentationTestCase2 { +@RunWith(AndroidJUnit4.class) +@LargeTest +public class TestAttributionHandler{ private MockLogger mockLogger; private MockActivityHandler mockActivityHandler; private MockHttpsURLConnection mockHttpsURLConnection; private AssertUtil assertUtil; - private UnitTestActivity activity; + private com.adjust.sdk.test.UnitTestActivity activity; private Context context; private ActivityPackage attributionPackage; private ActivityPackage firstSessionPackage; - public TestAttributionHandler() { - super(UnitTestActivity.class); - } - - public TestAttributionHandler(Class activityClass) { - super(activityClass); - } + @Rule + public ActivityTestRule mActivityRule = new ActivityTestRule(com.adjust.sdk.test.UnitTestActivity.class); - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() { mockLogger = new MockLogger(); mockActivityHandler = new MockActivityHandler(mockLogger); mockHttpsURLConnection = new MockHttpsURLConnection(null, mockLogger); @@ -49,7 +48,7 @@ protected void setUp() throws Exception { AdjustFactory.setActivityHandler(mockActivityHandler); AdjustFactory.setHttpsURLConnection(mockHttpsURLConnection); - activity = getActivity(); + activity = mActivityRule.getActivity(); context = activity.getApplicationContext(); savePackages(); @@ -80,7 +79,7 @@ private void savePackages() { SystemClock.sleep(3000); - ActivityPackage attributionPackage = activityHandler.getAttributionPackage(); + ActivityPackage attributionPackage = activityHandler.getAttributionPackageI(); TestActivityPackage attributionPackageTest = new TestActivityPackage(attributionPackage); @@ -91,10 +90,8 @@ private void savePackages() { this.attributionPackage = attributionPackage; } - @Override - protected void tearDown() throws Exception { - super.tearDown(); - + @After + public void tearDown() { AdjustFactory.setHttpsURLConnection(null); AdjustFactory.setActivityHandler(null); AdjustFactory.setLogger(null); @@ -103,6 +100,7 @@ protected void tearDown() throws Exception { context = null; } + @Test public void testGetAttribution() { // assert test name to read better in logcat mockLogger.Assert("TestAttributionHandler testGetAttribution"); @@ -129,6 +127,7 @@ public void testGetAttribution() { okMessageTest(attributionHandler); } + @Test public void testCheckSessionResponse() { // assert test name to read better in logcat mockLogger.Assert("TestAttributionHandler testCheckSessionResponse"); @@ -148,7 +147,7 @@ public void testCheckSessionResponse() { "\"creative\" : \"ctValue\" , " + "\"click_label\" : \"clValue\" }"); } catch (JSONException e) { - fail(e.getMessage()); + assertUtil.fail(e.getMessage()); } SessionResponseData sessionResponseData = (SessionResponseData) ResponseData.buildResponseData(firstSessionPackage); @@ -171,6 +170,7 @@ public void testCheckSessionResponse() { assertUtil.test("ActivityHandler launchSessionResponseTasks, message:null timestamp:null json:{\"tracker_token\":\"ttValue\",\"tracker_name\":\"tnValue\",\"network\":\"nValue\",\"campaign\":\"cpValue\",\"adgroup\":\"aValue\",\"creative\":\"ctValue\",\"click_label\":\"clValue\"}"); } + @Test public void testAskIn() { // assert test name to read better in logcat mockLogger.Assert("TestAttributionHandler testAskIn"); @@ -184,7 +184,7 @@ public void testAskIn() { try { askIn4sJson = new JSONObject("{ \"ask_in\" : 4000 }"); } catch (JSONException e) { - fail(e.getMessage()); + assertUtil.fail(e.getMessage()); } mockHttpsURLConnection.responseType = ResponseType.MESSAGE; @@ -215,7 +215,7 @@ public void testAskIn() { try { askIn5sJson = new JSONObject("{ \"ask_in\" : 5000 }"); } catch (JSONException e) { - fail(e.getMessage()); + assertUtil.fail(e.getMessage()); } sessionResponseData.jsonResponse = askIn5sJson; @@ -242,6 +242,7 @@ public void testAskIn() { //requestTest(mockHttpClient.lastRequest); } + @Test public void testPause() { // assert test name to read better in logcat mockLogger.Assert("TestAttributionHandler testPause"); @@ -264,6 +265,7 @@ public void testPause() { assertUtil.notInTest("MockHttpsURLConnection getInputStream"); } + @Test public void testWithoutListener() { // assert test name to read better in logcat mockLogger.Assert("TestAttributionHandler testPause"); @@ -286,6 +288,54 @@ public void testWithoutListener() { assertUtil.notInTest("MockHttpsURLConnection getInputStream"); } + @Test + public void testDeeplink() { + // assert test name to read better in logcat + mockLogger.Assert("TestAttributionHandler testDeeplink"); + + AttributionHandler attributionHandler = new AttributionHandler(mockActivityHandler, + attributionPackage, true, true); + + JSONObject responseJson = new JSONObject(); + + SessionResponseData sessionResponseDeeplink = (SessionResponseData) ResponseData.buildResponseData(firstSessionPackage); + try { + JSONObject internalAttributionJson = new JSONObject(); + internalAttributionJson.put("deeplink", "testDeeplinkAttribution://"); + + responseJson.put("deeplink", "testDeeplinkRoot://"); + responseJson.put("attribution", internalAttributionJson); + + //sessionResponseDeeplink.jsonResponse = new JSONObject("{ " + + // "\"deeplink\" : \"testDeeplinkRoot://\" }"); + + } catch (JSONException e) { + assertUtil.fail(e.getMessage()); + } + + sessionResponseDeeplink.jsonResponse = responseJson; + attributionHandler.checkSessionResponse(sessionResponseDeeplink); + SystemClock.sleep(2000); + + assertUtil.test("ActivityHandler setAskingAttribution, false"); + + assertUtil.test("ActivityHandler launchSessionResponseTasks, message:null timestamp:null " + + "json:{\"deeplink\":\"testDeeplinkRoot:\\/\\/\",\"attribution\":{\"deeplink\":\"testDeeplinkAttribution:\\/\\/\"}}"); + + AttributionResponseData attributionResponseDeeplink = (AttributionResponseData)ResponseData.buildResponseData(attributionPackage); + + attributionResponseDeeplink.jsonResponse = responseJson; + attributionHandler.checkAttributionResponse(attributionResponseDeeplink); + SystemClock.sleep(2000); + + assertUtil.test("ActivityHandler setAskingAttribution, false"); + + assertUtil.test("ActivityHandler launchAttributionResponseTasks, message:null timestamp:null " + + "json:{\"deeplink\":\"testDeeplinkRoot:\\/\\/\",\"attribution\":{\"deeplink\":\"testDeeplinkAttribution:\\/\\/\"}}"); + + assertUtil.isEqual(attributionResponseDeeplink.deeplink, Uri.parse("testDeeplinkAttribution://")); + } + private void nullClientTest(AttributionHandler attributionHandler) { startGetAttributionTest(attributionHandler, null); diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestPackageHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/TestPackageHandler.java similarity index 59% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestPackageHandler.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/TestPackageHandler.java index f8963aae0..7028df020 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestPackageHandler.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/TestPackageHandler.java @@ -1,44 +1,44 @@ -package com.adjust.sdk.test; +package com.adjust.sdk; import android.content.Context; import android.os.SystemClock; -import android.test.ActivityInstrumentationTestCase2; - -import com.adjust.sdk.ActivityKind; -import com.adjust.sdk.ActivityPackage; -import com.adjust.sdk.AdjustFactory; -import com.adjust.sdk.BackoffStrategy; -import com.adjust.sdk.Constants; -import com.adjust.sdk.PackageHandler; -import com.adjust.sdk.ResponseData; -import com.adjust.sdk.UnknownResponseData; - +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.LargeTest; + +import com.adjust.sdk.test.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * Created by pfms on 30/01/15. + * Created by pfms on 22/08/2016. */ -public class TestPackageHandler extends ActivityInstrumentationTestCase2 { +@RunWith(AndroidJUnit4.class) +@LargeTest +public class TestPackageHandler { private MockLogger mockLogger; private MockActivityHandler mockActivityHandler; protected MockRequestHandler mockRequestHandler; private AssertUtil assertUtil; - private UnitTestActivity activity; + private com.adjust.sdk.test.UnitTestActivity activity; private Context context; + @Rule + public ActivityTestRule mActivityRule = new ActivityTestRule(com.adjust.sdk.test.UnitTestActivity.class); - public TestPackageHandler() { - super(UnitTestActivity.class); - } - - public TestPackageHandler(Class activityClass) { - super(activityClass); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() { mockLogger = new MockLogger(); mockActivityHandler = new MockActivityHandler(mockLogger); mockRequestHandler = new MockRequestHandler(mockLogger); @@ -48,18 +48,17 @@ protected void setUp() throws Exception { AdjustFactory.setLogger(mockLogger); AdjustFactory.setRequestHandler(mockRequestHandler); - activity = getActivity(); + activity = mActivityRule.getActivity(); context = activity.getApplicationContext(); } - @Override - protected void tearDown() throws Exception { - super.tearDown(); - + @After + public void tearDown() { AdjustFactory.setRequestHandler(null); AdjustFactory.setLogger(null); } + @Test public void testAddPackage() { // assert test name to read better in logcat mockLogger.Assert("TestPackageHandler testAddPackage"); @@ -106,6 +105,7 @@ public void testAddPackage() { assertUtil.test("RequestHandler sendPackage, queueSize 0"); } + @Test public void testSendFirst() { // assert test name to read better in logcat mockLogger.Assert("TestPackageHandler testSendFirst"); @@ -149,6 +149,7 @@ public void testSendFirst() { sendFirstTests(SendFirstState.SEND, "unknownFirstPackage", 0); } + @Test public void testSendNext() { // assert test name to read better in logcat mockLogger.Assert("TestPackageHandler testSendNext"); @@ -178,6 +179,7 @@ public void testSendNext() { sendFirstTests(SendFirstState.SEND, "unknownSecondPackage", 0); } + @Test public void testCloseFirstPackage() { // assert test name to read better in logcat mockLogger.Assert("TestPackageHandler testCloseFirstPackage"); @@ -198,8 +200,9 @@ public void testCloseFirstPackage() { UnknownResponseData unknownResponseData = (UnknownResponseData) ResponseData.buildResponseData(activityPackage); packageHandler.closeFirstPackage(unknownResponseData, null); SystemClock.sleep(2000); - assertUtil.verbose("Package handler can send"); + assertUtil.test("ActivityHandler finishedTrackingActivity, message:null timestamp:null json:null"); + assertUtil.verbose("Package handler can send"); assertUtil.notInDebug("Package handler wrote"); @@ -207,6 +210,7 @@ public void testCloseFirstPackage() { sendFirstTests(SendFirstState.SEND, "unknownFirstPackage", 0); } + @Test public void testBackoffJitter() { // assert test name to read better in logcat mockLogger.Assert("TestPackageHandler testBackoffJitter"); @@ -217,14 +221,14 @@ public void testBackoffJitter() { ActivityPackage activityPackage = new ActivityPackage(ActivityKind.UNKNOWN); UnknownResponseData unknownResponseData = (UnknownResponseData) ResponseData.buildResponseData(activityPackage); - Pattern pattern = Pattern.compile("Sleeping for (\\d+\\.\\d) seconds before retrying the (\\d+) time"); + Pattern pattern = Pattern.compile("Waiting for (\\d+\\.\\d) seconds before retrying the (\\d+) time"); // 1st packageHandler.closeFirstPackage(unknownResponseData, activityPackage); SystemClock.sleep(1500); - String matchingString = assertUtil.verbose("Sleeping for "); - // Sleeping for 0.1 seconds before retrying the 1 time + String matchingString = assertUtil.verbose("Waiting for "); + // Waiting for 0.1 seconds before retrying the 1 time checkSleeping(pattern, matchingString, 0.1, 0.2, 1, 0.5, 1); @@ -232,7 +236,7 @@ public void testBackoffJitter() { packageHandler.closeFirstPackage(unknownResponseData, activityPackage); SystemClock.sleep(1500); - matchingString = assertUtil.verbose("Sleeping for "); + matchingString = assertUtil.verbose("Waiting for "); checkSleeping(pattern, matchingString, 0.2, 0.4, 1, 0.5, 2); @@ -240,7 +244,7 @@ public void testBackoffJitter() { packageHandler.closeFirstPackage(unknownResponseData, activityPackage); SystemClock.sleep(1500); - matchingString = assertUtil.verbose("Sleeping for "); + matchingString = assertUtil.verbose("Waiting for "); checkSleeping(pattern, matchingString, 0.4, 0.8, 1, 0.5, 3); @@ -248,7 +252,7 @@ public void testBackoffJitter() { packageHandler.closeFirstPackage(unknownResponseData, activityPackage); SystemClock.sleep(1500); - matchingString = assertUtil.verbose("Sleeping for "); + matchingString = assertUtil.verbose("Waiting for "); checkSleeping(pattern, matchingString, 0.8, 1.6, 1, 0.5, 4); @@ -256,7 +260,7 @@ public void testBackoffJitter() { packageHandler.closeFirstPackage(unknownResponseData, activityPackage); SystemClock.sleep(1500); - matchingString = assertUtil.verbose("Sleeping for "); + matchingString = assertUtil.verbose("Waiting for "); checkSleeping(pattern, matchingString, 1.6, 3.2, 1, 0.5, 5); @@ -264,10 +268,177 @@ public void testBackoffJitter() { packageHandler.closeFirstPackage(unknownResponseData, activityPackage); SystemClock.sleep(1500); - matchingString = assertUtil.verbose("Sleeping for "); + matchingString = assertUtil.verbose("Waiting for "); checkSleeping(pattern, matchingString, 6.4, 12.8, 1, 0.5, 6); - } + } + + @Test + public void testUpdate() { + // assert test name to read better in logcat + mockLogger.Assert("TestPackageHandler testUpdate"); + + List delayPackages = createDelayPackages(); + + ActivityPackage firstSessionPackage = delayPackages.get(0); + ActivityPackage firstEventPackage = delayPackages.get(1); + ActivityPackage secondEventPackage = delayPackages.get(2); + + // create event package test + TestActivityPackage testFirstSessionPackage = new TestActivityPackage(firstSessionPackage); + + testFirstSessionPackage.testSessionPackage(1); + + // create event package test + TestActivityPackage testFirstEventPackage = new TestActivityPackage(firstEventPackage); + + // set event test parameters + testFirstEventPackage.eventCount = "1"; + testFirstEventPackage.suffix = "'event1'"; + testFirstEventPackage.savedCallbackParameters = new HashMap(1); + testFirstEventPackage.savedCallbackParameters.put("ceFoo", "ceBar"); + testFirstEventPackage.savedPartnerParameters = new HashMap(1); + testFirstEventPackage.savedPartnerParameters.put("peFoo", "peBar"); + + // test first event + testFirstEventPackage.testEventPackage("event1"); + + // create event package test + TestActivityPackage testSecondEventPackage = new TestActivityPackage(secondEventPackage); + + // set event test parameters + testSecondEventPackage.eventCount = "2"; + testSecondEventPackage.suffix = "'event2'"; + testSecondEventPackage.savedCallbackParameters = new HashMap(1); + testSecondEventPackage.savedCallbackParameters.put("scpKey", "ceBar"); + testSecondEventPackage.savedPartnerParameters = new HashMap(1); + testSecondEventPackage.savedPartnerParameters.put("sppKey", "peBar"); + + // test second event + testSecondEventPackage.testEventPackage("event2"); + + // initialize Package Handler + IPackageHandler packageHandler = startPackageHandler(); + + sendFirstTests(SendFirstState.EMPTY_QUEUE, null, null); + + packageHandler.addPackage(firstSessionPackage); + packageHandler.addPackage(firstEventPackage); + packageHandler.addPackage(secondEventPackage); + + SystemClock.sleep(1000); + + addPackageTests(1, "session"); + + addPackageTests(2, "event'event1'"); + + addPackageTests(3, "event'event2'"); + + packageHandler.updatePackages(null); + + SystemClock.sleep(1000); + + assertUtil.notInDebug("Updating package handler queue"); + + SessionParameters emptySessionParameters = new SessionParameters(); + packageHandler.updatePackages(emptySessionParameters); + + SystemClock.sleep(1000); + + assertUtil.debug("Updating package handler queue"); + + assertUtil.verbose("Session callback parameters: null"); + assertUtil.verbose("Session partner parameters: null"); + + // writes the non-updated packages + assertUtil.debug("Wrote Package queue: [session, event'event1', event'event2']"); + assertUtil.debug("Package handler wrote 3 packages"); + + SessionParameters sessionParameters = new SessionParameters(); + sessionParameters.callbackParameters = new HashMap(1); + sessionParameters.callbackParameters.put("scpKey", "scpValue"); + sessionParameters.partnerParameters = new HashMap(1); + sessionParameters.partnerParameters.put("sppKey", "sppValue"); + + packageHandler.updatePackages(sessionParameters); + + SystemClock.sleep(1000); + + assertUtil.debug("Updating package handler queue"); + + assertUtil.verbose("Session callback parameters: {scpKey=scpValue}"); + assertUtil.verbose("Session partner parameters: {sppKey=sppValue}"); + + assertUtil.warn("Key scpKey with value scpValue from Callback parameter was replaced by value ceBar"); + assertUtil.warn("Key sppKey with value sppValue from Partner parameter was replaced by value peBar"); + assertUtil.debug("Package handler wrote 3 packages"); + + testFirstSessionPackage.callbackParams = "{scpKey=scpValue}"; + testFirstSessionPackage.partnerParams = "{sppKey=sppValue}"; + testFirstSessionPackage.testSessionPackage(1); + + testFirstEventPackage.callbackParams = "{scpKey=scpValue, ceFoo=ceBar}"; + testFirstEventPackage.partnerParams = "{sppKey=sppValue, peFoo=peBar}"; + + testFirstEventPackage.testEventPackage("event1"); + + testSecondEventPackage.callbackParams = "{scpKey=ceBar}"; + testSecondEventPackage.partnerParams = "{sppKey=peBar}"; + + testSecondEventPackage.testEventPackage("event2"); + } + + private List createDelayPackages() { + MockPackageHandler mockPackageHandler = new MockPackageHandler(mockLogger); + AdjustFactory.setPackageHandler(mockPackageHandler); + + MockSdkClickHandler mockSdkClickHandler = new MockSdkClickHandler(mockLogger); + AdjustFactory.setSdkClickHandler(mockSdkClickHandler); + + MockAttributionHandler mockAttributionHandler = new MockAttributionHandler(mockLogger); + AdjustFactory.setAttributionHandler(mockAttributionHandler); + + AdjustFactory.setSessionInterval(-1); + AdjustFactory.setSubsessionInterval(-1); + AdjustFactory.setTimerInterval(-1); + AdjustFactory.setTimerStart(-1); + + ActivityHandler.deleteActivityState(context); + ActivityHandler.deleteAttribution(context); + ActivityHandler.deleteSessionCallbackParameters(context); + ActivityHandler.deleteSessionPartnerParameters(context); + + AdjustConfig config = new AdjustConfig(context, "123456789012", "sandbox"); + + config.setDelayStart(4); + + IActivityHandler activityHandler = ActivityHandler.getInstance(config); + + activityHandler.addSessionCallbackParameter("scpKey", "scpValue"); + activityHandler.addSessionPartnerParameter("sppKey", "sppValue"); + + activityHandler.onResume(); + + AdjustEvent firstEvent = new AdjustEvent("event1"); + firstEvent.addCallbackParameter("ceFoo", "ceBar"); + firstEvent.addPartnerParameter("peFoo", "peBar"); + activityHandler.trackEvent(firstEvent); + + AdjustEvent secondEvent = new AdjustEvent("event2"); + secondEvent.addCallbackParameter("scpKey", "ceBar"); + secondEvent.addPartnerParameter("sppKey", "peBar"); + activityHandler.trackEvent(secondEvent); + + SystemClock.sleep(3000); + + ActivityPackage firstSessionPackage = mockPackageHandler.queue.get(0); + ActivityPackage firstEventPackage = mockPackageHandler.queue.get(1); + ActivityPackage secondEventPackage = mockPackageHandler.queue.get(2); + + mockLogger.reset(); + + return Arrays.asList(firstSessionPackage, firstEventPackage, secondEventPackage); + } private void checkSleeping(Pattern pattern, String sleepingLog, @@ -406,4 +577,5 @@ private ActivityPackage createClickPackage(String suffix) { return activityPackage; } + } diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestRequestHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/TestRequestHandler.java similarity index 87% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestRequestHandler.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/TestRequestHandler.java index 49fcbf3be..a501e37f3 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestRequestHandler.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/TestRequestHandler.java @@ -1,40 +1,41 @@ -package com.adjust.sdk.test; +package com.adjust.sdk; import android.content.Context; import android.os.SystemClock; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; -import com.adjust.sdk.ActivityHandler; -import com.adjust.sdk.ActivityPackage; -import com.adjust.sdk.AdjustConfig; -import com.adjust.sdk.AdjustFactory; -import com.adjust.sdk.RequestHandler; +import com.adjust.sdk.test.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; /** * Created by pfms on 30/01/15. */ -public class TestRequestHandler extends ActivityInstrumentationTestCase2 { +@RunWith(AndroidJUnit4.class) +@LargeTest +public class TestRequestHandler { private MockLogger mockLogger; private MockPackageHandler mockPackageHandler; private MockHttpsURLConnection mockHttpsURLConnection; private AssertUtil assertUtil; - private UnitTestActivity activity; + private com.adjust.sdk.test.UnitTestActivity activity; private Context context; private ActivityPackage sessionPackage; private RequestHandler requestHandler; - public TestRequestHandler() { - super(UnitTestActivity.class); - } + @Rule + public ActivityTestRule mActivityRule = new ActivityTestRule(com.adjust.sdk.test.UnitTestActivity.class); - public TestRequestHandler(Class activityClass) { - super(activityClass); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { mockLogger = new MockLogger(); mockPackageHandler = new MockPackageHandler(mockLogger); mockHttpsURLConnection = new MockHttpsURLConnection(null, mockLogger); @@ -45,21 +46,20 @@ protected void setUp() throws Exception { AdjustFactory.setPackageHandler(mockPackageHandler); AdjustFactory.setHttpsURLConnection(mockHttpsURLConnection); - activity = getActivity(); + activity = mActivityRule.getActivity(); context = activity.getApplicationContext(); sessionPackage = getSessionPackage(); } - @Override - protected void tearDown() throws Exception { - super.tearDown(); - + @After + public void tearDown() throws Exception { AdjustFactory.setHttpsURLConnection(null); AdjustFactory.setPackageHandler(null); AdjustFactory.setLogger(null); } + @Test public void testSend() { // assert test name to read better in logcat mockLogger.Assert("TestRequestHandler testSend"); @@ -106,9 +106,9 @@ private void nullResponseTest() { assertUtil.test("MockHttpsURLConnection getInputStream, responseType: null"); - assertUtil.error("Failed to read response. (null)"); + assertUtil.error("Failed to read response. (lock == null)"); - assertUtil.error("Failed to track session. (Runtime exception: java.lang.NullPointerException)"); + assertUtil.error("Failed to track session. (Runtime exception: java.lang.NullPointerException: lock == null)"); assertUtil.test("PackageHandler sendNextPackage"); } diff --git a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestSdkClickHandler.java b/Adjust/test/src/androidTest/java/com/adjust/sdk/TestSdkClickHandler.java similarity index 91% rename from Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestSdkClickHandler.java rename to Adjust/test/src/androidTest/java/com/adjust/sdk/TestSdkClickHandler.java index a95f938c2..5d55c5e86 100644 --- a/Adjust/test/src/androidTest/java/com/adjust/sdk/test/TestSdkClickHandler.java +++ b/Adjust/test/src/androidTest/java/com/adjust/sdk/TestSdkClickHandler.java @@ -1,39 +1,39 @@ -package com.adjust.sdk.test; +package com.adjust.sdk; import android.content.Context; import android.net.Uri; import android.os.SystemClock; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; -import com.adjust.sdk.ActivityHandler; -import com.adjust.sdk.ActivityPackage; -import com.adjust.sdk.AdjustConfig; -import com.adjust.sdk.AdjustFactory; -import com.adjust.sdk.BackoffStrategy; -import com.adjust.sdk.SdkClickHandler; +import com.adjust.sdk.test.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; /** * Created by pfms on 14/04/16. */ -public class TestSdkClickHandler extends ActivityInstrumentationTestCase2 { +@RunWith(AndroidJUnit4.class) +@LargeTest +public class TestSdkClickHandler { private MockLogger mockLogger; private AssertUtil assertUtil; private MockHttpsURLConnection mockHttpsURLConnection; - private UnitTestActivity activity; + private com.adjust.sdk.test.UnitTestActivity activity; private Context context; private ActivityPackage sdkClickPackage; - public TestSdkClickHandler() { - super(UnitTestActivity.class); - } + @Rule + public ActivityTestRule mActivityRule = new ActivityTestRule(com.adjust.sdk.test.UnitTestActivity.class); - public TestSdkClickHandler(Class activityClass) { - super(activityClass); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() { mockLogger = new MockLogger(); mockHttpsURLConnection = new MockHttpsURLConnection(null, mockLogger); @@ -42,15 +42,13 @@ protected void setUp() throws Exception { AdjustFactory.setLogger(mockLogger); AdjustFactory.setHttpsURLConnection(mockHttpsURLConnection); - activity = getActivity(); + activity = mActivityRule.getActivity(); context = activity.getApplicationContext(); sdkClickPackage = getClickPackage(); } - @Override - protected void tearDown() throws Exception { - super.tearDown(); - + @After + public void tearDown() { AdjustFactory.setHttpsURLConnection(null); AdjustFactory.setLogger(null); } @@ -79,6 +77,7 @@ private ActivityPackage getClickPackage() { return sdkClickPackage; } + @Test public void testPaused() { sdkClickPackage.setClientSdk("Test-First-Click"); ActivityPackage secondSdkClickPackage = getClickPackage(); @@ -183,6 +182,7 @@ private void checkSendSecondPackage(SdkClickHandler sdkClickHandler, int retries assertUtil.notInTest("MockHttpsURLConnection setRequestProperty"); } + @Test public void testNullResponse() { // assert test name to read better in logcat mockLogger.Assert("TestRequestHandler testNullResponse"); @@ -201,15 +201,16 @@ public void testNullResponse() { assertUtil.test("MockHttpsURLConnection getInputStream, responseType: null"); - assertUtil.error("Failed to read response. (null)"); + assertUtil.error("Failed to read response. (lock == null)"); - assertUtil.error("Failed to track click. (Sdk_click runtime exception: java.lang.NullPointerException)"); + assertUtil.error("Failed to track click. (Sdk_click runtime exception: java.lang.NullPointerException: lock == null)"); // does not to try to retry assertUtil.notInError("Retrying sdk_click package for the"); assertUtil.notInDebug("Added sdk_click"); } + @Test public void testClientException() { // assert test name to read better in logcat mockLogger.Assert("TestRequestHandler testClientException"); @@ -234,6 +235,7 @@ public void testClientException() { assertUtil.debug("Added sdk_click"); } + @Test public void testServerError() { // assert test name to read better in logcat mockLogger.Assert("TestRequestHandler testServerError"); @@ -254,6 +256,7 @@ public void testServerError() { assertUtil.error("testResponseError"); } + @Test public void testWrongJson() { // assert test name to read better in logcat mockLogger.Assert("TestRequestHandler testWrongJson"); @@ -274,6 +277,7 @@ public void testWrongJson() { assertUtil.error("Failed to parse json response. (Value not of type java.lang.String cannot be converted to JSONObject)"); } + @Test public void testEmptyJson() { // assert test name to read better in logcat mockLogger.Assert("TestRequestHandler testWrongJson"); @@ -294,6 +298,7 @@ public void testEmptyJson() { assertUtil.info("No message found"); } + @Test public void testMessage() { // assert test name to read better in logcat mockLogger.Assert("TestRequestHandler testWrongJson"); @@ -307,7 +312,7 @@ public void testMessage() { sdkClickHandler.sendSdkClick(sdkClickPackage); SystemClock.sleep(1000); - TestActivityPackage.testQueryStringRequest(mockHttpsURLConnection.readRequest(), 0); + TestActivityPackage.testQueryStringRequest(mockHttpsURLConnection.readRequest(), null); assertUtil.test("MockHttpsURLConnection getInputStream, responseType: MESSAGE"); diff --git a/Adjust/test/src/main/AndroidManifest.xml b/Adjust/test/src/main/AndroidManifest.xml index 966df7ebc..31e46c8f2 100644 --- a/Adjust/test/src/main/AndroidManifest.xml +++ b/Adjust/test/src/main/AndroidManifest.xml @@ -17,6 +17,18 @@ + + + + + + + + + diff --git a/README.md b/README.md index 79d721954..b170bbd6c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This is the Android SDK of adjust™. You can read more about adjust™ at [adjust.com]. -If your app is an app which uses web views and you would like to use adjust tracking from Javascript code, please consult +If your app is an app which uses web views and you would like to use adjust tracking from Javascript code, please consult our [Android web views SDK guide](doc/web_views.md). ## Table of contents @@ -29,6 +29,11 @@ our [Android web views SDK guide](doc/web_views.md). * [In-App Purchase verification](#iap-verification) * [Callback parameters](#callback-parameters) * [Partner parameters](#partner-parameters) + * [Session parameters](#session-parameters) + * [Session callback parameters](#session-callback-parameters) + * [Session partner parameters](#session-partner-parameters) + * [External device id](#external-device-id) + * [Delay start](#delay-start) * [Attribution callback](#attribution-callback) * [Session and event callbacks](#session-event-callbacks) * [Disable tracking](#disable-tracking) @@ -36,6 +41,7 @@ our [Android web views SDK guide](doc/web_views.md). * [Event buffering](#event-buffering) * [Background tracking](#background-tracking) * [Device IDs](#device-ids) + * [Push token](#push-token) * [Pre-installed trackers](#pre-installed-trackers) * [Deep linking](#deeplinking) * [Standard deep linking scenario](#deeplinking-standard) @@ -49,7 +55,7 @@ our [Android web views SDK guide](doc/web_views.md). ## Example app -There is an example app inside the [`example` directory][example]. You can open the Android project to see an example on +There is an example app inside the [`example` directory][example]. You can open the Android project to see an example on how the adjust SDK can be integrated. ## Basic integration @@ -69,7 +75,7 @@ In the Android Studio menu select `File → Import Module...`. ![][import_module] -In the `Source directory` field, locate the folder you extracted in step 1. Select and choose the folder +In the `Source directory` field, locate the folder you extracted in step 1. Select and choose the folder `./android_sdk/Adjust/adjust`. Make sure the module name `:adjust` appears before finishing. ![][select_module] @@ -91,19 +97,19 @@ compile project(":adjust") If you are using Maven, add this line instead: ``` -compile 'com.adjust.sdk:adjust-android:4.7.0' +compile 'com.adjust.sdk:adjust-android:4.10.0' ``` ### Add Google Play Services -Since the 1st of August of 2014, apps in the Google Play Store must use the [Google Advertising ID][google_ad_id] to -uniquely identify devices. To allow the adjust SDK to use the Google Advertising ID, you must integrate the +Since the 1st of August of 2014, apps in the Google Play Store must use the [Google Advertising ID][google_ad_id] to +uniquely identify devices. To allow the adjust SDK to use the Google Advertising ID, you must integrate the [Google Play Services][google_play_services]. If you haven't done this yet, follow these steps: 1. Open the `build.gradle` file of your app and find the `dependencies` block. Add the following line: ``` - compile 'com.google.android.gms:play-services-analytics:8.4.0' + compile 'com.google.android.gms:play-services-analytics:9.2.1' ``` ![][gradle_gps] @@ -122,7 +128,7 @@ uniquely identify devices. To allow the adjust SDK to use the Google Advertising ### Add permissions -In the Package Explorer open the `AndroidManifest.xml` of your Android project. Add the `uses-permission` tag for +In the Package Explorer open the `AndroidManifest.xml` of your Android project. Add the `uses-permission` tag for `INTERNET` if it's not present already. ```xml @@ -147,14 +153,14 @@ If you are using Proguard, add these lines to your Proguard file: public static **[] values(); public static ** valueOf(java.lang.String); } --keep class com.adjust.sdk.plugin.MacAddressUtil { - java.lang.String getMacAddress(android.content.Context); +-keep class com.adjust.sdk.plugin.MacAddressUtil { + java.lang.String getMacAddress(android.content.Context); } --keep class com.adjust.sdk.plugin.AndroidIdUtil { - java.lang.String getAndroidId(android.content.Context); +-keep class com.adjust.sdk.plugin.AndroidIdUtil { + java.lang.String getAndroidId(android.content.Context); } --keep class com.google.android.gms.common.ConnectionResult { - int SUCCESS; +-keep class com.google.android.gms.common.ConnectionResult { + int SUCCESS; } -keep class com.google.android.gms.ads.identifier.AdvertisingIdClient { com.google.android.gms.ads.identifier.AdvertisingIdClient$Info getAdvertisingIdInfo(android.content.Context); @@ -169,7 +175,7 @@ If you are **not targeting the Google Play Store**, you can remove the `com.goog ![][proguard] -**Important**: If you are using an `-overloadaggressively` flag in your Proguard file, then in order for the adjust SDK to +**Important**: If you are using an `-overloadaggressively` flag in your Proguard file, then in order for the adjust SDK to work properly you should consider one of two possible scenarios: * Remove `-overloadaggressively` if it is not necessary @@ -194,7 +200,7 @@ inside the `application` tag in your `AndroidManifest.xml`. We use this broadcast receiver to retrieve the install referrer, in order to improve conversion tracking. -If you are already using a different broadcast receiver for the `INSTALL_REFERRER` intent, follow +If you are already using a different broadcast receiver for the `INSTALL_REFERRER` intent, follow [these instructions][referrer] to add the Adjust broadcast receiver. ### Integrate the SDK into your app @@ -203,7 +209,7 @@ To start with, we'll set up basic session tracking. ### Basic setup -We recommend using a global android [Application][android_application] class to initialize the SDK. If you don't have one +We recommend using a global android [Application][android_application] class to initialize the SDK. If you don't have one in your app already, follow these steps: 1. Create a class that extends `Application`. @@ -223,18 +229,18 @@ in your app already, follow these steps: ![][manifest_application] -4. In your `Application` class find or create the `onCreate` method and add the following code to initialize the adjust +4. In your `Application` class find or create the `onCreate` method and add the following code to initialize the adjust SDK: ```java import com.adjust.sdk.Adjust; import com.adjust.sdk.AdjustConfig; - + public class GlobalApplication extends Application { @Override public void onCreate() { super.onCreate(); - + String appToken = "{YourAppToken}"; String environment = AdjustConfig.ENVIRONMENT_SANDBOX; AdjustConfig config = new AdjustConfig(this, appToken, environment); @@ -242,68 +248,68 @@ SDK: } } ``` - + ![][application_config] - + Replace `{YourAppToken}` with your app token. You can find this in your [dashboard]. - + Depending on whether you build your app for testing or for production, you must set `environment` with one of these values: - + ```java String environment = AdjustConfig.ENVIRONMENT_SANDBOX; String environment = AdjustConfig.ENVIRONMENT_PRODUCTION; ``` - + **Important:** This value should be set to `AdjustConfig.ENVIRONMENT_SANDBOX` if and only if you or someone else is testing your app. Make sure to set the environment to `AdjustConfig.ENVIRONMENT_PRODUCTION` just before you publish the app. Set it back to `AdjustConfig.ENVIRONMENT_SANDBOX` when you start developing and testing it again. - + We use this environment to distinguish between real traffic and test traffic from test devices. It is very important that you keep this value meaningful at all times! This is especially important if you are tracking revenue. ### Session tracking -**Note**: This step is **really important** and please **make sure that you implement it properly in your app**. By +**Note**: This step is **really important** and please **make sure that you implement it properly in your app**. By implementing it, you will enable proper session tracking by the adjust SDK in your app. #### API level 14 and higher -1. Add a private class that implements the `ActivityLifecycleCallbacks` interface. If you don't have access to this -interface, your app is targeting an Android API level inferior to 14. You will have to update manually each Activity by -following these [instructions](#session-tracking-api9). If you had `Adjust.onResume` and `Adjust.onPause` calls on each +1. Add a private class that implements the `ActivityLifecycleCallbacks` interface. If you don't have access to this +interface, your app is targeting an Android API level inferior to 14. You will have to update manually each Activity by +following these [instructions](#session-tracking-api9). If you had `Adjust.onResume` and `Adjust.onPause` calls on each Activity of your app before, you should remove them. ![][activity_lifecycle_class] -2. Edit the `onActivityResumed(Activity activity)` method and add a call to `Adjust.onResume()`. Edit the +2. Edit the `onActivityResumed(Activity activity)` method and add a call to `Adjust.onResume()`. Edit the `onActivityPaused(Activity activity)` method and add a call to `Adjust.onPause()`. ![][activity_lifecycle_methods] - -3. Add on the `onCreate()` method where the adjust SDK is configured and add call `registerActivityLifecycleCallbacks` + +3. Add on the `onCreate()` method where the adjust SDK is configured and add call `registerActivityLifecycleCallbacks` with an instance of the created `ActivityLifecycleCallbacks` class. ```java import com.adjust.sdk.Adjust; import com.adjust.sdk.AdjustConfig; - + public class GlobalApplication extends Application { @Override public void onCreate() { super.onCreate(); - + String appToken = "{YourAppToken}"; String environment = AdjustConfig.ENVIRONMENT_SANDBOX; AdjustConfig config = new AdjustConfig(this, appToken, environment); Adjust.onCreate(config); registerActivityLifecycleCallbacks(new AdjustLifecycleCallbacks()); - + //... } @@ -312,27 +318,27 @@ with an instance of the created `ActivityLifecycleCallbacks` class. public void onActivityResumed(Activity activity) { Adjust.onResume(); } - + @Override public void onActivityPaused(Activity activity) { Adjust.onPause(); } - + //... } } ``` - + ![][activity_lifecycle_register] #### API level 9 until 13 -If your app `minSdkVersion` in gradle is between `9` and `13`, consider updating it to at least `14` to simplify the +If your app `minSdkVersion` in gradle is between `9` and `13`, consider updating it to at least `14` to simplify the integration process in the long term. Consult the official Android [dashboard][android-dashboard] to know the latest market -share of the major versions. +share of the major versions. -To provide proper session tracking it is required to call certain adjust SDK 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 +To provide proper session tracking it is required to call certain adjust SDK 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**: 1. Open the source file of your Activity. @@ -362,12 +368,12 @@ public class YourActivity extends Activity { ![][activity] -Repeat these steps for **every** Activity of your app. Don't forget these steps when you create new Activities in the +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. ### Adjust Logging -You can increase or decrease the amount of logs you see in tests by calling `setLogLevel` on your `AdjustConfig` instance +You can increase or decrease the amount of logs that you see during testing by calling `setLogLevel` on your `AdjustConfig` instance with one of the following parameters: ```java @@ -381,7 +387,7 @@ config.setLogLevel(LogLevel.ASSERT); // disable errors as well ### Build your app -Build and run your Android app. In your `LogCat` viewer you can set the filter `tag:Adjust` to hide all other logs. After +Build and run your Android app. In your `LogCat` viewer you can set the filter `tag:Adjust` to hide all other logs. After your app has launched you should see the following Adjust log: `Install tracked` ![][log_message] @@ -392,7 +398,7 @@ Once you have integrated the adjust SDK into your project, you can take advantag ### Event tracking -You can use adjust to track any event in your app. Suppose you want to track every tap on a button. You would have to +You can use adjust to track any event in your app. Suppose you want to track every tap on a button. You would have to create a new event token in your [dashboard]. Let's say that event token is `abc123`. In your button's `onClick` method you could then add the following lines to track the click: @@ -403,7 +409,7 @@ Adjust.trackEvent(event); #### Revenue tracking -If your users can generate revenue by tapping on advertisements or making In-App Purchases you can track those revenues +If your users can generate revenue by tapping on advertisements or making In-App Purchases you can track those revenues with events. Lets say a tap is worth one Euro cent. You could then track the revenue event like this: ```java @@ -414,23 +420,23 @@ Adjust.trackEvent(event); This can be combined with callback parameters of course. -When you set a currency token, adjust will automatically convert the incoming revenues into a reporting revenue of your +When you set a currency token, adjust will automatically convert the incoming revenues into a reporting revenue of your choice. Read more about [currency conversion here.][currency-conversion] You can read more about revenue and event tracking in the [event tracking guide.][event-tracking] -The event instance can be used to configure the event even more before tracking it. +The event instance can be used to configure the event further before tracking it: #### In-App Purchase verification -If you want to check the validity of In-App Purchases made in your app using Purchase Verification, adjust's server side -receipt verification tool, then check out our Android purchase SDK to read more about it +If you want to check the validity of In-App Purchases made in your app using Purchase Verification, adjust's server side +receipt verification tool, then check out our Android purchase SDK to read more about it [here][android-purchase-verification]. #### Callback parameters -You can register a callback URL for your events in your [dashboard]. We will send a GET request to that URL whenever the -event gets tracked. You can add callback parameters to that event by calling `addCallbackParameter` on the event instance +You can register a callback URL for your events in your [dashboard]. We will send a GET request to that URL whenever the +event is tracked. You can add callback parameters to that event by calling `addCallbackParameter` to the event instance before tracking it. We will then append these parameters to your callback URL. For example, suppose you have registered the URL `http://www.adjust.com/callback` then track an event like this: @@ -450,20 +456,19 @@ In that case we would track the event and send a request to: http://www.adjust.com/callback?key=value&foo=bar ``` -It should be mentioned that we support a variety of placeholders like `{gps_adid}` that can be used as parameter values. -In the resulting callback this placeholder would be replaced with the Google Play Services ID 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 `{gps_adid}` that can be used as parameter values. +In the resulting callback this placeholder would be replaced with the Google Play Services ID 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. -You can read more about using URL callbacks, including a full list of available values, in our +You can read more about using URL callbacks, including a full list of available values, in our [callbacks guide][callbacks-guide]. #### Partner parameters -You can also add parameters to be transmitted to network partners, for the integrations that have been activated in your +You can also add parameters to be transmitted to network partners, which have been activated in your adjust dashboard. -This works similarly to the callback parameters mentioned above, but can be added by calling the `addPartnerParameter` +This works similarly to the callback parameters mentioned above, but can be added by calling the `addPartnerParameter` method on your `AdjustEvent` instance. ```java @@ -477,9 +482,105 @@ Adjust.trackEvent(event); You can read more about special partners and these integrations in our [guide to special partners.][special-partners] +### Set up session parameters + +Some parameters are saved to be sent in every event and session of the adjust SDK. +Once you have added any of these parameters, you don't need to add them every time, since they will be saved locally. +If you add the same parameter twice, there will be no effect. + +These session parameters can be called before the adjust SDK is launched to make sure they are sent even on install. +If you need to send them with an install, but can only obtain the needed values after launch, it's possible to [delay](#delay-start) the first launch of the adjust SDK to allow this behaviour. + +#### Session callback parameters + +The same callback parameters that are registered for [events](#callback-parameters) can be also saved to be sent in every event or session of the adjust SDK. + +The session callback parameters have a similar interface to the event callback parameters. +Instead of adding the key and it's value to an event, it's added through a call to `Adjust.addSessionCallbackParameter(String key, String value)`: + +```java +Adjust.addSessionCallbackParameter("foo", "bar"); +``` + +The session callback parameters will be merged with the callback parameters added to an event. +The callback parameters added to an event have precedence over the session callback parameters. +Meaning that, when adding a callback parameter to an event with the same key to one added from the session, the value that prevails is the callback parameter added to the event. + +It's possible to remove a specific session callback parameter by passing the desiring key to the method `Adjust.removeSessionCallbackParameter(String key)`. + +```java +Adjust.removeSessionCallbackParameter("foo"); +``` + +If you wish to remove all keys and their corresponding values from the session callback parameters, you can reset it with the method `Adjust.resetSessionCallbackParameters()`. + +```java +Adjust.resetSessionCallbackParameters(); +``` + +#### Session partner parameters + +In the same way that there are [session callback parameters](#session-callback-parameters) sent in every event or session of the adjust SDK, there is also session partner parameters. + +These will be transmitted to network partners, for the integrations that have been activated in your adjust [dashboard]. + +The session partner parameters have a similar interface to the event partner parameters. +Instead of adding the key and it's value to an event, it's added through a call to `Adjust.addSessionPartnerParameter(String key, String value)`: + +```java +Adjust.addSessionPartnerParameter("foo", "bar"); +``` + +The session partner parameters will be merged with the partner parameters added to an event. +The partner parameters added to an event have precedence over the session partner parameters. +Meaning that, when adding a partner parameter to an event with the same key to one added from the session, the value that prevails is the partner parameter added to the event. + +It's possible to remove a specific session partner parameter by passing the desiring key to the method `Adjust.removeSessionPartnerParameter(String key)`. + +```java +Adjust.removeSessionPartnerParameter("foo"); +``` + +If you wish to remove all keys and their corresponding values from the session partner parameters, you can reset it with the method `Adjust.resetSessionPartnerParameters()`. + +```java +Adjust.resetSessionPartnerParameters(); +``` + +#### External device id + +The adjust SDK uses device ids that are available from iOS, but your app might have other sources of identifiers that can help features like ad conversion. + +To save your unique identifier in the session parameters that are send in every request and session, call the `Adjust.addExternalDeviceId(String externalDeviceId)` method: + +```java +Adjust.addExternalDeviceId("customDeviceId"); +``` + +It's also possible to remove the saved unique identifier by calling the method `Adjust.resetExternalDeviceId()`: + +```java +Adjust.resetExternalDeviceId(); +``` + +#### Delay start + +Delaying the start of the adjust SDK allows your app some time to obtain session parameters, such as unique identifiers, to be sent on install. + +Set the initial delay time in seconds with the method `setDelayStart` in the `AdjustConfig` instance: + +```java +adjustConfig.setDelayStart(5.5); +``` + +In this case, this will make the adjust SDK not send the initial install session and any event created for 5.5 seconds. +After this time is expired or if you call `Adjust.sendFirstPackages()` in the meanwhile, every session parameter will be added to the delayed install session and events and the adjust SDK will resume as usual. + +The maximum delay start time of the adjust SDK is 10 seconds. + ### Attribution callback -You can register a listener to be notified of tracker attribution changes. Due to the different sources considered for +You can register a listener to be notified of tracker attribution changes. Due to the different sources considered for attribution, this information can not be provided synchronously. The simplest way is to create a single anonymous listener: Please make sure to consider our [applicable attribution data policies][attribution-data]. @@ -498,7 +599,7 @@ config.setOnAttributionChangedListener(new OnAttributionChangedListener() { Adjust.onCreate(config); ``` -Alternatively, you could implement the `OnAttributionChangedListener` interface in your `Application` class and set it as +Alternatively, you could implement the `OnAttributionChangedListener` interface in your `Application` class and set it as listener: ```java @@ -507,7 +608,7 @@ config.setOnAttributionChangedListener(this); Adjust.onCreate(config); ``` -The listener function will be called when the SDK receives the final attribution information. Within the listener function +The listener function will be called after the SDK receives the final attribution data. Within the listener function you have access to the `attribution` parameter. Here is a quick summary of its properties: - `String trackerToken` the tracker token of the current install. @@ -520,8 +621,8 @@ you have access to the `attribution` parameter. Here is a quick summary of its p ### Session and event callbacks -You can register a listener to be notified when events or sessions are tracked. There are four listeners: one for tracking -successful events, one for tracking failed events, one for tracking successful sessions and one for tracking failed +You can register a listener to be notified when events or sessions are tracked. There are four listeners: one for tracking +successful events, one for tracking failed events, one for tracking successful sessions and one for tracking failed sessions. You can add any number of listeners after creating the `AdjustConfig` object: ```java @@ -562,8 +663,8 @@ config.setOnSessionTrackingFailedListener(new OnSessionTrackingFailedListener() Adjust.onCreate(config); ``` -The listener function will be called after the SDK tries to send a package to the server. Within the listener function you -have access to a response data object specifically for the listener. Here is a quick summary of the success session +The listener function will be called after the SDK tries to send a package to the server. Within the listener function you +have access to a response data object specifically for the listener. Here is a quick summary of the success session response data object fields: - `String message` the message from the server or the error logged by the SDK. @@ -577,11 +678,11 @@ Both event response data objects contain: And both event and session failed objects also contain: -- `boolean willRetry` indicates there will be an attempt to resend the package at a later time. +- `boolean willRetry` indicates that will be an attempt to resend the package at a later time. ### Disable tracking -You can disable the adjust SDK from tracking any activities of the current device by calling `setEnabled` with parameter +You can disable the adjust SDK from tracking any activities of the current device by calling `setEnabled` with parameter `false`. **This setting is remembered between sessions**. ```java @@ -593,8 +694,8 @@ the adjust SDK by invoking `setEnabled` with the enabled parameter as `true`. ### Offline mode -You can put the adjust SDK in offline mode to suspend transmission to our servers, while retaining tracked data to be sent -later. While in offline mode, all information is saved in a file, so be careful not to trigger too many events while in +You can put the adjust SDK in offline mode to suspend transmission to our servers, while retaining tracked data to be sent +later. While in offline mode, all information is saved in a file, so be careful not to trigger too many events while in offline mode. You can activate offline mode by calling `setOfflineMode` with the parameter `true`. @@ -603,15 +704,15 @@ You can activate offline mode by calling `setOfflineMode` with the parameter `tr Adjust.setOfflineMode(true); ``` -Conversely, you can deactivate offline mode by calling `setOfflineMode` with `false`. When the adjust SDK is put back in -online mode, all saved information is send to our servers with the correct time information. +Conversely, you can deactivate offline mode by calling `setOfflineMode` with `false`. When the adjust SDK is put back in +online mode, all saved information is sent to our servers with the correct time information. -Unlike disabling tracking, this setting is **not remembered** between sessions. This means that the SDK is in online mode +Unlike disabling tracking, this setting is **not remembered** between sessions. This means that the SDK is in online mode whenever it is started, even if the app was terminated in offline mode. ### Event buffering -If your app makes heavy use of event tracking, you might want to delay some HTTP requests in order to send them in one +If your app makes heavy use of event tracking, you might want to delay some HTTP requests in order to send them in one batch every minute. You can enable event buffering with your `AdjustConfig` instance: ```java @@ -624,7 +725,7 @@ Adjust.onCreate(config); ### Background tracking -The default behaviour of the adjust SDK is to pause sending HTTP requests while the app is in the background. You can +The default behaviour of the adjust SDK is to pause sending HTTP requests while the app is in the background. You can change this in your `AdjustConfig` instance: ```java @@ -637,11 +738,11 @@ Adjust.onCreate(config); ### Device IDs -Certain services (such as Google Analytics) require you to coordinate Device and Client IDs in order to prevent duplicate -reporting. +Certain services (such as Google Analytics) require you to coordinate Device and Client IDs in order to prevent duplicate +reporting. -If you need to obtain the Google Advertising ID, there is a restriction that only allows it to be read in a background -thread. If you call the function `getGoogleAdId` with the context and a `OnDeviceIdsRead` instance, it will work in any +If you need to obtain the Google Advertising ID, there is a restriction that only allows it to be read in a background +thread. If you call the function `getGoogleAdId` with the context and a `OnDeviceIdsRead` instance, it will work in any situation: ```java @@ -653,9 +754,18 @@ Adjust.getGoogleAdId(this, new OnDeviceIdsRead() { }); ``` -Inside the method `onGoogleAdIdRead` of the `OnDeviceIdsRead` instance, you will have access to Google Advertising ID as +Inside the method `onGoogleAdIdRead` of the `OnDeviceIdsRead` instance, you will have access to Google Advertising ID as the variable `googleAdId`. +### Push token + +To send us the push notification token, add the following call to Adjust once you have obtained your token or when ever +it's value is changed: + +```java +Adjust.setPushToken(pushNotificationsToken); +``` + ### Pre-installed trackers If you want to use the Adjust SDK to recognize users that found your app @@ -684,18 +794,18 @@ pre-installed on their device, follow these steps. ### Deep linking If you are using the adjust tracker URL with an option to deep link into your app from the URL, there is the possibility to -get info about the deep link URL and its content. Hitting the URL can happen when the user has your app already installed -(standard deep linking scenario) or if they don't have the app on their device (deferred deep linking scenario). In the -standard deep linking scenario, Android platform natively offers the possibility for you to get the info about the deep -link content. Deferred deep linking scenario is something which Android platform doesn't support out of box and for this +get info about the deep link URL and its content. Hitting the URL can happen when the user has your app already installed +(standard deep linking scenario) or if they don't have the app on their device (deferred deep linking scenario). In the +standard deep linking scenario, Android platform natively offers the possibility for you to get the info about the deep +link content. Deferred deep linking scenario is something which Android platform doesn't support out of box and for this case, the adjust SDK will offer you the mechanism to get the info about the deep link content. #### Standard deep linking scenario -If a user has your app installed and you want it to launch after hitting an adjust tracker URL with the `deep_link` -parameter in it, you need enable deep linking in your app. This is being done by choosing a desired **unique scheme name** -and assigning it to the Activity which you want to launch once the app opens after the user clicked on the link. This is -set in the `AndroidManifest.xml`. You need to add the `intent-filter` section to your desired Activity definition in the +If a user has your app installed and you want it to launch after hitting an adjust tracker URL with the `deep_link` +parameter in it, you need enable deep linking in your app. This is being done by choosing a desired **unique scheme name** +and assigning it to the Activity which you want to launch once the app opens after the user clicked on the link. This is +set in the `AndroidManifest.xml`. You need to add the `intent-filter` section to your desired Activity definition in the manifest file and assign `android:scheme` property value with the desired scheme name: ```xml @@ -704,7 +814,7 @@ manifest file and assign `android:scheme` property value with the desired scheme android:configChanges="orientation|keyboardHidden" android:label="@string/app_name" android:screenOrientation="portrait"> - + @@ -720,7 +830,7 @@ manifest file and assign `android:scheme` property value with the desired scheme ``` With this being set, you need to use the assigned scheme name in the adjust tracker URL's `deep_link` parameter if you want -your app to launch once the tracker URL is clicked. A tracker URL without any information added to the deep link can be +your app to launch once the tracker URL is clicked. A tracker URL without any information added to the deep link can be built to look something like this: ``` @@ -729,18 +839,18 @@ https://app.adjust.com/abc123?deep_link=adjustExample%3A%2F%2F Please, have in mind that the `deep_link` parameter value in the URL **must be URL encoded**. -After clicking this tracker URL, and with the app set as described above, your app will launch along with the -`MainActivity` intent. Inside the `MainActivity` class, you will automatically be provided with the information about the -`deep_link` parameter content. Once this content is delivered to you, it **will not be encoded**, although it was encoded +After clicking this tracker URL, and with the app set as described above, your app will launch along with the +`MainActivity` intent. Inside the `MainActivity` class, you will automatically be provided with the information about the +`deep_link` parameter content. Once this content is delivered to you, it **will not be encoded**, although it was encoded in the URL. -Depending on the `android:launchMode` setting of your Activity in the `AndroidManifest.xml` file, information about the -`deep_link` parameter content will be delivered to the appropriate place in the Activity file. For more information about +Depending on the `android:launchMode` setting of your Activity in the `AndroidManifest.xml` file, information about the +`deep_link` parameter content will be delivered to the appropriate place in the Activity file. For more information about the possible values of the `android:launchMode` property, check [the official Android documentation][android-launch-modes]. -There are two possible places in which information about the deep link content will be delivered to your desired Activity -via `Intent` object - either in the Activity's `onCreate` or `onNewIntent` method. After the app has launched and one of -these methods is triggered, you will be able to get the actual deeplink passed in the `deep_link` parameter in the click +There are two possible places in which information about the deep link content will be delivered to your desired Activity +via `Intent` object - either in the Activity's `onCreate` or `onNewIntent` method. After the app has launched and one of +these methods is triggered, you will be able to get the actual deeplink passed in the `deep_link` parameter in the click URL. You can then use this information to do some additional logic in your app. You can extract the deep link content from these two methods like this: @@ -753,7 +863,7 @@ protected void onCreate(Bundle savedInstanceState) { Intent intent = getIntent(); Uri data = intent.getData(); - + // data.toString() -> This is your deep_link parameter value. } ``` @@ -762,22 +872,22 @@ protected void onCreate(Bundle savedInstanceState) { @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - + Uri data = intent.getData(); - + // data.toString() -> This is your deep_link parameter value. } ``` #### Deferred deep linking scenario -Deferred deep linking scenario happens when a user clicks on the adjust tracker URL with the `deep_link` parameter in it, -but does not have the app installed on the device at the moment of click. After that, the user will get redirected to the -Play Store to download and install your app. After opening it for the first time, the content of the `deep_link` parameter +Deferred deep linking scenario happens when a user clicks on the adjust tracker URL with the `deep_link` parameter in it, +but does not have the app installed on the device at the moment of click. After that, the user will get redirected to the +Play Store to download and install your app. After opening it for the first time, the content of the `deep_link` parameter will be delivered to the app. In order to get info about the `deep_link` parameter content in a deferred deep linking scenario, you should set a listener -method on the `AdjustConfig` object. This will get triggered once the adjust SDK gets the info about the deep link content +method on the `AdjustConfig` object. This will get triggered once the adjust SDK gets the info about the deep link content from the backend. ```java @@ -799,28 +909,28 @@ config.setOnDeeplinkResponseListener(new OnDeeplinkResponseListener() { Adjust.onCreate(config); ``` -Once the adjust SDK receives the info about the deep link content from the backend, it will deliver you the info about its -content in this listener and expect the `boolean` return value from you. This return value represents your decision on -whether the adjust SDK should launch the Activity to which you have assigned the scheme name from the deep link (like in -the standard deep linking scenario) or not. +Once the adjust SDK receives the info about the deep link content from the backend, it will deliver you the info about its +content in this listener and expect the `boolean` return value from you. This return value represents your decision on +whether the adjust SDK should launch the Activity to which you have assigned the scheme name from the deep link (like in +the standard deep linking scenario) or not. -If you return `true`, we will launch it and the exact same scenario which is described in the -[Standard deep linking scenario chapter](#deeplinking-standard) will happen. If you do not want the SDK to launch the -Activity, you can return `false` from this listener and based on the deep link content decide on your own what to do next +If you return `true`, we will launch it and the exact same scenario which is described in the +[Standard deep linking scenario chapter](#deeplinking-standard) will happen. If you do not want the SDK to launch the +Activity, you can return `false` from this listener and based on the deep link content decide on your own what to do next in your app. #### Reattribution via deep links -Adjust enables you to run re-engagement campaigns with usage of deep links. For more information on how to do that, please -check our [official docs][reattribution-with-deeplinks]. +Adjust enables you to run re-engagement campaigns with usage of deep links. For more information on how to do that, please +check our [official docs][reattribution-with-deeplinks]. -If you are using this feature, in order for your user to be properly reattributed, you need to make one additional call to +If you are using this feature, in order for your user to be properly reattributed, you need to make one additional call to the adjust SDK in your app. -Once you have received deep link content information in your app, add a call to `Adjust.appWillOpenUrl` method. By making +Once you have received deep link content information in your app, add a call to `Adjust.appWillOpenUrl` method. By making this call, the adjust SDK will try to find if there is any new attribution info inside of the deep link and if any, it will -be sent to the adjust backend. If your user should be reattributed due to a click on the adjust tracker URL with deep link -content in it, you will see the [attribution callback](#attribution-callback) in your app being triggered with new +be sent to the adjust backend. If your user should be reattributed due to a click on the adjust tracker URL with deep link +content in it, you will see the [attribution callback](#attribution-callback) in your app being triggered with new attribution info for this user. The call to `Adjust.appWillOpenUrl` should be done like this: @@ -833,7 +943,7 @@ protected void onCreate(Bundle savedInstanceState) { Intent intent = getIntent(); Uri data = intent.getData(); - + Adjust.appWillOpenUrl(data); } ``` @@ -842,9 +952,9 @@ protected void onCreate(Bundle savedInstanceState) { @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - + Uri data = intent.getData(); - + Adjust.appWillOpenUrl(data); } ``` @@ -853,11 +963,11 @@ protected void onNewIntent(Intent intent) { ### I'm seeing the "Session failed (Ignoring too frequent session. ...)" error. -This error typically occurs when testing installs. Uninstalling and reinstalling the app is not enough to trigger a new -install. The servers will determine that the SDK has lost its locally aggregated session data and ignore the erroneous +This error typically occurs when testing installs. Uninstalling and reinstalling the app is not enough to trigger a new +install. The servers will determine that the SDK has lost its locally aggregated session data and ignore the erroneous message, given the information available on the servers about the device. -This behaviour can be cumbersome during tests, but is necessary in order to have the sandbox behaviour match production as +This behaviour can be cumbersome during tests, but is necessary in order to have the sandbox behaviour match production as much as possible. You can reset the session data of the device in our servers. Check the error message in the logs: @@ -881,25 +991,25 @@ http://app.adjust.com/forget_device?app_token={yourAppToken}&gps_adid={gps_adidV http://app.adjust.com/forget_device?app_token={yourAppToken}&android_id={androidIDValue} ``` -When the device is forgotten, the link just returns `Forgot device`. If the device was already forgotten or the values were +When the device is forgotten, the link just returns `Forgot device`. If the device was already forgotten or the values were incorrect, the link returns `Device not found`. ### Is my broadcast receiver capturing the install referrer? -If you followed the instructions in the [guide](#broadcast_receiver), the broadcast receiver should be configured to send +If you followed the instructions in the [guide](#broadcast_receiver), the broadcast receiver should be configured to send the install referrer to our SDK and to our servers. -You can test this by triggering a test install referrer manually. Replace `com.your.appid` with your app ID and run the +You can test this by triggering a test install referrer manually. Replace `com.your.appid` with your app ID and run the following command with the [adb](http://developer.android.com/tools/help/adb.html) tool that comes with Android Studio: ``` adb shell am broadcast -a com.android.vending.INSTALL_REFERRER -n com.your.appid/com.adjust.sdk.AdjustReferrerReceiver --es "referrer" "adjust_reftag%3Dabc1234%26tracking_id%3D123456789%26utm_source%3Dnetwork%26utm_medium%3Dbanner%26utm_campaign%3Dcampaign" ``` -If you are already using a different broadcast receiver for the `INSTALL_REFERRER` intent and followed this +If you are already using a different broadcast receiver for the `INSTALL_REFERRER` intent and followed this [guide][referrer], replace `com.adjust.sdk.AdjustReferrerReceiver` with your broadcast receiver. -You can also remove the `-n com.your.appid/com.adjust.sdk.AdjustReferrerReceiver` parameter so that all the apps in the +You can also remove the `-n com.your.appid/com.adjust.sdk.AdjustReferrerReceiver` parameter so that all the apps in the device will receive the `INSTALL_REFERRER` intent. If you set the log level to `verbose`, you should be able to see the log from reading the referrer: @@ -926,19 +1036,19 @@ V/Adjust: Path: /sdk_click tracking_enabled 1 ``` -If you perform this test before launching the app, you won't see the package being sent. The package will be sent once the +If you perform this test before launching the app, you won't see the package being sent. The package will be sent once the app is launched. ### Can I trigger an event at application launch? -Not how you might intuitively think. The `onCreate` method on the global `Application` class is called not only at +Not how you might intuitively think. The `onCreate` method on the global `Application` class is called not only at application launch, but also when a system or application event is captured by the app. -Our SDK is prepared for initialization at this time, but not actually started. This will only happen when an activity is +Our SDK is prepared for initialization at this time, but not actually started. This will only happen when an activity is started, i.e., when a user actually launches the app. -That's why triggering an event at this time will not do what you would expect. Such calls will start the adjust SDK and -send the events, even when the app was not launched by the user - at a time that depends on external factors of the app. +That's why triggering an event at this time will not do what you would expect. Such calls will start the adjust SDK and +send the events, even when the app was not launched by the user - at a time that depends on external factors of the app. Triggering events at application launch will thus result in inaccuracies in the number of installs and sessions tracked. diff --git a/VERSION b/VERSION index f6cdf4098..2da431623 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.7.0 +4.10.0 diff --git a/doc/english/criteo_plugin.md b/doc/english/criteo_plugin.md index f8a6695c3..b8effeb8e 100644 --- a/doc/english/criteo_plugin.md +++ b/doc/english/criteo_plugin.md @@ -3,7 +3,7 @@ Add the dependency of the adjust sdk with the Criteo plugin: ``` -compile 'com.adjust.sdk:adjust-android-criteo:4.7.0' +compile 'com.adjust.sdk:adjust-android-criteo:4.10.0' ``` Or integrate adjust with Criteo events by following these steps: diff --git a/doc/english/migrate.md b/doc/english/migrate.md index ea3ac952b..23a661197 100644 --- a/doc/english/migrate.md +++ b/doc/english/migrate.md @@ -1,4 +1,4 @@ -## Migrate your adjust SDK for Android to 4.7.0 from 3.6.2 +## Migrate your adjust SDK for Android to 4.10.0 from 3.6.2 ### The Application class diff --git a/doc/english/sociomantic_plugin.md b/doc/english/sociomantic_plugin.md index f07506944..9dac1c985 100644 --- a/doc/english/sociomantic_plugin.md +++ b/doc/english/sociomantic_plugin.md @@ -3,7 +3,7 @@ Add the dependency of the adjust sdk with the Sociomantic plugin: ``` -compile 'com.adjust.sdk:adjust-android-sociomantic:4.7.0' +compile 'com.adjust.sdk:adjust-android-sociomantic:4.10.0' ``` Or integrate adjust with Sociomantic events by following these steps: diff --git a/doc/english/trademob_plugin.md b/doc/english/trademob_plugin.md index 6aa39d3e9..051d7742b 100644 --- a/doc/english/trademob_plugin.md +++ b/doc/english/trademob_plugin.md @@ -3,7 +3,7 @@ Add the dependency of the adjust sdk with the Trademob plugin: ``` -compile 'com.adjust.sdk:adjust-android-trademob:4.7.0' +compile 'com.adjust.sdk:adjust-android-trademob:4.10.0' ``` Or integrate adjust with Trademob events by following these steps: