diff --git a/README.md b/README.md index 8d0599b0c..b7ac28b1c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -[ ![Download](https://api.bintray.com/packages/rudderstack/rudderstack/core/images/download.svg?version=1.0) ](https://bintray.com/rudderstack/rudderstack/core/1.0/link) - # What is Rudder? **Short answer:** @@ -15,31 +13,27 @@ Released under [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) 1. Add these lines to your ```app/build.gradle``` ``` repositories { - maven { - url "https://dl.bintray.com/rudderstack/rudderstack" - } + maven { url "https://dl.bintray.com/rudderstack/rudderstack" } } ``` 2. Add the dependency under ```dependencies``` ``` -implementation 'com.rudderstack.android.sdk:core:1.0' +implementation 'com.rudderstack.android.sdk:core:1.0.1' ``` ## Initialize ```RudderClient``` ``` val rudderClient: RudderClient = RudderClient.getInstance( this, - WRITE_KEY, + , RudderConfig.Builder() - .withEndPointUri(END_POINT_URI) - .withLogLevel(RudderLogger.RudderLogLevel.DEBUG) + .withDataPlaneUrl() .build() ) ``` or (compatible with existing Segment code) ``` -RudderClient.Builder builder = new RudderClient.Builder(this, WRITE_KEY); -builder.logLevel(RudderLogger.RudderLogLevel.VERBOSE); +RudderClient.Builder builder = new RudderClient.Builder(this, ); RudderClient.setSingletonInstance(builder.build()); ``` @@ -67,9 +61,7 @@ RudderClient.with(this).track( ); ``` -For more detailed documentation check [here](https://docs.rudderstack.com/sdk-integration-guide/getting-started-with-android-sdk) +For more detailed documentation check [the documentation page](https://docs.rudderstack.com/sdk-integration-guide/getting-started-with-android-sdk). -# Coming Soon -1. Install attribution support using ```referrer``` API. -2. Option to opt-out from tracking any Analytics Event. -3. RudderOption implementation for custom destination support. +## Contact Us +If you come across any issues while configuring or using RudderStack, please feel free to [contact us](https://rudderstack.com/contact/) or start a conversation on our [Discord](https://discordapp.com/invite/xNEdEGw) channel. We will be happy to help you. \ No newline at end of file diff --git a/build.gradle b/build.gradle index d17df0e89..8e61a1eb9 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' + classpath 'com.android.tools.build:gradle:3.6.2' } } diff --git a/core/build.gradle b/core/build.gradle index 0f6a22a53..1028ec5a6 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -19,7 +19,7 @@ android { minSdkVersion 14 targetSdkVersion 29 versionCode 1 - versionName "1.0" + versionName "1.0.1" } buildTypes { diff --git a/core/maven.gradle b/core/maven.gradle index 6dce7022b..6b77b29e2 100644 --- a/core/maven.gradle +++ b/core/maven.gradle @@ -11,7 +11,7 @@ ext { siteUrl = 'https://github.com/rudderlabs/rudder-sdk-android' gitUrl = 'https://github.com/rudderlabs/rudder-sdk-android.git' - libraryVersion = '1.0' + libraryVersion = '1.0.1' developerId = 'arnabp92' developerName = 'Arnab Pal' diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/Constants.java b/core/src/main/java/com/rudderstack/android/sdk/core/Constants.java index 029aceef6..a7158dc37 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/Constants.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/Constants.java @@ -7,7 +7,7 @@ class Constants { // how often config should be fetched from the server (in hours) (2 hrs by default) static final int CONFIG_REFRESH_INTERVAL = 2; // default base url or rudder-backend-server - static final String BASE_URL = "https://api.rudderlabs.com"; + static final String DATA_PLANE_URL = "https://hosted.rudderlabs.com"; // default flush queue size for the events to be flushed to server static final int FLUSH_QUEUE_SIZE = 30; // default threshold of number of events to be persisted in sqlite db @@ -17,8 +17,7 @@ class Constants { // events will be flushed to server after sleepTimeOut seconds static final int SLEEP_TIMEOUT = 10; // config-plane url to get the config for the writeKey - static final String CONFIG_PLANE_URL = "https://api.rudderlabs.com"; -// static final String CONFIG_PLANE_URL = "https://f7572250.ngrok.io"; + static final String CONTROL_PLANE_URL = "https://api.rudderlabs.com"; // whether we should trackLifecycle events static final boolean TRACK_LIFECYCLE_EVENTS = true; // whether we should record screen views automatically diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/DBPersistentManager.java b/core/src/main/java/com/rudderstack/android/sdk/core/DBPersistentManager.java index 038800e74..8b7347c75 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/DBPersistentManager.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/DBPersistentManager.java @@ -59,6 +59,21 @@ void clearEventFromDB(int messageId) { clearEventsFromDB(messageIds); } + /** + * flush the events from the database + */ + void flushEvents() { + SQLiteDatabase database = getWritableDatabase(); + if (database.isOpen()) { + String deleteSQL = String.format(Locale.US, "DELETE FROM %s", EVENTS_TABLE_NAME); + RudderLogger.logDebug(String.format(Locale.US, "DBPersistentManager: flushEvents: deleteSQL: %s", deleteSQL)); + database.execSQL(deleteSQL); + RudderLogger.logInfo("DBPersistentManager: flushEvents: Messages deleted from DB"); + } else { + RudderLogger.logError("DBPersistentManager: flushEvents: database is not writable"); + } + } + /* * remove selected events from persistence database storage * */ diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/EventRepository.java b/core/src/main/java/com/rudderstack/android/sdk/core/EventRepository.java index 5a2a62a46..af458bfea 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/EventRepository.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/EventRepository.java @@ -32,6 +32,7 @@ * utility class for event processing * */ class EventRepository implements Application.ActivityLifecycleCallbacks { + private final ArrayList eventReplayMessage = new ArrayList<>(); private String authHeaderString; private String anonymousIdHeaderString; private RudderConfig config; @@ -39,10 +40,12 @@ class EventRepository implements Application.ActivityLifecycleCallbacks { private RudderServerConfigManager configManager; private RudderPreferenceManager preferenceManager; private Map integrationOperationsMap = new HashMap<>(); - private final ArrayList eventReplayMessage = new ArrayList<>(); private Map integrationCallbacks = new HashMap<>(); - private boolean initiated = false; + private boolean isSDKInitialized = false; + private boolean isSDKEnabled = true; + private boolean isFactoryInitialized = false; + private int noOfActivities; /* * constructor to be called from RudderClient internally. @@ -85,15 +88,8 @@ class EventRepository implements Application.ActivityLifecycleCallbacks { this.configManager = RudderServerConfigManager.getInstance(_application, _writeKey, _config); // 5. start processor thread - RudderLogger.logDebug("EventRepository: constructor: Starting Processor thread"); - Thread processorThread = new Thread(getProcessorRunnable()); - processorThread.start(); - - this.initiated = true; - - // 6. initiate factories - RudderLogger.logDebug("EventRepository: constructor: Initiating factories"); - this.initiateFactories(); + RudderLogger.logDebug("EventRepository: constructor: Initiating processor and factories"); + this.initiateSDK(); // initiate RudderPreferenceManager and check for lifeCycleEvents preferenceManager = RudderPreferenceManager.getInstance(_application); @@ -101,13 +97,54 @@ class EventRepository implements Application.ActivityLifecycleCallbacks { if (config.isTrackLifecycleEvents() || config.isRecordScreenViews()) { _application.registerActivityLifecycleCallbacks(this); } - - } catch (Exception ex) { RudderLogger.logError(ex.getCause()); } } + private void initiateSDK() { + new Thread(new Runnable() { + @Override + public void run() { + try { + int retryCount = 0; + while (!isSDKInitialized && retryCount <= 5) { + RudderServerConfig serverConfig = configManager.getConfig(); + if (serverConfig != null) { + // initiate processor + isSDKEnabled = serverConfig.source.isSourceEnabled; + if (isSDKEnabled) { + RudderLogger.logDebug("EventRepository: initiateSDK: Initiating processor"); + Thread processorThread = new Thread(getProcessorRunnable()); + processorThread.start(); + + // initiate factories + if (serverConfig.source.destinations != null) { + initiateFactories(serverConfig.source.destinations); + } else { + RudderLogger.logDebug("EventRepository: initiateSDK: No native SDKs are found"); + } + } else { + RudderLogger.logDebug("EventRepository: initiateSDK: source is disabled in the dashboard"); + RudderLogger.logDebug("Flushing persisted events"); + dbManager.flushEvents(); + } + + isSDKInitialized = true; + + } else { + retryCount += 1; + RudderLogger.logDebug("EventRepository: initiateFactories: retry count: " + retryCount); + Thread.sleep(10000); + } + } + } catch (Exception ex) { + RudderLogger.logError(ex); + } + } + }).start(); + } + private void checkApplicationUpdateStatus(Application application) { try { int previousVersionCode = preferenceManager.getBuildVersionCode(); @@ -143,9 +180,7 @@ private void checkApplicationUpdateStatus(Application application) { } } - private boolean isFactoryInitialized = false; - - private void initiateFactories() { + private void initiateFactories(List destinations) { // initiate factory initialization after 10s // let the factories capture everything they want to capture if (config == null || config.getFactories() == null || config.getFactories().isEmpty()) { @@ -154,79 +189,56 @@ private void initiateFactories() { return; } // initiate factories if client is initialized properly - final RudderClient client = RudderClient.getInstance(); - new Thread(new Runnable() { - @Override - public void run() { - try { - int retryCount = 0; - while (!isFactoryInitialized && retryCount <= 5) { - RudderServerConfig serverConfig = configManager.getConfig(); - - if (serverConfig != null && serverConfig.source != null && serverConfig.source.destinations != null) { - List destinations = serverConfig.source.destinations; + if (destinations.isEmpty()) { + RudderLogger.logInfo("EventRepository: initiateFactories: No destination found in the config"); + } else { + Map destinationConfigMap = new HashMap<>(); + for (RudderServerDestination destination : destinations) { + destinationConfigMap.put(destination.destinationDefinition.displayName, destination); + } - if (destinations.isEmpty()) { - RudderLogger.logInfo("EventRepository: initiateFactories: No destination found in the config"); + for (RudderIntegration.Factory factory : config.getFactories()) { + // if factory is present in the config + String key = factory.key(); + if (destinationConfigMap.containsKey(key)) { + RudderServerDestination destination = destinationConfigMap.get(key); + // initiate factory if destination is enabled from the dashboard + if (destination != null && destination.isDestinationEnabled) { + Object destinationConfig = destination.destinationConfig; + RudderLogger.logDebug(String.format(Locale.US, "EventRepository: initiateFactories: Initiating %s native SDK factory", key)); + RudderIntegration nativeOp = factory.create(destinationConfig, RudderClient.getInstance(), config); + RudderLogger.logInfo(String.format(Locale.US, "EventRepository: initiateFactories: Initiated %s native SDK factory", key)); + integrationOperationsMap.put(key, nativeOp); + if (integrationCallbacks.containsKey(key)) { + Object nativeInstance = nativeOp.getUnderlyingInstance(); + RudderClient.Callback callback = integrationCallbacks.get(key); + if (nativeInstance != null && callback != null) { + RudderLogger.logInfo(String.format(Locale.US, "EventRepository: initiateFactories: Callback for %s factory invoked", key)); + callback.onReady(nativeInstance); } else { - Map destinationConfigMap = new HashMap<>(); - for (RudderServerDestination destination : destinations) { - destinationConfigMap.put(destination.destinationDefinition.displayName, destination); - } - - for (RudderIntegration.Factory factory : config.getFactories()) { - // if factory is present in the config - String key = factory.key(); - if (destinationConfigMap.containsKey(key)) { - RudderServerDestination destination = destinationConfigMap.get(key); - // initiate factory if destination is enabled from the dashboard - if (destination != null && destination.isDestinationEnabled) { - Object destinationConfig = destination.destinationConfig; - RudderLogger.logDebug(String.format(Locale.US, "EventRepository: initiateFactories: Initiating %s native SDK factory", key)); - RudderIntegration nativeOp = factory.create(destinationConfig, client, config); - RudderLogger.logInfo(String.format(Locale.US, "EventRepository: initiateFactories: Initiated %s native SDK factory", key)); - integrationOperationsMap.put(key, nativeOp); - if (integrationCallbacks.containsKey(key)) { - Object nativeInstance = nativeOp.getUnderlyingInstance(); - RudderClient.Callback callback = integrationCallbacks.get(key); - if (nativeInstance != null && callback != null) { - RudderLogger.logInfo(String.format(Locale.US, "EventRepository: initiateFactories: Callback for %s factory invoked", key)); - callback.onReady(nativeInstance); - } else { - RudderLogger.logDebug(String.format(Locale.US, "EventRepository: initiateFactories: Callback for %s factory is null", key)); - } - } - } else { - RudderLogger.logDebug(String.format(Locale.US, "EventRepository: initiateFactories: destination was null or not enabled for %s", key)); - } - } else { - RudderLogger.logInfo(String.format(Locale.US, "EventRepository: initiateFactories: %s is not present in configMap", key)); - } - } - } - - isFactoryInitialized = true; - synchronized (eventReplayMessage) { - RudderLogger.logDebug("EventRepository: initiateFactories: replaying old messages with factory"); - ArrayList tempList = new ArrayList<>(eventReplayMessage); - if (!tempList.isEmpty()) { - for (RudderMessage message : tempList) { - makeFactoryDump(message); - } - } - eventReplayMessage.clear(); + RudderLogger.logDebug(String.format(Locale.US, "EventRepository: initiateFactories: Callback for %s factory is null", key)); } - } else { - retryCount += 1; - RudderLogger.logDebug("EventRepository: initiateFactories: retry count: " + retryCount); - Thread.sleep(10000); } + } else { + RudderLogger.logDebug(String.format(Locale.US, "EventRepository: initiateFactories: destination was null or not enabled for %s", key)); } - } catch (Exception ex) { - RudderLogger.logError(ex); + } else { + RudderLogger.logInfo(String.format(Locale.US, "EventRepository: initiateFactories: %s is not present in configMap", key)); } } - }).start(); + } + + isFactoryInitialized = true; + synchronized (eventReplayMessage) { + RudderLogger.logDebug("EventRepository: initiateFactories: replaying old messages with factory"); + ArrayList tempList = new ArrayList<>(eventReplayMessage); + if (!tempList.isEmpty()) { + for (RudderMessage message : tempList) { + makeFactoryDump(message); + } + } + eventReplayMessage.clear(); + } } private Runnable getProcessorRunnable() { @@ -270,13 +282,13 @@ public void run() { // we have at least one event to flush to server if (messages.size() >= config.getFlushQueueSize() || (!messages.isEmpty() && sleepCount >= config.getSleepTimeOut())) { // form payload JSON form the list of messages - String payload = getPayloadFromMessages(messages); + String payload = getPayloadFromMessages(messageIds, messages); RudderLogger.logDebug(String.format(Locale.US, "EventRepository: processor: payload: %s", payload)); + RudderLogger.logInfo(String.format(Locale.US, "EventRepository: processor: EventCount: %d", messageIds.size())); if (payload != null) { // send payload to server if it is not null String response = flushEventsToServer(payload); RudderLogger.logInfo(String.format(Locale.US, "EventRepository: processor: ServerResponse: %s", response)); - RudderLogger.logInfo(String.format(Locale.US, "EventRepository: processor: EventCount: %d", messages.size())); // if success received from server if (response != null && response.equalsIgnoreCase("OK")) { // remove events from DB @@ -305,11 +317,13 @@ public void run() { * of deserialization and forming the payload object and creating the json string * again from the object * */ - private String getPayloadFromMessages(ArrayList messages) { + private String getPayloadFromMessages(ArrayList messageIds, ArrayList messages) { try { RudderLogger.logDebug("EventRepository: getPayloadFromMessages: recordCount: " + messages.size()); String sentAtTimestamp = Utils.getTimeStamp(); RudderLogger.logDebug("EventRepository: getPayloadFromMessages: sentAtTimestamp: " + sentAtTimestamp); + // initialize ArrayLists to store current batch + ArrayList batchMessageIds = new ArrayList<>(); // get string builder StringBuilder builder = new StringBuilder(); // append initial json token @@ -318,25 +332,38 @@ private String getPayloadFromMessages(ArrayList messages) { builder.append("\"sentAt\":\"").append(sentAtTimestamp).append("\","); // initiate batch array in the json builder.append("\"batch\": ["); + int totalBatchSize = Utils.getUTF8Length(builder) + 2; // we add 2 characters at the end + int messageSize; // loop through messages list and add in the builder for (int index = 0; index < messages.size(); index++) { String message = messages.get(index); // strip last ending object character message = message.substring(0, message.length() - 1); // add sentAt time stamp - message = String.format("%s,\"sentAt\":\"%s\"}", message, sentAtTimestamp); - // finally add message string to builder + message = String.format("%s,\"sentAt\":\"%s\"},", message, sentAtTimestamp); + // add message size to batch size + messageSize = Utils.getUTF8Length(message); + totalBatchSize += messageSize; + // check batch size + if (totalBatchSize >= Utils.MAX_BATCH_SIZE) { + RudderLogger.logDebug(String.format(Locale.US, "EventRepository: getPayloadFromMessages: MAX_BATCH_SIZE reached at index: %d | Total: %d", index, totalBatchSize)); + break; + } // finally add message string to builder builder.append(message); - // if not last item in the list, add a "," - if (index != messages.size() - 1) { - builder.append(","); - } + // add message to batch ArrayLists + batchMessageIds.add(messageIds.get(index)); + } + if (builder.charAt(builder.length() - 1) == ',') { + // remove trailing ',' + builder.deleteCharAt(builder.length() - 1); } // close batch array in the json builder.append("]"); // append closing token in the json builder.append("}"); + // retain all events belonging to the batch + messageIds.retainAll(batchMessageIds); // finally return the entire payload return builder.toString(); } catch (Exception ex) { @@ -356,11 +383,11 @@ private String flushEventsToServer(String payload) { } // get endPointUrl form config object - String endPointUri = config.getEndPointUri() + "v1/batch"; - RudderLogger.logDebug("EventRepository: flushEventsToServer: endPointRepository: " + endPointUri); + String dataPlaneEndPoint = config.getDataPlaneUrl() + "v1/batch"; + RudderLogger.logDebug("EventRepository: flushEventsToServer: dataPlaneEndPoint: " + dataPlaneEndPoint); // create url object - URL url = new URL(endPointUri); + URL url = new URL(dataPlaneEndPoint); // get connection object HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection(); // set connection object to return output @@ -418,11 +445,15 @@ private String flushEventsToServer(String payload) { * generic method for dumping all the events * */ void dump(@NonNull RudderMessage message) { - if (!initiated) return; + if (!isSDKEnabled) return; makeFactoryDump(message); String eventJson = new Gson().toJson(message); RudderLogger.logDebug(String.format(Locale.US, "EventRepository: dump: message: %s", eventJson)); + if (Utils.getUTF8Length(eventJson) > Utils.MAX_EVENT_SIZE) { + RudderLogger.logError(String.format(Locale.US, "EventRepository: dump: Event size exceeds the maximum permitted event size(%d)", Utils.MAX_EVENT_SIZE)); + return; + } dbManager.saveEvent(eventJson); } @@ -465,6 +496,19 @@ void reset() { } } + void flush() { + if (isFactoryInitialized) { + RudderLogger.logDebug("EventRepository: flush native SDKs"); + for (String key : integrationOperationsMap.keySet()) { + RudderLogger.logDebug(String.format(Locale.US, "EventRepository: flush for %s", key)); + RudderIntegration integration = integrationOperationsMap.get(key); + if (integration != null) { + integration.flush(); + } + } + } + } + void onIntegrationReady(String key, RudderClient.Callback callback) { RudderLogger.logDebug(String.format(Locale.US, "EventRepository: onIntegrationReady: callback registered for %s", key)); integrationCallbacks.put(key, callback); @@ -502,8 +546,6 @@ public void onActivityStarted(@NonNull Activity activity) { } } - private int noOfActivities; - @Override public void onActivityResumed(@NonNull Activity activity) { diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/RudderClient.java b/core/src/main/java/com/rudderstack/android/sdk/core/RudderClient.java index 40e674c47..60d1cedb2 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/RudderClient.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/RudderClient.java @@ -77,9 +77,9 @@ public static RudderClient getInstance(@NonNull Context context, @Nullable Strin config = new RudderConfig(); } else { RudderLogger.logVerbose("getInstance: config present. using config."); - if (TextUtils.isEmpty(config.getEndPointUri())) { + if (TextUtils.isEmpty(config.getDataPlaneUrl())) { RudderLogger.logVerbose("getInstance: EndPointUri is blank or null. using default."); - config.setEndPointUri(Constants.BASE_URL); + config.setDataPlaneUrl(Constants.DATA_PLANE_URL); } if (config.getFlushQueueSize() < 0 || config.getFlushQueueSize() > 100) { RudderLogger.logVerbose("getInstance: FlushQueueSize is wrong. using default."); @@ -411,6 +411,13 @@ public void reset() { if (repository != null) repository.reset(); } + /** + * Flush Events + */ + public void flush() { + if (repository != null) repository.flush(); + } + /** * Register Native SDK callback for custom implementation * diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/RudderConfig.java b/core/src/main/java/com/rudderstack/android/sdk/core/RudderConfig.java index 6611c834c..c7e35a3f2 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/RudderConfig.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/RudderConfig.java @@ -23,12 +23,13 @@ * - configRefreshInterval -> time in hours as interval of downloading config from config server * - trackLifecycleEvents -> whether track lifecycle events automatically * - recordScreenViews -> whether we should record screen views automatically + * - controlPlaneUrl -> link to self-hosted sourceConfig * * default values are set at Constants file * * */ public class RudderConfig { - private String endPointUri; + private String dataPlaneUrl; private int flushQueueSize; private int dbCountThreshold; private int sleepTimeOut; @@ -36,11 +37,12 @@ public class RudderConfig { private int configRefreshInterval; private boolean trackLifecycleEvents; private boolean recordScreenViews; + private String controlPlaneUrl; private List factories; RudderConfig() { this( - Constants.BASE_URL, + Constants.DATA_PLANE_URL, Constants.FLUSH_QUEUE_SIZE, Constants.DB_COUNT_THRESHOLD, Constants.SLEEP_TIMEOUT, @@ -48,12 +50,13 @@ public class RudderConfig { Constants.CONFIG_REFRESH_INTERVAL, Constants.TRACK_LIFECYCLE_EVENTS, Constants.RECORD_SCREEN_VIEWS, + Constants.CONTROL_PLANE_URL, null ); } private RudderConfig( - String endPointUri, + String dataPlaneUrl, int flushQueueSize, int dbCountThreshold, int sleepTimeOut, @@ -61,19 +64,20 @@ private RudderConfig( int configRefreshInterval, boolean trackLifecycleEvents, boolean recordScreenViews, + String controlPlaneUrl, List factories ) { RudderLogger.init(logLevel); - if (TextUtils.isEmpty(endPointUri)) { + if (TextUtils.isEmpty(dataPlaneUrl)) { RudderLogger.logError("endPointUri can not be null or empty. Set to default."); - this.endPointUri = Constants.BASE_URL; - } else if (!URLUtil.isValidUrl(endPointUri)) { + this.dataPlaneUrl = Constants.DATA_PLANE_URL; + } else if (!URLUtil.isValidUrl(dataPlaneUrl)) { RudderLogger.logError("Malformed endPointUri. Set to default"); - this.endPointUri = Constants.BASE_URL; + this.dataPlaneUrl = Constants.DATA_PLANE_URL; } else { - if (!endPointUri.endsWith("/")) endPointUri += "/"; - this.endPointUri = endPointUri; + if (!dataPlaneUrl.endsWith("/")) dataPlaneUrl += "/"; + this.dataPlaneUrl = dataPlaneUrl; } if (flushQueueSize < Utils.MIN_FLUSH_QUEUE_SIZE || flushQueueSize > Utils.MAX_FLUSH_QUEUE_SIZE) { @@ -113,14 +117,33 @@ private RudderConfig( if (factories != null && !factories.isEmpty()) { this.factories = factories; } + + if (TextUtils.isEmpty(controlPlaneUrl)) { + RudderLogger.logError("configPlaneUrl can not be null or empty. Set to default."); + this.controlPlaneUrl = Constants.CONTROL_PLANE_URL; + } else if (!URLUtil.isValidUrl(controlPlaneUrl)) { + RudderLogger.logError("Malformed configPlaneUrl. Set to default"); + this.controlPlaneUrl = Constants.CONTROL_PLANE_URL; + } else { + if (!controlPlaneUrl.endsWith("/")) controlPlaneUrl += "/"; + this.controlPlaneUrl = controlPlaneUrl; + } } /** * @return endPointUrl (your data-plane url) + * @deprecated use getDataPlaneUrl() */ @NonNull public String getEndPointUri() { - return endPointUri; + return dataPlaneUrl; + } + + /** + * @return dataPlaneUrl (your data-plane url) + */ + public String getDataPlaneUrl() { + return dataPlaneUrl; } /** @@ -181,8 +204,27 @@ public List getFactories() { return factories; } - void setEndPointUri(String endPointUri) { - this.endPointUri = endPointUri; + /** + * @return configPlaneUrl (Link to your hosted version of source-config) + * @deprecated use getControlPlaneUrl() + */ + public String getConfigPlaneUrl() { + return controlPlaneUrl; + } + + /** + * @return controlPlaneUrl (Link to your hosted version of source-config) + */ + public String getControlPlaneUrl() { + return controlPlaneUrl; + } + + void setDataPlaneUrl(String dataPlaneUrl) { + this.dataPlaneUrl = dataPlaneUrl; + } + + void setControlPlaneUrl(String controlPlaneUrl) { + this.controlPlaneUrl = controlPlaneUrl; } void setFlushQueueSize(int flushQueueSize) { @@ -223,7 +265,7 @@ void setRecordScreenViews(boolean recordScreenViews) { @Override @NonNull public String toString() { - return String.format(Locale.US, "RudderConfig: endPointUrl:%s | flushQueueSize: %d | dbCountThreshold: %d | sleepTimeOut: %d | logLevel: %d", endPointUri, flushQueueSize, dbCountThreshold, sleepTimeOut, logLevel); + return String.format(Locale.US, "RudderConfig: endPointUrl:%s | flushQueueSize: %d | dbCountThreshold: %d | sleepTimeOut: %d | logLevel: %d", dataPlaneUrl, flushQueueSize, dbCountThreshold, sleepTimeOut, logLevel); } @@ -260,11 +302,12 @@ public Builder withFactories(@NonNull RudderIntegration.Factory... factories) { return this; } - private String endPointUri = Constants.BASE_URL; + private String dataPlaneUrl = Constants.DATA_PLANE_URL; /** * @param endPointUri Your data-plane Url * @return RudderConfig.Builder + * @deprecated use withDataPlaneUrl(String dataPlaneUrl) */ public Builder withEndPointUri(@NonNull String endPointUri) { if (TextUtils.isEmpty(endPointUri)) { @@ -275,7 +318,24 @@ public Builder withEndPointUri(@NonNull String endPointUri) { RudderLogger.logError("Malformed endPointUri. Set to default"); return this; } - this.endPointUri = endPointUri; + this.dataPlaneUrl = endPointUri; + return this; + } + + /** + * @param dataPlaneUrl Your data-plane Url + * @return RudderConfig.Builder + */ + public Builder withDataPlaneUrl(@NonNull String dataPlaneUrl) { + if (TextUtils.isEmpty(dataPlaneUrl)) { + RudderLogger.logError("endPointUri can not be null or empty. Set to default"); + return this; + } + if (!URLUtil.isValidUrl(dataPlaneUrl)) { + RudderLogger.logError("Malformed endPointUri. Set to default"); + return this; + } + this.dataPlaneUrl = dataPlaneUrl; return this; } @@ -374,7 +434,28 @@ public Builder withRecordScreenViews(boolean shouldRecordScreenViews) { */ public Builder withTrackLifecycleEvents(boolean shouldTrackLifecycleEvents) { this.trackLifecycleEvents = shouldTrackLifecycleEvents; - return this; + return this; + } + + private String controlPlaneUrl = Constants.CONTROL_PLANE_URL; + + /** + * @param configPlaneUrl Your hosted version of sourceConfig + * @return RudderConfig.Builder + * @deprecated use withControlPlaneUrl(String controlPlaneUrl) + */ + public Builder withConfigPlaneUrl(String configPlaneUrl) { + this.controlPlaneUrl = configPlaneUrl; + return this; + } + + /** + * @param controlPlaneUrl Your hosted version of sourceConfig + * @return RudderConfig.Builder + */ + public Builder withControlPlaneUrl(String controlPlaneUrl) { + this.controlPlaneUrl = controlPlaneUrl; + return this; } /** @@ -384,7 +465,7 @@ public Builder withTrackLifecycleEvents(boolean shouldTrackLifecycleEvents) { */ public RudderConfig build() { return new RudderConfig( - this.endPointUri, + this.dataPlaneUrl, this.flushQueueSize, this.dbThresholdCount, this.sleepTimeout, @@ -392,7 +473,9 @@ public RudderConfig build() { this.configRefreshInterval, this.trackLifecycleEvents, this.recordScreenViews, - this.factories); + this.controlPlaneUrl, + this.factories + ); } } } diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/RudderContext.java b/core/src/main/java/com/rudderstack/android/sdk/core/RudderContext.java index 38ef746de..891de2611 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/RudderContext.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/RudderContext.java @@ -9,7 +9,7 @@ import java.util.Locale; import java.util.Map; -class RudderContext { +public class RudderContext { @SerializedName("app") private RudderApp app; @SerializedName("traits") @@ -89,7 +89,7 @@ void persistTraits() { } } - Map getTraits() { + public Map getTraits() { return traits; } @@ -100,4 +100,8 @@ void updateTraitsMap(Map traits) { String getDeviceId() { return deviceInfo.getDeviceId(); } + + public void putDeviceToken(String token) { + this.deviceInfo.setToken(token); + } } diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/RudderDeviceInfo.java b/core/src/main/java/com/rudderstack/android/sdk/core/RudderDeviceInfo.java index c097de01a..910d91a54 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/RudderDeviceInfo.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/RudderDeviceInfo.java @@ -1,6 +1,7 @@ package com.rudderstack.android.sdk.core; import android.os.Build; + import com.google.gson.annotations.SerializedName; class RudderDeviceInfo { @@ -14,6 +15,8 @@ class RudderDeviceInfo { private String name = Build.DEVICE; @SerializedName("type") private String type = "android"; + @SerializedName("token") + private String token; RudderDeviceInfo(String deviceId) { this.deviceId = deviceId; @@ -22,4 +25,8 @@ class RudderDeviceInfo { String getDeviceId() { return deviceId; } + + public void setToken(String token) { + this.token = token; + } } diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/RudderIntegration.java b/core/src/main/java/com/rudderstack/android/sdk/core/RudderIntegration.java index 894047929..10f872065 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/RudderIntegration.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/RudderIntegration.java @@ -11,6 +11,8 @@ public interface Factory { public abstract void dump(RudderMessage element); + public void flush() { + } /** * @return Instance of the initiated SDK */ diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/RudderLogger.java b/core/src/main/java/com/rudderstack/android/sdk/core/RudderLogger.java index b927796b8..5c8fb8a22 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/RudderLogger.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/RudderLogger.java @@ -15,17 +15,17 @@ static void init(int l) { logLevel = l; } - static void logError(Throwable throwable) { + public static void logError(Throwable throwable) { if (logLevel >= RudderLogLevel.ERROR) { Log.e(TAG, "Error: ", throwable); } } - static void logError(Exception ex) { + public static void logError(Exception ex) { logError(ex.getMessage()); } - static void logError(String message) { + public static void logError(String message) { if (logLevel >= RudderLogLevel.ERROR) { Log.e(TAG, "Error: " + message); } @@ -37,19 +37,19 @@ public static void logWarn(String message) { } } - static void logInfo(String message) { + public static void logInfo(String message) { if (logLevel >= RudderLogLevel.INFO) { Log.i(TAG, "Info: " + message); } } - static void logDebug(String message) { + public static void logDebug(String message) { if (logLevel >= RudderLogLevel.DEBUG) { Log.d(TAG, "Debug: " + message); } } - static void logVerbose(String message) { + public static void logVerbose(String message) { if (logLevel >= RudderLogLevel.VERBOSE) { Log.v(TAG, "Verbose: " + message); } diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/RudderServerConfigManager.java b/core/src/main/java/com/rudderstack/android/sdk/core/RudderServerConfigManager.java index 6929e4fd4..7f846a0ef 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/RudderServerConfigManager.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/RudderServerConfigManager.java @@ -76,7 +76,7 @@ public void run() { int retryCount = 0, retryTimeOut = 10; while (!isDone && retryCount <= 3) { try { - String configUrl = Constants.CONFIG_PLANE_URL + "/sourceConfig"; + String configUrl = rudderConfig.getControlPlaneUrl() + "sourceConfig"; RudderLogger.logDebug(String.format(Locale.US, "RudderServerConfigManager: downloadConfig: configUrl: %s", configUrl)); // create url object URL url = new URL(configUrl); diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/RudderTraits.java b/core/src/main/java/com/rudderstack/android/sdk/core/RudderTraits.java index 5cc3c2a1c..2105e6178 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/RudderTraits.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/RudderTraits.java @@ -1,5 +1,6 @@ package com.rudderstack.android.sdk.core; +import com.google.gson.Gson; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; import com.rudderstack.android.sdk.core.util.Utils; @@ -46,16 +47,255 @@ public class RudderTraits { @Expose(serialize = false) private transient Map extras; + private static final String ANONYMOUSID_KEY = "anonymousid"; + private static final String ADDRESS_KEY = "address"; + private static final String AGE_KEY = "age"; + private static final String BIRTHDAY_KEY = "birthday"; + private static final String COMPANY_KEY = "company"; + private static final String CREATEDAT_KEY = "createdat"; + private static final String DESCRIPTION_KEY = "description"; + private static final String EMAIL_KEY = "email"; + private static final String FIRSTNAME_KEY = "firstname"; + private static final String GENDER_KEY = "gender"; + private static final String USERID_KEY = "userid"; + private static final String LASTNAME_KEY = "lastname"; + private static final String NAME_KEY = "name"; + private static final String PHONE_KEY = "phone"; + private static final String TITLE_KEY = "title"; + private static final String USERNAME_KEY = "username"; + + /** + * Get Anonymous Id from traits + * + * @param traitsMap Map + * @return anonymousId String + */ + public static String getAnonymousId(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(ANONYMOUSID_KEY)) + return (String) traitsMap.get(ANONYMOUSID_KEY); + return null; + } + + /** + * Get Address from traits + * + * @param traitsMap Map + * @return address String + */ + public static String getAddress(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(ADDRESS_KEY)) + return new Gson().toJson(traitsMap.get(ADDRESS_KEY)); + return null; + } + + /** + * Get Age from traits + * + * @param traitsMap Map + * @return age String + */ + public static String getAge(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(AGE_KEY)) + return (String) traitsMap.get(AGE_KEY); + return null; + } + + /** + * Get Birthday from traits + * + * @param traitsMap Map + * @return birthday String + */ + public static String getBirthday(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(BIRTHDAY_KEY)) + return (String) traitsMap.get(BIRTHDAY_KEY); + return null; + } + + /** + * Get Company from traits + * + * @param traitsMap Map + * @return company String + */ + public static String getCompany(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(COMPANY_KEY)) + return (String) traitsMap.get(COMPANY_KEY); + return null; + } + + /** + * Get createdAt from traits + * + * @param traitsMap Map + * @return created_at String + */ + public static String getCreatedAt(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(CREATEDAT_KEY)) + return (String) traitsMap.get(CREATEDAT_KEY); + return null; + } + + /** + * Get description from traits + * + * @param traitsMap Map + * @return description String + */ + public static String getDescription(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(DESCRIPTION_KEY)) + return (String) traitsMap.get(DESCRIPTION_KEY); + return null; + } + + /** + * Get First Name from traits + * + * @param traitsMap Map + * @return firstName String + */ + public static String getFirstname(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(FIRSTNAME_KEY)) + return (String) traitsMap.get(FIRSTNAME_KEY); + return null; + } + + /** + * Get email from traits + * + * @param traitsMap Map + * @return email String + */ + public static String getEmail(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(EMAIL_KEY)) + return (String) traitsMap.get(EMAIL_KEY); + return null; + } + + /** + * Get gender from traits + * + * @param traitsMap Map + * @return gender String + */ + public static String getGender(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(GENDER_KEY)) + return (String) traitsMap.get(GENDER_KEY); + return null; + } + + /** + * Get user id from traits + * + * @param traitsMap Map + * @return userId String + */ + public static String getUserid(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(USERID_KEY)) + return (String) traitsMap.get(USERID_KEY); + return null; + } + + /** + * Get Last Name from traits + * + * @param traitsMap Map + * @return lastName String + */ + public static String getLastname(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(LASTNAME_KEY)) + return (String) traitsMap.get(LASTNAME_KEY); + return null; + } + + /** + * Get name from traits + * + * @param traitsMap Map + * @return name String + */ + public static String getName(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(NAME_KEY)) + return (String) traitsMap.get(NAME_KEY); + return null; + } + + /** + * Get phone from traits + * + * @param traitsMap Map + * @return phone String + */ + public static String getPhone(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(PHONE_KEY)) + return (String) traitsMap.get(PHONE_KEY); + return null; + } + + /** + * Get title from traits + * + * @param traitsMap Map + * @return title String + */ + public static String getTitle(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(TITLE_KEY)) + return (String) traitsMap.get(TITLE_KEY); + return null; + } + + /** + * Get user name from traits + * + * @param traitsMap Map + * @return userName String + */ + public static String getUsername(Map traitsMap) { + if (traitsMap != null & traitsMap.containsKey(USERNAME_KEY)) + return (String) traitsMap.get(USERNAME_KEY); + return null; + } + + + /** + * constructor + */ public RudderTraits() { RudderContext rudderContext = RudderElementCache.getCachedContext(); if (rudderContext != null) this.anonymousId = rudderContext.getDeviceId(); } + + /** + * constructor + * + * @param anonymousId String + */ RudderTraits(String anonymousId) { this.anonymousId = anonymousId; } + /** + * Initialise RudderTraits + * + * @param address Address + * @param age String + * @param birthday String + * @param company Company + * @param createdAt String + * @param description String + * @param email String + * @param firstName String + * @param gender String + * @param id String + * @param lastName String + * @param name String + * @param phone String + * @param title + * @param userName String + */ public RudderTraits(Address address, String age, String birthday, Company company, String createdAt, String description, String email, String firstName, String gender, String id, String lastName, String name, String phone, String title, String userName) { + this.anonymousId = RudderElementCache.getCachedContext().getDeviceId(); this.address = address; this.age = age; this.birthday = birthday; @@ -74,95 +314,209 @@ public RudderTraits(Address address, String age, String birthday, Company compan this.userName = userName; } + /** + * Get Id + * + * @return id String + */ public String getId() { return id; } + /** + * Get Extras + * + * @return map Map + */ public Map getExtras() { return extras; } + + /** + * Put Address + * + * @param address Address + * @return traits RudderTraits + */ public RudderTraits putAddress(Address address) { this.address = address; return this; } + /** + * put Age + * + * @param age String + * @return traits RudderTraits + */ public RudderTraits putAge(String age) { this.age = age; return this; } + /** + * put Birthday + * + * @param birthday String + * @return traits RudderTraits + */ public RudderTraits putBirthday(String birthday) { this.birthday = birthday; return this; } + /** + * put Birthday as Date + * + * @param birthday Date + * @return traits RudderTraits + */ public RudderTraits putBirthday(Date birthday) { this.birthday = Utils.toDateString(birthday); return this; } + /** + * put Company + * + * @param company Company + * @return traits RudderTraits + */ public RudderTraits putCompany(Company company) { this.company = company; return this; } + /** + * put Created At + * + * @param createdAt String + * @return traits RudderTraits + */ public RudderTraits putCreatedAt(String createdAt) { this.createdAt = createdAt; return this; } + /** + * put description + * + * @param description String + * @return traits RudderTraits + */ public RudderTraits putDescription(String description) { this.description = description; return this; } + /** + * put email + * + * @param email String + * @return traits RudderTraits + */ public RudderTraits putEmail(String email) { this.email = email; return this; } + /** + * put First Name + * + * @param firstName String + * @return traits RudderTraits + */ public RudderTraits putFirstName(String firstName) { this.firstName = firstName; return this; } + /** + * put gender + * + * @param gender String + * @return traits RudderTraits + */ public RudderTraits putGender(String gender) { this.gender = gender; return this; } + /** + * put id + * + * @param id String + * @return traits RudderTraits + */ public RudderTraits putId(String id) { this.id = id; this.oldId = id; return this; } + /** + * put Last Name + * + * @param lastName String + * @return traits RudderTraits + */ public RudderTraits putLastName(String lastName) { this.lastName = lastName; return this; } + /** + * put name + * + * @param name String + * @return traits RudderTraits + */ public RudderTraits putName(String name) { this.name = name; return this; } + /** + * put phone + * + * @param phone String + * @return traits RudderTraits + */ public RudderTraits putPhone(String phone) { this.phone = phone; return this; } + /** + * put title + * + * @param title String + * @return traits RudderTraits + */ public RudderTraits putTitle(String title) { this.title = title; return this; } + /** + * put User Name + * + * @param userName String + * @return traits RudderTraits + */ public RudderTraits putUserName(String userName) { this.userName = userName; return this; } + /** + * put generic key value pairs + * + * @param key String + * @param value Object + * @return traits RudderTraits + */ public RudderTraits put(String key, Object value) { if (this.extras == null) { this.extras = new HashMap<>(); @@ -183,34 +537,118 @@ public static class Address { @SerializedName("street") private String street; + /** + * get city + * + * @return city String + */ + public String getCity() { + return city; + } + + /** + * get country + * + * @return country String + */ + public String getCountry() { + return country; + } + + /** + * get postal code + * + * @return postalCode String + */ + public String getPostalCode() { + return postalCode; + } + + /** + * get state + * + * @return state String + */ + public String getState() { + return state; + } + + /** + * get street + * + * @return street String + */ + public String getStreet() { + return street; + } + public Address() { } + /** + * put city + * + * @param city String + * @return address Address + */ public Address putCity(String city) { this.city = city; return this; } + /** + * put country + * + * @param country String + * @return address Address + */ public Address putCountry(String country) { this.country = country; return this; } + /** + * put postal code + * + * @param postalCode String + * @return address Address + */ public Address putPostalCode(String postalCode) { this.postalCode = postalCode; return this; } + /** + * put state String + * + * @param state String + * @return address Address + */ public Address putState(String state) { this.state = state; return this; } + /** + * put street String + * + * @param street String + * @return address Address + */ public Address putStreet(String street) { this.street = street; return this; } + /** + * constructor + * + * @param city String + * @param country String + * @param postalCode String + * @param state String + * @param street String + */ public Address(String city, String country, String postalCode, String state, String street) { this.city = city; this.country = country; @@ -218,6 +656,16 @@ public Address(String city, String country, String postalCode, String state, Str this.state = state; this.street = street; } + + /** + * make address from String + * + * @param address String + * @return address Address + */ + public static Address fromString(String address) { + return new Gson().fromJson(address, Address.class); + } } public static class Company { @@ -228,25 +676,50 @@ public static class Company { @SerializedName("industry") private String industry; + /** + * constructor + * + * @param name String + * @param id String + * @param industry String + */ Company(String name, String id, String industry) { this.name = name; this.id = id; this.industry = industry; } + /** + * put name + * + * @param name String + * @return company Company + */ public Company putName(String name) { this.name = name; return this; } + /** + * put company Id + * + * @param id String + * @return company Company + */ public Company putId(String id) { this.id = id; return this; } + /** + * put industry + * + * @param industry String + * @return company Company + */ public Company putIndustry(String industry) { this.industry = industry; return this; } } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/rudderstack/android/sdk/core/util/Utils.java b/core/src/main/java/com/rudderstack/android/sdk/core/util/Utils.java index 83b97a837..9c40225bd 100644 --- a/core/src/main/java/com/rudderstack/android/sdk/core/util/Utils.java +++ b/core/src/main/java/com/rudderstack/android/sdk/core/util/Utils.java @@ -7,7 +7,10 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; +import com.rudderstack.android.sdk.core.RudderLogger; +import java.io.UnsupportedEncodingException; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; @@ -27,6 +30,8 @@ public class Utils { public static final int MIN_SLEEP_TIMEOUT = 10; public static final int MIN_FLUSH_QUEUE_SIZE = 1; public static final int MAX_FLUSH_QUEUE_SIZE = 100; + public static final int MAX_EVENT_SIZE = 32 * 1024; // 32 KB + public static final int MAX_BATCH_SIZE = 500 * 1024; // 500 KB public static String getTimeZone() { TimeZone timeZone = TimeZone.getDefault(); @@ -82,4 +87,19 @@ public static String getWriteKeyFromStrings(Context context) { return null; } } + + public static int getUTF8Length(String message) { + int utf8Length; + try { + utf8Length = message.getBytes("UTF-8").length; + } catch (UnsupportedEncodingException ex) { + RudderLogger.logError(ex); + utf8Length = -1; + } + return utf8Length; + } + + public static int getUTF8Length(StringBuilder message) { + return getUTF8Length(message.toString()); + } } diff --git a/integration-dummy-impl/.gitignore b/dummy-impl/.gitignore similarity index 100% rename from integration-dummy-impl/.gitignore rename to dummy-impl/.gitignore diff --git a/integration-dummy-impl/build.gradle b/dummy-impl/build.gradle similarity index 100% rename from integration-dummy-impl/build.gradle rename to dummy-impl/build.gradle diff --git a/integration-dummy-impl/proguard-rules.pro b/dummy-impl/proguard-rules.pro similarity index 100% rename from integration-dummy-impl/proguard-rules.pro rename to dummy-impl/proguard-rules.pro diff --git a/integration-dummy-impl/src/main/AndroidManifest.xml b/dummy-impl/src/main/AndroidManifest.xml similarity index 100% rename from integration-dummy-impl/src/main/AndroidManifest.xml rename to dummy-impl/src/main/AndroidManifest.xml diff --git a/integration-dummy-impl/src/main/java/com/rudderstack/android/integration/dummy/DummyGAIntegration.java b/dummy-impl/src/main/java/com/rudderstack/android/integration/dummy/DummyGAIntegration.java similarity index 100% rename from integration-dummy-impl/src/main/java/com/rudderstack/android/integration/dummy/DummyGAIntegration.java rename to dummy-impl/src/main/java/com/rudderstack/android/integration/dummy/DummyGAIntegration.java diff --git a/integration-dummy-impl/src/main/java/com/rudderstack/android/integration/dummy/DummyGAIntegrationImpl.java b/dummy-impl/src/main/java/com/rudderstack/android/integration/dummy/DummyGAIntegrationImpl.java similarity index 100% rename from integration-dummy-impl/src/main/java/com/rudderstack/android/integration/dummy/DummyGAIntegrationImpl.java rename to dummy-impl/src/main/java/com/rudderstack/android/integration/dummy/DummyGAIntegrationImpl.java diff --git a/integration-dummy-impl/src/main/res/values/strings.xml b/dummy-impl/src/main/res/values/strings.xml similarity index 100% rename from integration-dummy-impl/src/main/res/values/strings.xml rename to dummy-impl/src/main/res/values/strings.xml diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 494b50f6c..f4cc6ddf8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Aug 22 14:56:15 IST 2019 +#Mon Mar 02 17:09:51 IST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/sample-kotlin-integration/.gitignore b/sample-kotlin-integration/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/sample-kotlin-integration/.gitignore @@ -0,0 +1 @@ +/build diff --git a/sample-kotlin-integration/build.gradle b/sample-kotlin-integration/build.gradle new file mode 100644 index 000000000..5aa250a39 --- /dev/null +++ b/sample-kotlin-integration/build.gradle @@ -0,0 +1,48 @@ +buildscript { + ext.kotlin_version = '1.3.41' + repositories { + google() + jcenter() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.70" + } +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.2" + defaultConfig { + applicationId "com.rudderstack.integration.test.firebase" + minSdkVersion 16 + targetSdkVersion 29 + versionCode 4 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.core:core-ktx:1.2.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'com.google.android.material:material:1.1.0' + + implementation project(':core') + + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +} diff --git a/sample-kotlin-integration/proguard-rules.pro b/sample-kotlin-integration/proguard-rules.pro new file mode 100644 index 000000000..f1b424510 --- /dev/null +++ b/sample-kotlin-integration/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/sample-kotlin-integration/src/main/AndroidManifest.xml b/sample-kotlin-integration/src/main/AndroidManifest.xml new file mode 100644 index 000000000..794ba2988 --- /dev/null +++ b/sample-kotlin-integration/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample-kotlin-integration/src/main/java/com/rudderstack/android/sample/kotlin/FirstActivity.kt b/sample-kotlin-integration/src/main/java/com/rudderstack/android/sample/kotlin/FirstActivity.kt new file mode 100644 index 000000000..6e3358f8f --- /dev/null +++ b/sample-kotlin-integration/src/main/java/com/rudderstack/android/sample/kotlin/FirstActivity.kt @@ -0,0 +1,17 @@ +package com.rudderstack.android.sample.kotlin + +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import kotlinx.android.synthetic.main.activity_first.* + +class FirstActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_first) + + navigate_to_second.setOnClickListener { + startActivity(Intent(this, SecondActivity::class.java)) + } + } +} \ No newline at end of file diff --git a/sample-kotlin-integration/src/main/java/com/rudderstack/android/sample/kotlin/MainActivity.kt b/sample-kotlin-integration/src/main/java/com/rudderstack/android/sample/kotlin/MainActivity.kt new file mode 100644 index 000000000..0d13aefdd --- /dev/null +++ b/sample-kotlin-integration/src/main/java/com/rudderstack/android/sample/kotlin/MainActivity.kt @@ -0,0 +1,109 @@ +package com.rudderstack.android.sample.kotlin + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.rudderstack.android.sdk.core.RudderMessageBuilder +import com.rudderstack.android.sdk.core.ecomm.* + + +class MainActivity : AppCompatActivity() { + private var count = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + val rudderClient = MainApplication.rudderClient + + val properties: MutableMap = mutableMapOf() + properties["test_key_1"] = "test_value_1" + + val childProperties: MutableMap = HashMap() + childProperties["test_child_key_1"] = "test_child_value_1" + properties["test_key_2"] = childProperties + properties["category"] = "test_category" + + rudderClient!!.track( + RudderMessageBuilder() + .setEventName("WebSite_test") + .setUserId("test_user_id") + .setProperty(properties) + .build() + ) + + // ECommerce Product + val productA = ECommerceProduct.Builder() + .withProductId("some_product_id_a") + .withSku("some_product_sku_a") + .withCategory("some_category") + .withName("Product Name A") + .withBrand("Product Brand A") + .withVariant("Product Variant A") + .withPrice(2.99f) + .withCurrency("USD") + .withQuantity(1f) + .withCoupon("some_coupon") + .withPosition(1) + .withUrl("https://product.com/productA") + .withImageUrl("https://product.com/productA.jpg") + .build() + + val productB = ECommerceProduct.Builder() + .withProductId("some_product_id_b") + .withSku("some_product_sku_b") + .withCurrency("USD") + .withPrice(3.99f) + .withName("Some Product Name B") + .withQuantity(1f) + .build() + + val productC = ECommerceProduct.Builder() + .withProductId("some_product_id_c") + .withSku("some_product_sku_c") + .withCurrency("USD") + .withPrice(4.99f) + .withName("Some Product Name C") + .withQuantity(1f) + .build() + + // ECommerce WishList + val wishList = ECommerceWishList.Builder() + .withWishListId("some_wish_list_id") + .withWishListName("Some Wish List Name") + .build() + + // ECommerce Cart + val cart = ECommerceCart.Builder() + .withCartId("some_cart_id") + .withProduct(productA) + .withProduct(productB) + .withProduct(productC) + .build() + + // ECommerce Order + val order = ECommerceOrder.Builder() + .withOrderId("some_order_id") + .withAffiliation("some_order_affiliation") + .withCoupon("some_coupon") + .withCurrency("USD") + .withDiscount(1.49f) + .withProducts(productA, productB, productC) + .withRevenue(10.99f) + .withShippingCost(2.49f) + .withTax(1.49f) + .withTotal(12.99f) + .withValue(10.49f) + .build() + + // ECommerce Checkout event + val checkout = ECommerceCheckout.Builder() + .withCheckoutId("some_checkout_id") + .withOrderId("some_order_id") + .withPaymentMethod("Visa") + .withShippingMethod("FedEx") + .withStep(1) + .build() + + rudderClient.identify("test_user_id") + } +} diff --git a/sample-kotlin-integration/src/main/java/com/rudderstack/android/sample/kotlin/MainApplication.kt b/sample-kotlin-integration/src/main/java/com/rudderstack/android/sample/kotlin/MainApplication.kt new file mode 100644 index 000000000..6cdb8e02e --- /dev/null +++ b/sample-kotlin-integration/src/main/java/com/rudderstack/android/sample/kotlin/MainApplication.kt @@ -0,0 +1,81 @@ +package com.rudderstack.android.sample.kotlin + +import android.app.Activity +import android.app.Application +import android.os.Bundle +import android.util.Log +import com.rudderstack.android.sdk.core.RudderClient +import com.rudderstack.android.sdk.core.RudderConfig +import com.rudderstack.android.sdk.core.RudderLogger + +class MainApplication : Application() { + companion object { + var rudderClient: RudderClient? = null + const val TAG = "MainApplication" + const val END_URL = "https://cd740f82.ngrok.io" + const val WRITE_KEY = "1TSRSskqa15PG7F89tkwEbl5Td8" + } + + override fun onCreate() { + super.onCreate() + + rudderClient = RudderClient.getInstance( + this, + WRITE_KEY, + RudderConfig.Builder() + .withDataPlaneUrl(END_URL) + .withLogLevel(RudderLogger.RudderLogLevel.VERBOSE) + .withControlPlaneUrl(END_URL) + .withTrackLifecycleEvents(true) + .withRecordScreenViews(true) + .build() + ) + rudderClient!!.rudderContext.putDeviceToken("Some_device_token") + + registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { + override fun onActivityPaused(p0: Activity) { + Log.d(TAG, "onActivityPaused ${p0.localClassName}") + } + + override fun onActivityStarted(p0: Activity) { + Log.d(TAG, "onActivityStarted ${p0.localClassName}") + } + + override fun onActivityDestroyed(p0: Activity) { + Log.d(TAG, "onActivityDestroyed ${p0.localClassName}") + } + + override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) { + Log.d(TAG, "onActivitySaveInstanceState ${p0.localClassName}, Bundle: $p1") + } + + override fun onActivityStopped(p0: Activity) { + Log.d(TAG, "onActivityStopped ${p0.localClassName}") + } + + override fun onActivityCreated(p0: Activity, p1: Bundle?) { + Log.d(TAG, "onActivityCreated ${p0.localClassName} Bundle: ${p1.toString()}") + } + + override fun onActivityResumed(p0: Activity) { + Log.d(TAG, "onActivityResumed ${p0.localClassName}") + } + + }) + } + + override fun onLowMemory() { + super.onLowMemory() + Log.d(TAG, "onLowMemory") + } + + override fun onTerminate() { + super.onTerminate() + Log.d(TAG, "onTerminate") + } + + override fun onTrimMemory(level: Int) { + super.onTrimMemory(level) + Log.d(TAG, "onTrimMemory: $level") + } +} \ No newline at end of file diff --git a/sample-kotlin-integration/src/main/java/com/rudderstack/android/sample/kotlin/SecondActivity.kt b/sample-kotlin-integration/src/main/java/com/rudderstack/android/sample/kotlin/SecondActivity.kt new file mode 100644 index 000000000..fd29494cc --- /dev/null +++ b/sample-kotlin-integration/src/main/java/com/rudderstack/android/sample/kotlin/SecondActivity.kt @@ -0,0 +1,11 @@ +package com.rudderstack.android.sample.kotlin + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity + +class SecondActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_second) + } +} \ No newline at end of file diff --git a/sample-kotlin-integration/src/main/res/drawable-v24/ic_launcher_foreground.xml b/sample-kotlin-integration/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 000000000..6348baae3 --- /dev/null +++ b/sample-kotlin-integration/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/sample-kotlin-integration/src/main/res/drawable/ic_launcher_background.xml b/sample-kotlin-integration/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..a0ad202f9 --- /dev/null +++ b/sample-kotlin-integration/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sample-kotlin-integration/src/main/res/layout/activity_first.xml b/sample-kotlin-integration/src/main/res/layout/activity_first.xml new file mode 100644 index 000000000..13f8f1841 --- /dev/null +++ b/sample-kotlin-integration/src/main/res/layout/activity_first.xml @@ -0,0 +1,13 @@ + + + +