diff --git a/plugin.xml b/plugin.xml index 683d9f21..0727f0fa 100644 --- a/plugin.xml +++ b/plugin.xml @@ -52,13 +52,15 @@ - + + + @@ -69,13 +71,17 @@ - + + + + + diff --git a/src/android/BufferedCallbackContext.java b/src/android/BufferedCallbackContext.java new file mode 100644 index 00000000..527d4d3c --- /dev/null +++ b/src/android/BufferedCallbackContext.java @@ -0,0 +1,32 @@ +package com.adjust.sdk; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginResult; + +import java.util.ArrayList; + +class BufferedCallbackContext extends CallbackContext { + private ArrayList results = new ArrayList(); + + public BufferedCallbackContext(String callbackId, CordovaWebView webView) { + super(callbackId, webView); + } + + @Override + public void sendPluginResult(PluginResult pluginResult) { + super.sendPluginResult(pluginResult); + results.add(pluginResult); + } + + /** + * Calls `sendPluginResult` on callbackContext instance, with all buffered PluginResults. + * + * @param callbackContext + */ + public void replayResults(CallbackContext callbackContext) { + for (PluginResult result : results) { + callbackContext.sendPluginResult(result); + } + } +} \ No newline at end of file diff --git a/src/android/EagerAdjustCordova.java b/src/android/EagerAdjustCordova.java new file mode 100644 index 00000000..9f0410c7 --- /dev/null +++ b/src/android/EagerAdjustCordova.java @@ -0,0 +1,128 @@ +package com.adjust.sdk; + +import android.os.Bundle; + +import org.apache.cordova.CallbackContext; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.adjust.sdk.AdjustCordovaUtils.COMMAND_CREATE; +import static com.adjust.sdk.AdjustCordovaUtils.COMMAND_SET_ATTRIBUTION_CALLBACK; +import static com.adjust.sdk.AdjustCordovaUtils.COMMAND_SET_DEFERRED_DEEPLINK_CALLBACK; +import static com.adjust.sdk.AdjustCordovaUtils.COMMAND_SET_EVENT_TRACKING_FAILED_CALLBACK; +import static com.adjust.sdk.AdjustCordovaUtils.COMMAND_SET_EVENT_TRACKING_SUCCEEDED_CALLBACK; +import static com.adjust.sdk.AdjustCordovaUtils.COMMAND_SET_SESSION_TRACKING_FAILED_CALLBACK; +import static com.adjust.sdk.AdjustCordovaUtils.COMMAND_SET_SESSION_TRACKING_SUCCEEDED_CALLBACK; +import static com.adjust.sdk.AdjustCordovaUtils.KEY_APP_TOKEN; +import static com.adjust.sdk.AdjustCordovaUtils.KEY_ENVIRONMENT; + +public class EagerAdjustCordova extends AdjustCordova { + + // DO NOT CHANGE (Used in MainActivity) + private final String AdjustTokenKey = "adjust_token"; + private final String EnvironmentKey = "environment"; + + // Adjust event callbacks to set + private final List callbacks = Arrays.asList( + COMMAND_SET_ATTRIBUTION_CALLBACK, + COMMAND_SET_EVENT_TRACKING_SUCCEEDED_CALLBACK, + COMMAND_SET_EVENT_TRACKING_FAILED_CALLBACK, + COMMAND_SET_SESSION_TRACKING_SUCCEEDED_CALLBACK, + COMMAND_SET_SESSION_TRACKING_FAILED_CALLBACK, + COMMAND_SET_DEFERRED_DEEPLINK_CALLBACK); + + private Map callbackContextMap = new HashMap<>(); + + @Override + public void onResume(boolean multitasking) { + super.onResume(multitasking); + + for (String callback : callbacks) { + setBufferedCallback(callback); + } + initializeSdk(); + } + + /** + * Sets `COMMAND_SET_*_CALLBACK` callback as a placeholder until we call + * setCallback from JS. + * This enables us to buffer the result of the callback incase + * JS is slow in calling setCallback. + */ + private void setBufferedCallback(String action) { + BufferedCallbackContext bufferedCallbackContext = new BufferedCallbackContext(action, webView); + JSONArray empty = new JSONArray(); + try { + this.execute(action, empty, bufferedCallbackContext); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + @Override + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + handleCallback(action, callbackContext); + return super.execute(action, args, callbackContext); + } + + /** + * Stores BufferedCallbacks to the Map as a placeholder callback, + * for when the JS is slower in setting the callback for each action. + * BufferedCallback stores the result (if invoked) and passes the + * result via the `sendPluginResult` call to the callback to JS. + * + * @param action + * @param callbackContext + */ + private void handleCallback(String action, CallbackContext callbackContext) { + // Only handle callbacks for specified commands + if (!callbacks.contains(action)) return; + + BufferedCallbackContext bufferedCallback = callbackContextMap.remove(action); + if (bufferedCallback != null) { + bufferedCallback.replayResults(callbackContext); + } else if (callbackContext instanceof BufferedCallbackContext) { + callbackContextMap.put(action, (BufferedCallbackContext) callbackContext); + } + } + + // Initializer function to call the `execute` method with the required arguments for init. + private void initializeSdk() { + + // Getting data from extras + Bundle extras = this.cordova.getActivity().getIntent().getExtras(); + + // If no eager initialization params were set, e.g. in Debug UI, don't do initialize eagerly. + if (extras == null) return; + + try { + String appToken = extras.getString(AdjustTokenKey); + boolean isDebug = extras.getBoolean(EnvironmentKey, false); + + JSONObject data = new JSONObject(); + data.put(KEY_APP_TOKEN, appToken); + data.put( + KEY_ENVIRONMENT, + isDebug + ? AdjustConfig.ENVIRONMENT_SANDBOX + : AdjustConfig.ENVIRONMENT_PRODUCTION + ); + + JSONArray dataArray = new JSONArray(); + dataArray.put(data); + + JSONArray jsonArray = new JSONArray(); + jsonArray.put(dataArray); + + execute(COMMAND_CREATE, jsonArray, null); + } catch (JSONException e) { + e.printStackTrace(); + } + } +} diff --git a/src/ios/AdjustCordovaDelegate.m b/src/ios/AdjustCordovaDelegate.m index a5693f31..7304c818 100644 --- a/src/ios/AdjustCordovaDelegate.m +++ b/src/ios/AdjustCordovaDelegate.m @@ -9,6 +9,7 @@ #import #import #import "AdjustCordovaDelegate.h" +#import "EagerAdjustBuffer.h" static dispatch_once_t onceToken; static AdjustCordovaDelegate *defaultInstance = nil; @@ -108,6 +109,7 @@ - (void)adjustAttributionChangedWannabe:(ADJAttribution *)attribution { CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; pluginResult.keepCallback = [NSNumber numberWithBool:YES]; [_adjustCordovaCommandDelegate sendPluginResult:pluginResult callbackId:_attributionCallbackId]; + [[EagerAdjustBuffer getInstance] addPluginResult:pluginResult andCallbackId:_attributionCallbackId]; } - (void)adjustEventTrackingSucceededWannabe:(ADJEventSuccess *)eventSuccessResponseData { @@ -128,6 +130,7 @@ - (void)adjustEventTrackingSucceededWannabe:(ADJEventSuccess *)eventSuccessRespo CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; pluginResult.keepCallback = [NSNumber numberWithBool:YES]; [_adjustCordovaCommandDelegate sendPluginResult:pluginResult callbackId:_eventSucceededCallbackId]; + [[EagerAdjustBuffer getInstance] addPluginResult:pluginResult andCallbackId:_attributionCallbackId]; } - (void)adjustEventTrackingFailedWannabe:(ADJEventFailure *)eventFailureResponseData { @@ -149,6 +152,7 @@ - (void)adjustEventTrackingFailedWannabe:(ADJEventFailure *)eventFailureResponse CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; pluginResult.keepCallback = [NSNumber numberWithBool:YES]; [_adjustCordovaCommandDelegate sendPluginResult:pluginResult callbackId:_eventFailedCallbackId]; + [[EagerAdjustBuffer getInstance] addPluginResult:pluginResult andCallbackId:_eventFailedCallbackId]; } - (void)adjustSessionTrackingSucceededWannabe:(ADJSessionSuccess *)sessionSuccessResponseData { @@ -167,6 +171,7 @@ - (void)adjustSessionTrackingSucceededWannabe:(ADJSessionSuccess *)sessionSucces CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; pluginResult.keepCallback = [NSNumber numberWithBool:YES]; [_adjustCordovaCommandDelegate sendPluginResult:pluginResult callbackId:_sessionSucceededCallbackId]; + [[EagerAdjustBuffer getInstance] addPluginResult:pluginResult andCallbackId:_sessionSucceededCallbackId]; } - (void)adjustSessionTrackingFailedWananbe:(ADJSessionFailure *)sessionFailureResponseData { @@ -186,6 +191,7 @@ - (void)adjustSessionTrackingFailedWananbe:(ADJSessionFailure *)sessionFailureRe CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; pluginResult.keepCallback = [NSNumber numberWithBool:YES]; [_adjustCordovaCommandDelegate sendPluginResult:pluginResult callbackId:_sessionFailedCallbackId]; + [[EagerAdjustBuffer getInstance] addPluginResult:pluginResult andCallbackId:_sessionFailedCallbackId]; } - (BOOL)adjustDeeplinkResponseWannabe:(NSURL *)deeplink { @@ -193,7 +199,7 @@ - (BOOL)adjustDeeplinkResponseWannabe:(NSURL *)deeplink { CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:path]; pluginResult.keepCallback = [NSNumber numberWithBool:YES]; [_adjustCordovaCommandDelegate sendPluginResult:pluginResult callbackId:_deferredDeeplinkCallbackId]; - + [[EagerAdjustBuffer getInstance] addPluginResult:pluginResult andCallbackId:_deferredDeeplinkCallbackId]; return _shouldLaunchDeferredDeeplink; } diff --git a/src/ios/EagerAdjustBuffer.h b/src/ios/EagerAdjustBuffer.h new file mode 100644 index 00000000..23148a7e --- /dev/null +++ b/src/ios/EagerAdjustBuffer.h @@ -0,0 +1,9 @@ +#import "AdjustCordovaDelegate.h" + +@interface EagerAdjustBuffer : NSObject + ++ (id)getInstance; +- (void)addPluginResult:(CDVPluginResult*)result andCallbackId:(NSString *)callbackId; +- (void)replayResultsWithCommand:(id ) commandDelegate; + +@end diff --git a/src/ios/EagerAdjustBuffer.m b/src/ios/EagerAdjustBuffer.m new file mode 100644 index 00000000..3ec33711 --- /dev/null +++ b/src/ios/EagerAdjustBuffer.m @@ -0,0 +1,41 @@ +#import "EagerAdjustBuffer.h" + + +@implementation EagerAdjustBuffer + +static EagerAdjustBuffer *defaultInstance = nil; +static dispatch_once_t onceToken; +NSMutableDictionary *results; + ++ (id)getInstance { + dispatch_once(&onceToken, ^{ + defaultInstance = [[EagerAdjustBuffer alloc] init]; + results = [NSMutableDictionary new]; + }); + + return defaultInstance; +} + + +- (void)addPluginResult:(CDVPluginResult*)result andCallbackId:(NSString *)callbackId { + NSMutableArray *callBackResults = [results objectForKey:callbackId]; + if(callBackResults != nil) { + [callBackResults addObject:result]; + } else { + callBackResults = [NSMutableArray arrayWithObject:result]; + } + [results setObject:callBackResults forKey:callbackId]; +} + +- (void)replayResultsWithCommand:(id ) commandDelegate { + for(NSString *callbackId in results.allKeys) { + NSMutableArray *callBackResults = [results objectForKey:callbackId]; + for (CDVPluginResult *result in callBackResults) { + [commandDelegate sendPluginResult:result callbackId:callbackId]; + } + [results removeObjectForKey:callbackId]; + } + +} + +@end diff --git a/src/ios/EagerAdjustCordova.h b/src/ios/EagerAdjustCordova.h new file mode 100644 index 00000000..a209f278 --- /dev/null +++ b/src/ios/EagerAdjustCordova.h @@ -0,0 +1,6 @@ +#import "AdjustCordova.h" + + +@interface EagerAdjustCordova : AdjustCordova + +@end diff --git a/src/ios/EagerAdjustCordova.m b/src/ios/EagerAdjustCordova.m new file mode 100644 index 00000000..11c27f89 --- /dev/null +++ b/src/ios/EagerAdjustCordova.m @@ -0,0 +1,77 @@ +#import "EagerAdjustCordova.h" +#import "EagerAdjustBuffer.h" + +#define KEY_APP_TOKEN @"appToken" +#define KEY_ENVIRONMENT @"environment" + +@implementation EagerAdjustCordova + +- (void)pluginInitialize { + [super pluginInitialize]; + [self initializeEagerly]; +} + +- (void)initCallbacks { + [self setAttributionCallback:[[CDVInvokedUrlCommand alloc] initWithArguments:@[] callbackId:@"setAttributionCallback" className:@"EagerAdjustCordova" methodName:@"setAttributionCallback"]]; + [self setEventTrackingSucceededCallback:[[CDVInvokedUrlCommand alloc] initWithArguments:@[] callbackId:@"setEventTrackingSucceededCallback" className:@"EagerAdjustCordova" methodName:@"setEventTrackingSucceededCallback"]]; + [self setEventTrackingFailedCallback:[[CDVInvokedUrlCommand alloc] initWithArguments:@[] callbackId:@"setEventTrackingFailedCallback" className:@"EagerAdjustCordova" methodName:@"setEventTrackingFailedCallback"]]; + [self setSessionTrackingSucceededCallback:[[CDVInvokedUrlCommand alloc] initWithArguments:@[] callbackId:@"setSessionTrackingSucceededCallback" className:@"EagerAdjustCordova" methodName:@"setSessionTrackingSucceededCallback"]]; + [self setSessionTrackingFailedCallback:[[CDVInvokedUrlCommand alloc] initWithArguments:@[] callbackId:@"setSessionTrackingFailedCallback" className:@"EagerAdjustCordova" methodName:@"setSessionTrackingFailedCallback"]]; + [self setDeferredDeeplinkCallback:[[CDVInvokedUrlCommand alloc] initWithArguments:@[] callbackId:@"setDeferredDeeplinkCallback" className:@"EagerAdjustCordova" methodName:@"setDeferredDeeplinkCallback"]]; +} + +- (void)initializeEagerly { + [self initCallbacks]; + if([self.appDelegate respondsToSelector:@selector(getAdjustToken)] && [self.appDelegate respondsToSelector:@selector(getEnvironment)]) { + + NSString *adjustKey = [self.appDelegate performSelector:@selector(getAdjustToken)]; + NSString *environment = [self.appDelegate performSelector:@selector(getEnvironment)]; + + NSArray *commandArray = @[[self createCommandWithKey:adjustKey andEnvironment:environment]]; + CDVInvokedUrlCommand *command = [[CDVInvokedUrlCommand alloc] initWithArguments:commandArray callbackId:nil className:nil methodName:nil]; + [super create:command]; + } +} + +- (NSString *)createCommandWithKey:(NSString *)adjustKey andEnvironment:(NSString *)environment { + + NSDictionary *dic = @{KEY_APP_TOKEN: adjustKey, + KEY_ENVIRONMENT : environment + }; + + NSData *data = [NSJSONSerialization dataWithJSONObject:@[dic] options:NSJSONWritingPrettyPrinted error:nil]; + NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + return jsonString; +} + +- (void)setAttributionCallback:(CDVInvokedUrlCommand *)command { + [[EagerAdjustBuffer getInstance] replayResultsWithCommand:self.commandDelegate]; + [super setAttributionCallback:command]; +} + +- (void)setEventTrackingSucceededCallback:(CDVInvokedUrlCommand *)command { + [[EagerAdjustBuffer getInstance] replayResultsWithCommand:self.commandDelegate]; + [super setEventTrackingSucceededCallback:command]; +} + +- (void)setEventTrackingFailedCallback:(CDVInvokedUrlCommand *)command { + [[EagerAdjustBuffer getInstance] replayResultsWithCommand:self.commandDelegate]; + [super setEventTrackingFailedCallback:command]; +} + +- (void)setSessionTrackingSucceededCallback:(CDVInvokedUrlCommand *)command { + [[EagerAdjustBuffer getInstance] replayResultsWithCommand:self.commandDelegate]; + [super setSessionTrackingSucceededCallback:command]; +} + +- (void)setSessionTrackingFailedCallback:(CDVInvokedUrlCommand *)command { + [[EagerAdjustBuffer getInstance] replayResultsWithCommand:self.commandDelegate]; + [super setSessionTrackingFailedCallback:command]; +} + +- (void)setDeferredDeeplinkCallback:(CDVInvokedUrlCommand *)command { + [[EagerAdjustBuffer getInstance] replayResultsWithCommand:self.commandDelegate]; + [super setDeferredDeeplinkCallback:command]; +} + +@end diff --git a/www/adjust.js b/www/adjust.js index 97110210..319dfe8a 100644 --- a/www/adjust.js +++ b/www/adjust.js @@ -34,6 +34,7 @@ function callCordovaCallback(action, callback) { } var Adjust = { + // Would not be called, when using the eager initialization. create: function(adjustConfig) { if (adjustConfig) { adjustConfig.sdkPrefix = this.getSdkPrefix(); @@ -66,6 +67,30 @@ var Adjust = { callCordovaStringify('create', adjustConfig); }, + setAttributionCallback: function(attributionCallback) { + callCordovaCallback('setAttributionCallback', attributionCallback); + }, + + setEventTrackingSucceededCallback: function(eventTrackingSucceededCallback) { + callCordovaCallback('setEventTrackingSucceededCallback', eventTrackingSucceededCallback); + }, + + setEventTrackingFailedCallback: function(eventTrackingFailedCallback) { + callCordovaCallback('setEventTrackingFailedCallback', eventTrackingFailedCallback); + }, + + setSessionTrackingSucceededCallback: function(sessionTrackingSucceededCallback) { + callCordovaCallback('setSessionTrackingSucceededCallback', sessionTrackingSucceededCallback); + }, + + setSessionTrackingFailedCallback: function(sessionTrackingFailedCallback) { + callCordovaCallback('setSessionTrackingFailedCallback', sessionTrackingFailedCallback); + }, + + setDeferredDeeplinkCallback: function(deferredDeeplinkCallback) { + callCordovaCallback('setDeferredDeeplinkCallback', deferredDeeplinkCallback); + }, + trackEvent: function(adjustEvent) { callCordovaStringify('trackEvent', adjustEvent); },