diff --git a/android/build.gradle b/android/build.gradle index aef261f..04cf221 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -103,6 +103,7 @@ repositories { ktlint { outputToConsole = true + verbose = true } dependencies { diff --git a/android/src/main/java/com/mparticle/react/MParticleModule.java b/android/src/main/java/com/mparticle/react/MParticleModule.java deleted file mode 100644 index 8f93f8f..0000000 --- a/android/src/main/java/com/mparticle/react/MParticleModule.java +++ /dev/null @@ -1,988 +0,0 @@ -package com.mparticle.react; - -import android.location.Location; -import android.util.Log; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContextBaseJavaModule; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.ReadableMapKeySetIterator; -import com.facebook.react.bridge.Callback; -import com.facebook.react.bridge.ReadableType; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.bridge.WritableNativeMap; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableNativeArray; -import com.mparticle.AttributionResult; -import com.mparticle.MParticle; -import com.mparticle.MPEvent; -import com.mparticle.Session; -import com.mparticle.UserAttributeListenerType; -import com.mparticle.commerce.CommerceEvent; -import com.mparticle.commerce.Impression; -import com.mparticle.commerce.Product; -import com.mparticle.commerce.TransactionAttributes; -import com.mparticle.commerce.Promotion; -import com.mparticle.consent.ConsentState; -import com.mparticle.consent.GDPRConsent; -import com.mparticle.consent.CCPAConsent; -import com.mparticle.identity.AliasRequest; -import com.mparticle.identity.IdentityApi; -import com.mparticle.identity.IdentityApiRequest; -import com.mparticle.identity.IdentityApiResult; -import com.mparticle.identity.MParticleUser; -import com.mparticle.identity.IdentityHttpResponse; -import com.mparticle.identity.TaskFailureListener; -import com.mparticle.identity.TaskSuccessListener; -import com.mparticle.internal.Logger; -import com.mparticle.UserAttributeListener; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nullable; - -public class MParticleModule extends ReactContextBaseJavaModule { - - - public static final String MODULE_NAME = "MParticle"; - private final static String LOG_TAG = "MParticleModule"; - - ReactApplicationContext reactContext; - - public MParticleModule(ReactApplicationContext reactContext) { - super(reactContext); - this.reactContext = reactContext; - } - - @Override - public String getName() { - return MODULE_NAME; - } - - @ReactMethod - public void upload() { - MParticle.getInstance().upload(); - } - - @ReactMethod - public void setUploadInterval(int uploadInterval) { - MParticle.getInstance().setUpdateInterval(uploadInterval); - } - - @ReactMethod - public void setLocation(double latitude, double longitude) { - Location newLocation = new Location(""); - newLocation.setLatitude(latitude); - newLocation.setLongitude(longitude); - MParticle.getInstance().setLocation(newLocation); - - } - - @ReactMethod - public void logEvent(final String name, int type, final ReadableMap attributesMap) { - Map attributes = ConvertStringMap(attributesMap); - MParticle.EventType eventType = ConvertEventType(type); - - MPEvent event = new MPEvent.Builder(name, eventType) - .customAttributes(attributes) - .build(); - MParticle.getInstance().logEvent(event); - } - - @ReactMethod - public void logMPEvent(final ReadableMap attributesMap) { - MPEvent event = ConvertMPEvent(attributesMap); - MParticle.getInstance().logEvent(event); - } - - @ReactMethod - public void logCommerceEvent(final ReadableMap map) { - if (map != null) { - CommerceEvent commerceEvent = ConvertCommerceEvent(map); - MParticle.getInstance().logEvent(commerceEvent); - } - } - - @ReactMethod - public void logScreenEvent(final String event, final ReadableMap attributesMap, final boolean shouldUploadEvent) { - Map attributes = ConvertStringMap(attributesMap); - MParticle.getInstance().logScreen(event, attributes, shouldUploadEvent); - } - - @ReactMethod - public void setUserAttribute(final String userId, final String userAttribute, final String value) { - MParticleUser selectedUser = MParticle.getInstance().Identity().getUser(parseMpid(userId)); - if (selectedUser != null) { - selectedUser.setUserAttribute(userAttribute, value); - } - } - - @ReactMethod - public void setUserAttributeArray(final String userId, final String key, final ReadableArray values) { - if (values != null) { - List list = new ArrayList(); - for (int i = 0; i < values.size(); ++i) { - list.add(values.getString(i)); - } - - MParticleUser selectedUser = MParticle.getInstance().Identity().getUser(parseMpid(userId)); - if (selectedUser != null) { - selectedUser.setUserAttributeList(key, list); - } - } - } - - @ReactMethod - public void getUserAttributes(final String userId, final Callback completion) { - MParticleUser selectedUser = MParticle.getInstance().Identity().getUser(parseMpid(userId)); - if (selectedUser != null) { - selectedUser.getUserAttributes(new UserAttributeListener() { - @Override - public void onUserAttributesReceived(Map userAttributes, Map> userAttributeLists, Long mpid) { - WritableMap resultMap = new WritableNativeMap(); - for (Map.Entry entry : userAttributes.entrySet()) { - resultMap.putString(entry.getKey(), entry.getValue()); - } - for (Map.Entry> entry : userAttributeLists.entrySet()) { - WritableArray resultArray = new WritableNativeArray(); - List valueList = entry.getValue(); - for (String arrayVal : valueList) { - resultArray.pushString(arrayVal); - } - resultMap.putArray(entry.getKey(), resultArray); - } - completion.invoke(null, resultMap); - } - }); - } else { - completion.invoke(); - } - } - - @ReactMethod - public void setUserTag(final String userId, final String tag) { - MParticleUser selectedUser = MParticle.getInstance().Identity().getUser(parseMpid(userId)); - if (selectedUser != null) { - selectedUser.setUserTag(tag); - } - } - - @ReactMethod - public void removeUserAttribute(final String userId, final String key) { - MParticleUser selectedUser = MParticle.getInstance().Identity().getUser(parseMpid(userId)); - if (selectedUser != null) { - selectedUser.removeUserAttribute(key); - } - } - - @ReactMethod - public void incrementUserAttribute(final String userId, final String key, final Integer value) { - MParticleUser selectedUser = MParticle.getInstance().Identity().getUser(parseMpid(userId)); - if (selectedUser != null) { - selectedUser.incrementUserAttribute(key, value); - } - } - @ReactMethod - public void identify(final ReadableMap requestMap, final Callback completion) { - IdentityApiRequest request = ConvertIdentityAPIRequest(requestMap); - - MParticle.getInstance().Identity().identify(request) - .addFailureListener(new TaskFailureListener() { - @Override - public void onFailure(IdentityHttpResponse identityHttpResponse) { - completion.invoke(ConvertIdentityHttpResponse(identityHttpResponse), null); - } - }) - .addSuccessListener(new TaskSuccessListener() { - @Override - public void onSuccess(IdentityApiResult identityApiResult) { - //Continue with login, and you can also access the new/updated user: - MParticleUser user = identityApiResult.getUser(); - String userID = Long.toString(user.getId()); - completion.invoke(null, userID); - } - }); - } - - - @ReactMethod - public void login(final ReadableMap requestMap, final Callback completion) { - IdentityApiRequest request = ConvertIdentityAPIRequest(requestMap); - - MParticle.getInstance().Identity().login(request) - .addFailureListener(new TaskFailureListener() { - @Override - public void onFailure(IdentityHttpResponse identityHttpResponse) { - completion.invoke(ConvertIdentityHttpResponse(identityHttpResponse), null); - } - }) - .addSuccessListener(new TaskSuccessListener() { - @Override - public void onSuccess(IdentityApiResult identityApiResult) { - //Continue with login, and you can also access the new/updated user: - MParticleUser user = identityApiResult.getUser(); - String userId = Long.toString(user.getId()); - MParticleUser previousUser = identityApiResult.getPreviousUser(); - String previousUserId = null; - if (previousUser != null) { - previousUserId = Long.toString(previousUser.getId()); - } - completion.invoke(null, userId, previousUserId); - } - }); - } - - - @ReactMethod - public void logout(final ReadableMap requestMap, final Callback completion) { - IdentityApiRequest request = ConvertIdentityAPIRequest(requestMap); - - MParticle.getInstance().Identity().logout(request) - .addFailureListener(new TaskFailureListener() { - @Override - public void onFailure(IdentityHttpResponse identityHttpResponse) { - completion.invoke(ConvertIdentityHttpResponse(identityHttpResponse), null); - } - }) - .addSuccessListener(new TaskSuccessListener() { - @Override - public void onSuccess(IdentityApiResult identityApiResult) { - //Continue with login, and you can also access the new/updated user: - MParticleUser user = identityApiResult.getUser(); - String userID = Long.toString(user.getId()); - completion.invoke(null, userID); - } - }); - } - - @ReactMethod - public void modify(final ReadableMap requestMap, final Callback completion) { - IdentityApiRequest request = ConvertIdentityAPIRequest(requestMap); - - MParticle.getInstance().Identity().modify(request) - .addFailureListener(new TaskFailureListener() { - @Override - public void onFailure(IdentityHttpResponse identityHttpResponse) { - completion.invoke(ConvertIdentityHttpResponse(identityHttpResponse), null); - } - }) - .addSuccessListener(new TaskSuccessListener() { - @Override - public void onSuccess(IdentityApiResult identityApiResult) { - //Continue with login, and you can also access the new/updated user: - MParticleUser user = identityApiResult.getUser(); - String userID = Long.toString(user.getId()); - completion.invoke(null, userID); - } - }); - } - - @ReactMethod - public void getCurrentUserWithCompletion(Callback completion) { - MParticleUser currentUser = MParticle.getInstance().Identity().getCurrentUser(); - if (currentUser != null) { - String userID = Long.toString(currentUser.getId()); - completion.invoke(null, userID); - } else { - completion.invoke(null, null); - } - - } - - @ReactMethod - public void aliasUsers(final ReadableMap readableMap, final Callback completion) { - IdentityApi identityApi = MParticle.getInstance().Identity(); - ReadableMapKeySetIterator iterator = readableMap.keySetIterator(); - Long destinationMpid = null; - Long sourceMpid = null; - Long startTime = null; - Long endTime = null; - - while (iterator.hasNextKey()) { - try { - switch (iterator.nextKey()) { - case "destinationMpid": - destinationMpid = Utils.getLong(readableMap, "destinationMpid", false); - break; - case "sourceMpid": - sourceMpid = Utils.getLong(readableMap, "sourceMpid", false); - break; - case "startTime": - startTime = Utils.getLong(readableMap, "startTime", true); - break; - case "endTime": - endTime = Utils.getLong(readableMap, "endTime", true); - break; - } - } catch (NumberFormatException ex) { - Logger.error(ex.getMessage()); - completion.invoke(false, ex.getMessage()); - return; - } - } - if (startTime == null && endTime == null) { - MParticleUser sourceUser = null; - MParticleUser destinationUser = null; - if (sourceMpid != null) { - sourceUser = identityApi.getUser(sourceMpid); - } - if (destinationMpid != null) { - destinationUser = identityApi.getUser(destinationMpid); - } - if (sourceUser != null && destinationUser != null) { - AliasRequest request = AliasRequest.builder(sourceUser, destinationUser).build(); - boolean success = MParticle.getInstance().Identity().aliasUsers(request); - completion.invoke(success); - } else { - completion.invoke(false, "MParticleUser could not be found for provided sourceMpid and destinationMpid"); - } - } else { - AliasRequest request = AliasRequest.builder() - .destinationMpid(destinationMpid) - .sourceMpid(sourceMpid) - .startTime(startTime) - .endTime(endTime) - .build(); - boolean success = identityApi.aliasUsers(request); - completion.invoke(success); - } - } - - @ReactMethod - public void getSession(Callback completion) { - Session session = MParticle.getInstance().getCurrentSession(); - if (session != null) { - String sessionId = session.getSessionUUID(); - completion.invoke(sessionId); - } else { - completion.invoke(); - } - } - - @ReactMethod - public void getUserIdentities(final String userId, Callback completion) { - MParticleUser selectedUser = MParticle.getInstance().Identity().getUser(parseMpid(userId)); - if (selectedUser != null) { - completion.invoke(null, ConvertToUserIdentities(selectedUser.getUserIdentities())); - } else { - completion.invoke(); - } - } - - @ReactMethod - public void getFirstSeen(final String userId, Callback completion) { - MParticleUser selectedUser = MParticle.getInstance().Identity().getUser(Utils.parseMpid(userId)); - if (selectedUser != null) { - completion.invoke(String.valueOf(selectedUser.getFirstSeenTime())); - } else { - completion.invoke(); - } - } - - @ReactMethod - public void getLastSeen(final String userId, Callback completion) { - MParticleUser selectedUser = MParticle.getInstance().Identity().getUser(Utils.parseMpid(userId)); - if (selectedUser != null) { - completion.invoke(String.valueOf(selectedUser.getLastSeenTime())); - } else { - completion.invoke(); - } - } - - @ReactMethod - public void getAttributions(Callback completion) { - Map attributionResultMap = MParticle.getInstance().getAttributionResults(); - WritableMap map = Arguments.createMap(); - if (attributionResultMap != null) { - for (Map.Entry entry : attributionResultMap.entrySet()) { - WritableMap attributeMap = Arguments.createMap(); - AttributionResult attribution = entry.getValue(); - if (attribution != null) { - attributeMap.putInt("kitId", attribution.getServiceProviderId()); - if (attribution.getLink() != null) { - attributeMap.putString("link", attribution.getLink()); - } - if (attribution.getParameters() != null) { - attributeMap.putString("linkParameters", attribution.getParameters().toString()); - } - } - map.putMap(String.valueOf(entry.getKey()), attributeMap); - } - } - completion.invoke(map); - } - - @ReactMethod - public void setOptOut(Boolean optOut) { - MParticle.getInstance().setOptOut(optOut); - } - - @ReactMethod - public void getOptOut(Callback completion) { - boolean optedOut = MParticle.getInstance().getOptOut(); - completion.invoke(optedOut); - } - - @ReactMethod - public void isKitActive(Integer kitId, Callback completion) { - boolean isActive = MParticle.getInstance().isKitActive(kitId); - completion.invoke(isActive); - } - - @ReactMethod - public void logPushRegistration(String instanceId, String senderId) { - if (!isEmpty(instanceId) && !isEmpty(senderId)) { - MParticle.getInstance().logPushRegistration(instanceId, senderId); - } - } - - @ReactMethod - public void addGDPRConsentState(final ReadableMap map, String purpose) { - MParticleUser currentUser = MParticle.getInstance().Identity().getCurrentUser(); - if (currentUser != null) { - - - GDPRConsent consent = ConvertToGDPRConsent(map); - if (consent != null) { - ConsentState consentState = ConsentState.withConsentState(currentUser.getConsentState()) - .addGDPRConsentState(purpose, consent) - .build(); - currentUser.setConsentState(consentState); - Logger.info("GDPRConsentState added, \n\t\"purpose\": " + purpose + "\n" + consentState.toString()); - } else { - Logger.warning("GDPRConsentState was not able to be deserialized, will not be added"); - } - } - } - - @ReactMethod - public void removeGDPRConsentStateWithPurpose(String purpose) { - MParticleUser currentUser = MParticle.getInstance().Identity().getCurrentUser(); - if (currentUser != null) { - ConsentState consentState = ConsentState.withConsentState(currentUser.getConsentState()) - .removeGDPRConsentState(purpose) - .build(); - currentUser.setConsentState(consentState); - } - } - - @ReactMethod - public void setCCPAConsentState(final ReadableMap map) { - MParticleUser currentUser = MParticle.getInstance().Identity().getCurrentUser(); - if (currentUser != null) { - - - CCPAConsent consent = ConvertToCCPAConsent(map); - if (consent != null) { - ConsentState consentState = ConsentState.withConsentState(currentUser.getConsentState()) - .setCCPAConsentState(consent) - .build(); - currentUser.setConsentState(consentState); - Logger.info("CCPAConsentState added, \n" + consentState.toString()); - } else { - Logger.warning("CCPAConsentState was not able to be deserialized, will not be added"); - } - } - } - - @ReactMethod - public void removeCCPAConsentState() { - MParticleUser currentUser = MParticle.getInstance().Identity().getCurrentUser(); - if (currentUser != null) { - ConsentState consentState = ConsentState.withConsentState(currentUser.getConsentState()) - .removeCCPAConsentState() - .build(); - currentUser.setConsentState(consentState); - } - } - - protected WritableMap getWritableMap() { - return new WritableNativeMap(); - } - - private static IdentityApiRequest ConvertIdentityAPIRequest(ReadableMap map) { - IdentityApiRequest.Builder identityRequest = IdentityApiRequest.withEmptyUser(); - Map userIdentities = ConvertUserIdentities(map); - identityRequest.userIdentities(userIdentities); - - return identityRequest.build(); - } - - private static MPEvent ConvertMPEvent(ReadableMap map) { - if ((map.hasKey("name")) && (map.hasKey("type"))) { - String name = map.getString("name"); - Integer type = map.getInt("type"); - - MPEvent.Builder builder = new MPEvent.Builder(name, ConvertEventType(type)); - - if (map.hasKey("category")) { - builder.category(map.getString("category")); - } - - if (map.hasKey("duration")) { - builder.duration(map.getDouble("duration")); - } - - if (map.hasKey("info")) { - ReadableMap customInfoMap = map.getMap("info"); - Map customInfo = ConvertStringMap(customInfoMap); - builder.customAttributes(customInfo); - } - - if (map.hasKey("customFlags")) { - ReadableMap customFlagsMap = map.getMap("customFlags"); - Map customFlags = ConvertStringMap(customFlagsMap); - for (Map.Entry entry : customFlags.entrySet()) - { - builder.addCustomFlag(entry.getKey(), entry.getValue()); - } - } - - if (map.hasKey("shouldUploadEvent")) { - builder.shouldUploadEvent(map.getBoolean("shouldUploadEvent")); - } - - return builder.build(); - } - - return null; - } - - private static ReadableMap ConvertIdentityHttpResponse(IdentityHttpResponse response) { - WritableMap map = Arguments.createMap(); - map.putInt("httpCode", response.getHttpCode()); - if (response.getMpId() != 0) { - map.putString("mpid", String.valueOf(response.getMpId())); - } - StringBuilder stringBuilder = new StringBuilder(); - if (response.getErrors() != null) { - for (IdentityHttpResponse.Error error: response.getErrors()) { - if (error != null) { - stringBuilder.append("Code: " + error.code + "\n"); - stringBuilder.append("Message: " + error.message + "\n"); - } - } - } - map.putString("errors", stringBuilder.toString()); - return map; - } - - private static CommerceEvent ConvertCommerceEvent(ReadableMap map) { - Boolean isProductAction = map.hasKey("productActionType"); - Boolean isPromotion = map.hasKey("promotionActionType"); - Boolean isImpression = map.hasKey("impressions"); - - if (!isProductAction && !isPromotion && !isImpression) { - Log.e(LOG_TAG, "Invalid commerce event:" + map.toString()); - return null; - } - - CommerceEvent.Builder builder = null; - - if (isProductAction) { - int productActionInt = map.getInt("productActionType"); - String productAction = ConvertProductActionType(productActionInt); - ReadableArray productsArray = map.getArray("products"); - ReadableMap productMap = productsArray.getMap(0); - Product product = ConvertProduct(productMap); - ReadableMap transactionAttributesMap = map.getMap("transactionAttributes"); - TransactionAttributes transactionAttributes = ConvertTransactionAttributes(transactionAttributesMap); - builder = new CommerceEvent.Builder(productAction, product).transactionAttributes(transactionAttributes); - - for (int i = 1; i < productsArray.size(); ++i) { - productMap = productsArray.getMap(i); - product = ConvertProduct(productMap); - builder.addProduct(product); - } - } - else if (isPromotion) { - int promotionActionTypeInt = map.getInt("promotionActionType"); - String promotionAction = ConvertPromotionActionType(promotionActionTypeInt); - ReadableArray promotionsReadableArray = map.getArray("promotions"); - ReadableMap promotionMap = promotionsReadableArray.getMap(0); - Promotion promotion = ConvertPromotion(promotionMap); - builder = new CommerceEvent.Builder(promotionAction, promotion); - - for (int i = 1; i < promotionsReadableArray.size(); ++i) { - promotionMap = promotionsReadableArray.getMap(i); - promotion = ConvertPromotion(promotionMap); - builder.addPromotion(promotion); - } - } - else { - ReadableArray impressionsArray = map.getArray("impressions"); - ReadableMap impressionMap = impressionsArray.getMap(0); - Impression impression = ConvertImpression(impressionMap); - builder = new CommerceEvent.Builder(impression); - - for (int i = 1; i < impressionsArray.size(); ++i) { - impressionMap = impressionsArray.getMap(i); - impression = ConvertImpression(impressionMap); - builder.addImpression(impression); - } - } - - if (map.hasKey("shouldUploadEvent")) { - builder.shouldUploadEvent(map.getBoolean("shouldUploadEvent")); - } - if (map.hasKey("customAttributes")) { - builder.customAttributes(ConvertStringMap(map.getMap("customAttributes"))); - } - if (map.hasKey("currency")) { - builder.currency(map.getString("currency")); - } - if (map.hasKey("checkoutStep")) { - builder.checkoutStep(map.getInt("checkoutStep")); - } - if (map.hasKey("checkoutOptions")) { - builder.checkoutOptions(map.getString("checkoutOptions")); - } - - - return builder.build(); - } - - private static Product ConvertProduct(ReadableMap map) { - String name = map.getString("name"); - String sku = map.getString("sku"); - double unitPrice = map.getDouble("price"); - Product.Builder builder = new Product.Builder(name, sku, unitPrice); - - if (map.hasKey("brand")) { - String brand = map.getString("brand"); - builder.brand(brand); - } - - if (map.hasKey("category")) { - String category = map.getString("category"); - builder.category(category); - } - - if (map.hasKey("couponCode")) { - String couponCode = map.getString("couponCode"); - builder.couponCode(couponCode); - } - - if (map.hasKey("customAttributes")) { - ReadableMap customAttributesMap = map.getMap("customAttributes"); - Map customAttributes = ConvertStringMap(customAttributesMap); - builder.customAttributes(customAttributes); - } - - if (map.hasKey("position")) { - int position = map.getInt("position"); - builder.position(position); - } - - if (map.hasKey("quantity")) { - double quantity = map.getDouble("quantity"); - builder.quantity(quantity); - } - - if (map.hasKey("variant")) { - String variant = map.getString("variant"); - builder.variant(variant); - } - - return builder.build(); - } - - private static TransactionAttributes ConvertTransactionAttributes(ReadableMap map) { - if (!map.hasKey("transactionId")) { - return null; - } - - TransactionAttributes transactionAttributes = new TransactionAttributes(map.getString("transactionId")); - - if (map.hasKey("affiliation")) { - transactionAttributes.setAffiliation(map.getString("affiliation")); - } - - if (map.hasKey("revenue")) { - transactionAttributes.setRevenue(map.getDouble("revenue")); - } - - if (map.hasKey("shipping")) { - transactionAttributes.setShipping(map.getDouble("shipping")); - } - - if (map.hasKey("tax")) { - transactionAttributes.setTax(map.getDouble("tax")); - } - - if (map.hasKey("couponCode")) { - transactionAttributes.setCouponCode(map.getString("couponCode")); - } - - return transactionAttributes; - } - - private static Promotion ConvertPromotion(ReadableMap map) { - Promotion promotion = new Promotion(); - - if (map.hasKey("id")) { - promotion.setId(map.getString("id")); - } - - if (map.hasKey("name")) { - promotion.setName(map.getString("name")); - } - - if (map.hasKey("creative")) { - promotion.setCreative(map.getString("creative")); - } - - if (map.hasKey("position")) { - promotion.setPosition(map.getString("position")); - } - - return promotion; - } - - private static Impression ConvertImpression(ReadableMap map) { - - String listName = map.getString("impressionListName"); - ReadableArray productsArray = map.getArray("products"); - ReadableMap productMap = productsArray.getMap(0); - Product product = ConvertProduct(productMap); - Impression impression = new Impression(listName, product); - - for (int i = 1; i < productsArray.size(); ++i) { - productMap = productsArray.getMap(i); - product = ConvertProduct(productMap); - impression.addProduct(product); - } - - return impression; - } - - private static Map ConvertStringMap(ReadableMap readableMap) { - Map map = null; - - if (readableMap != null) { - map = new HashMap(); - ReadableMapKeySetIterator iterator = readableMap.keySetIterator(); - while (iterator.hasNextKey()) { - String key = iterator.nextKey(); - switch (readableMap.getType(key)) { - case Null: - map.put(key, null); - break; - case Boolean: - map.put(key, Boolean.valueOf(readableMap.getBoolean(key)).toString()); - break; - case Number: - try { - map.put(key, Integer.toString(readableMap.getInt(key))); - } catch (Exception e) { - try { - map.put(key, Double.toString(readableMap.getDouble(key))); - } catch (Exception ex) { - Logger.warning("Unable to parse value for \"" + key + "\""); - } - } - break; - case String: - map.put(key, readableMap.getString(key)); - break; - case Map: - Logger.warning("Maps are not supported Attribute value types"); - break; - case Array: - Logger.warning("Lists are not supported Attribute value types"); - break; - } - } - } - - return map; - } - - private static Map ConvertUserIdentities(ReadableMap readableMap) { - Map map = new HashMap<>(); - if (readableMap != null) { - ReadableMapKeySetIterator iterator = readableMap.keySetIterator(); - while (iterator.hasNextKey()) { - MParticle.IdentityType identity; - String key = iterator.nextKey(); - if ("email".equals(key)) { - identity = MParticle.IdentityType.Email; - } else if ("customerId".equals(key)) { - identity = MParticle.IdentityType.CustomerId; - } else { - identity = MParticle.IdentityType.parseInt(Integer.parseInt(key)); - } - if (identity != null) { - map.put(identity, readableMap.getString(key)); - } - } - } - return map; - } - - private WritableMap ConvertToUserIdentities(Map userIdentities) { - WritableMap nativeMap = getWritableMap(); - for (Map.Entry entry: userIdentities.entrySet()) { - nativeMap.putString(String.valueOf(entry.getKey().getValue()), entry.getValue()); - } - return nativeMap; - } - - - private static MParticle.EventType ConvertEventType(int eventType) { - switch (eventType) { - case 1: - return MParticle.EventType.Navigation; - case 2: - return MParticle.EventType.Location; - case 3: - return MParticle.EventType.Search; - case 4: - return MParticle.EventType.Transaction; - case 5: - return MParticle.EventType.UserContent; - case 6: - return MParticle.EventType.UserPreference; - case 7: - return MParticle.EventType.Social; - case 8: - return MParticle.EventType.Other; - case 9: - return MParticle.EventType.Media; - default: - return MParticle.EventType.Other; - } - } - - private static String ConvertProductActionType(int productActionType) { - switch (productActionType) { - case 1: - return Product.ADD_TO_CART; - case 2: - return Product.REMOVE_FROM_CART; - case 3: - return Product.CHECKOUT; - case 4: - return Product.CHECKOUT_OPTION; - case 5: - return Product.CLICK; - case 6: - return Product.DETAIL; - case 7: - return Product.PURCHASE; - case 8: - return Product.REFUND; - case 9: - return Product.ADD_TO_WISHLIST; - default: - return Product.REMOVE_FROM_WISHLIST; - } - } - - private static String ConvertPromotionActionType(int promotionActionType) { - switch (promotionActionType) { - case 0: - return Promotion.VIEW; - default: - return Promotion.CLICK; - } - } - - private boolean isEmpty(String str) { - return str == null || str.length() == 0; - } - - private long parseMpid(String longString) { - try { - return Long.parseLong(longString); - } catch (NumberFormatException ex) { - return 0L; - } - } - - @Nullable - private GDPRConsent ConvertToGDPRConsent(ReadableMap map) { - Boolean consented; - try { - if (map.getType("consented").equals(ReadableType.Boolean)) { - consented = map.getBoolean("consented"); - } else { - consented = Boolean.valueOf(map.getString("consented")); - } - } catch (Exception ex) { - Logger.error("failed to convert \"consented\" value to a Boolean, unable to process addGDPRConsentState"); - return null; - } - GDPRConsent.Builder builder = GDPRConsent.builder(consented); - - if (map.hasKey("document")) { - String document = map.getString("document"); - builder.document(document); - } - if (map.hasKey("hardwareId")) { - String hardwareId = map.getString("hardwareId"); - builder.hardwareId(hardwareId); - } - if (map.hasKey("location")) { - String location = map.getString("location"); - builder.location(location); - } - if (map.hasKey("timestamp")) { - Long timestamp = null; - try { - String timestampString = map.getString("timestamp"); - timestamp = Long.valueOf(timestampString); - builder.timestamp(timestamp); - } catch (Exception ex) { - Logger.warning("failed to convert \"timestamp\" value to Long"); - } - } - return builder.build(); - } - - @Nullable - private CCPAConsent ConvertToCCPAConsent(ReadableMap map ) { - Boolean consented; - try { - if (map.getType("consented").equals(ReadableType.Boolean)) { - consented = map.getBoolean("consented"); - } else { - consented = Boolean.valueOf(map.getString("consented")); - } - } catch (Exception ex) { - Logger.error("failed to convert \"consented\" value to a Boolean, unable to process addCCPAConsentState"); - return null; - } - CCPAConsent.Builder builder = CCPAConsent.builder(consented); - - if (map.hasKey("document")) { - String document = map.getString("document"); - builder.document(document); - } - if (map.hasKey("hardwareId")) { - String hardwareId = map.getString("hardwareId"); - builder.hardwareId(hardwareId); - } - if (map.hasKey("location")) { - String location = map.getString("location"); - builder.location(location); - } - if (map.hasKey("timestamp")) { - Long timestamp = null; - try { - String timestampString = map.getString("timestamp"); - timestamp = Long.valueOf(timestampString); - builder.timestamp(timestamp); - } catch (Exception ex) { - Logger.warning("failed to convert \"timestamp\" value to Long"); - } - } - return builder.build(); - } -} diff --git a/android/src/main/java/com/mparticle/react/MParticleModule.kt b/android/src/main/java/com/mparticle/react/MParticleModule.kt new file mode 100644 index 0000000..8b7456e --- /dev/null +++ b/android/src/main/java/com/mparticle/react/MParticleModule.kt @@ -0,0 +1,969 @@ +package com.mparticle.react + +import android.location.Location +import android.util.Log +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.Callback +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.ReadableArray +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.bridge.ReadableType +import com.facebook.react.bridge.WritableMap +import com.facebook.react.bridge.WritableNativeArray +import com.facebook.react.bridge.WritableNativeMap +import com.mparticle.MPEvent +import com.mparticle.MParticle +import com.mparticle.UserAttributeListener +import com.mparticle.commerce.CommerceEvent +import com.mparticle.commerce.Impression +import com.mparticle.commerce.Product +import com.mparticle.commerce.Promotion +import com.mparticle.commerce.TransactionAttributes +import com.mparticle.consent.CCPAConsent +import com.mparticle.consent.ConsentState +import com.mparticle.consent.GDPRConsent +import com.mparticle.identity.AliasRequest +import com.mparticle.identity.IdentityApiRequest +import com.mparticle.identity.IdentityHttpResponse +import com.mparticle.identity.MParticleUser +import com.mparticle.internal.Logger +import javax.annotation.Nullable + +class MParticleModule( + reactContext: ReactApplicationContext, +) : ReactContextBaseJavaModule(reactContext) { + companion object { + const val MODULE_NAME = "MParticle" + private const val LOG_TAG = "MParticleModule" + } + + override fun getName(): String = MODULE_NAME + + @ReactMethod + fun upload() { + MParticle.getInstance()?.upload() + } + + @ReactMethod + fun setUploadInterval(uploadInterval: Int) { + MParticle.getInstance()?.setUpdateInterval(uploadInterval) + } + + @ReactMethod + fun setLocation( + latitude: Double, + longitude: Double, + ) { + val newLocation = + Location("").apply { + this.latitude = latitude + this.longitude = longitude + } + MParticle.getInstance()?.setLocation(newLocation) + } + + @ReactMethod + fun logEvent( + name: String, + type: Int, + attributesMap: ReadableMap?, + ) { + val attributes = convertStringMap(attributesMap) + val eventType = convertEventType(type) + + val event = + MPEvent + .Builder(name, eventType) + .customAttributes(attributes) + .build() + MParticle.getInstance()?.logEvent(event) + } + + @ReactMethod + fun logMPEvent(attributesMap: ReadableMap?) { + val event = convertMPEvent(attributesMap) + if (event != null) { + MParticle.getInstance()?.logEvent(event) + } + } + + @ReactMethod + fun logCommerceEvent(map: ReadableMap?) { + if (map != null) { + val commerceEvent = convertCommerceEvent(map) + if (commerceEvent != null) { + MParticle.getInstance()?.logEvent(commerceEvent) + } + } + } + + @ReactMethod + fun logScreenEvent( + eventName: String, + attributesMap: ReadableMap?, + shouldUploadEvent: Boolean, + ) { + val attributes = convertStringMap(attributesMap) + MParticle.getInstance()?.logScreen(eventName, attributes, shouldUploadEvent) + } + + @ReactMethod + fun setUserAttribute( + userId: String, + userAttribute: String, + value: String, + ) { + val selectedUser = MParticle.getInstance()?.Identity()?.getUser(parseMpid(userId)) + selectedUser?.setUserAttribute(userAttribute, value) + } + + @ReactMethod + fun setUserAttributeArray( + userId: String, + key: String, + values: ReadableArray?, + ) { + if (values != null) { + val list = mutableListOf() + for (i in 0 until values.size()) { + values.getString(i)?.let { list.add(it) } + } + + val selectedUser = MParticle.getInstance()?.Identity()?.getUser(parseMpid(userId)) + selectedUser?.setUserAttributeList(key, list) + } + } + + @ReactMethod + fun getUserAttributes( + userId: String, + completion: Callback, + ) { + val selectedUser = MParticle.getInstance()?.Identity()?.getUser(parseMpid(userId)) + if (selectedUser != null) { + selectedUser.getUserAttributes( + object : UserAttributeListener { + override fun onUserAttributesReceived( + userAttributes: MutableMap?, + userAttributeLists: MutableMap>?, + mpid: Long?, + ) { + val resultMap = WritableNativeMap() + userAttributes?.let { attrs -> + for ((key, value) in attrs) { + resultMap.putString(key, value) + } + } + userAttributeLists?.let { attrLists -> + for ((key, valueList) in attrLists) { + val resultArray = WritableNativeArray() + for (arrayVal in valueList) { + resultArray.pushString(arrayVal) + } + resultMap.putArray(key, resultArray) + } + } + completion.invoke(null, resultMap) + } + }, + ) + } else { + completion.invoke() + } + } + + @ReactMethod + fun setUserTag( + userId: String, + tag: String, + ) { + val selectedUser = MParticle.getInstance()?.Identity()?.getUser(parseMpid(userId)) + selectedUser?.setUserTag(tag) + } + + @ReactMethod + fun removeUserAttribute( + userId: String, + key: String, + ) { + val selectedUser = MParticle.getInstance()?.Identity()?.getUser(parseMpid(userId)) + selectedUser?.removeUserAttribute(key) + } + + @ReactMethod + fun incrementUserAttribute( + userId: String, + key: String, + value: Int, + ) { + val selectedUser = MParticle.getInstance()?.Identity()?.getUser(parseMpid(userId)) + selectedUser?.incrementUserAttribute(key, value) + } + + @ReactMethod + fun identify( + requestMap: ReadableMap?, + completion: Callback, + ) { + val request = convertIdentityAPIRequest(requestMap) + + MParticle + .getInstance() + ?.Identity() + ?.identify(request) + ?.addFailureListener { identityHttpResponse -> + completion.invoke(convertIdentityHttpResponse(identityHttpResponse), null) + }?.addSuccessListener { identityApiResult -> + val user = identityApiResult.user + val userID = user.id.toString() + completion.invoke(null, userID) + } + } + + @ReactMethod + fun login( + requestMap: ReadableMap?, + completion: Callback, + ) { + val request = convertIdentityAPIRequest(requestMap) + + MParticle + .getInstance() + ?.Identity() + ?.login(request) + ?.addFailureListener { identityHttpResponse -> + completion.invoke(convertIdentityHttpResponse(identityHttpResponse), null) + }?.addSuccessListener { identityApiResult -> + val user = identityApiResult.user + val userId = user.id.toString() + val previousUser = identityApiResult.previousUser + val previousUserId = previousUser?.id?.toString() + completion.invoke(null, userId, previousUserId) + } + } + + @ReactMethod + fun logout( + requestMap: ReadableMap?, + completion: Callback, + ) { + val request = convertIdentityAPIRequest(requestMap) + + MParticle + .getInstance() + ?.Identity() + ?.logout(request) + ?.addFailureListener { identityHttpResponse -> + completion.invoke(convertIdentityHttpResponse(identityHttpResponse), null) + }?.addSuccessListener { identityApiResult -> + val user = identityApiResult.user + val userID = user.id.toString() + completion.invoke(null, userID) + } + } + + @ReactMethod + fun modify( + requestMap: ReadableMap?, + completion: Callback, + ) { + val request = convertIdentityAPIRequest(requestMap) + + MParticle + .getInstance() + ?.Identity() + ?.modify(request) + ?.addFailureListener { identityHttpResponse -> + completion.invoke(convertIdentityHttpResponse(identityHttpResponse), null) + }?.addSuccessListener { identityApiResult -> + val user = identityApiResult.user + val userID = user.id.toString() + completion.invoke(null, userID) + } + } + + @ReactMethod + fun getCurrentUserWithCompletion(completion: Callback) { + val currentUser = MParticle.getInstance()?.Identity()?.currentUser + if (currentUser != null) { + val userID = currentUser.id.toString() + completion.invoke(null, userID) + } else { + completion.invoke(null, null) + } + } + + @ReactMethod + fun aliasUsers( + readableMap: ReadableMap?, + completion: Callback, + ) { + val identityApi = MParticle.getInstance()?.Identity() ?: return + val iterator = readableMap?.keySetIterator() ?: return + var destinationMpid: Long? = null + var sourceMpid: Long? = null + var startTime: Long? = null + var endTime: Long? = null + + while (iterator.hasNextKey()) { + try { + when (iterator.nextKey()) { + "destinationMpid" -> destinationMpid = Utils.getLong(readableMap, "destinationMpid", false) + "sourceMpid" -> sourceMpid = Utils.getLong(readableMap, "sourceMpid", false) + "startTime" -> startTime = Utils.getLong(readableMap, "startTime", true) + "endTime" -> endTime = Utils.getLong(readableMap, "endTime", true) + } + } catch (ex: NumberFormatException) { + Logger.error(ex.message) + completion.invoke(false, ex.message) + return + } + } + + if (startTime == null && endTime == null) { + val sourceUser: MParticleUser? = sourceMpid?.let { identityApi.getUser(it) } + val destinationUser: MParticleUser? = destinationMpid?.let { identityApi.getUser(it) } + + if (sourceUser != null && destinationUser != null) { + val request = AliasRequest.builder(sourceUser, destinationUser).build() + val success = MParticle.getInstance()?.Identity()?.aliasUsers(request) ?: false + completion.invoke(success) + } else { + completion.invoke(false, "MParticleUser could not be found for provided sourceMpid and destinationMpid") + } + } else { + val request = + AliasRequest + .builder() + .destinationMpid(destinationMpid ?: 0) + .sourceMpid(sourceMpid ?: 0) + .startTime(startTime ?: 0) + .endTime(endTime ?: 0) + .build() + val success = identityApi.aliasUsers(request) + completion.invoke(success) + } + } + + @ReactMethod + fun getSession(completion: Callback) { + val session = MParticle.getInstance()?.currentSession + if (session != null) { + val sessionId = session.sessionUUID + completion.invoke(sessionId) + } else { + completion.invoke() + } + } + + @ReactMethod + fun getUserIdentities( + userId: String, + completion: Callback, + ) { + val selectedUser = MParticle.getInstance()?.Identity()?.getUser(parseMpid(userId)) + if (selectedUser != null) { + completion.invoke(null, convertToUserIdentities(selectedUser.userIdentities)) + } else { + completion.invoke() + } + } + + @ReactMethod + fun getFirstSeen( + userId: String, + completion: Callback, + ) { + val selectedUser = MParticle.getInstance()?.Identity()?.getUser(Utils.parseMpid(userId)) + if (selectedUser != null) { + completion.invoke(selectedUser.firstSeenTime.toString()) + } else { + completion.invoke() + } + } + + @ReactMethod + fun getLastSeen( + userId: String, + completion: Callback, + ) { + val selectedUser = MParticle.getInstance()?.Identity()?.getUser(Utils.parseMpid(userId)) + if (selectedUser != null) { + completion.invoke(selectedUser.lastSeenTime.toString()) + } else { + completion.invoke() + } + } + + @ReactMethod + fun getAttributions(completion: Callback) { + val attributionResultMap = MParticle.getInstance()?.attributionResults + val map = Arguments.createMap() + attributionResultMap?.let { resultMap -> + for ((key, attribution) in resultMap) { + val attributeMap = Arguments.createMap() + attribution?.let { attr -> + attributeMap.putInt("kitId", attr.serviceProviderId) + attr.link?.let { attributeMap.putString("link", it) } + attr.parameters?.let { attributeMap.putString("linkParameters", it.toString()) } + } + map.putMap(key.toString(), attributeMap) + } + } + completion.invoke(map) + } + + @ReactMethod + fun setOptOut(optOut: Boolean) { + MParticle.getInstance()?.setOptOut(optOut) + } + + @ReactMethod + fun getOptOut(completion: Callback) { + val optedOut = MParticle.getInstance()?.optOut ?: false + completion.invoke(optedOut) + } + + @ReactMethod + fun isKitActive( + kitId: Int, + completion: Callback, + ) { + val isActive = MParticle.getInstance()?.isKitActive(kitId) ?: false + completion.invoke(isActive) + } + + @ReactMethod + fun logPushRegistration( + instanceId: String?, + senderId: String?, + ) { + if (!instanceId.isNullOrEmpty() && !senderId.isNullOrEmpty()) { + MParticle.getInstance()?.logPushRegistration(instanceId, senderId) + } + } + + @ReactMethod + fun addGDPRConsentState( + map: ReadableMap?, + purpose: String, + ) { + val currentUser = MParticle.getInstance()?.Identity()?.currentUser + if (currentUser != null) { + val consent = convertToGDPRConsent(map) + if (consent != null) { + val consentState = + ConsentState + .withConsentState(currentUser.consentState) + .addGDPRConsentState(purpose, consent) + .build() + currentUser.setConsentState(consentState) + Logger.info("GDPRConsentState added, \n\t\"purpose\": $purpose\n$consentState") + } else { + Logger.warning("GDPRConsentState was not able to be deserialized, will not be added") + } + } + } + + @ReactMethod + fun removeGDPRConsentStateWithPurpose(purpose: String) { + val currentUser = MParticle.getInstance()?.Identity()?.currentUser + if (currentUser != null) { + val consentState = + ConsentState + .withConsentState(currentUser.consentState) + .removeGDPRConsentState(purpose) + .build() + currentUser.setConsentState(consentState) + } + } + + @ReactMethod + fun setCCPAConsentState(map: ReadableMap?) { + val currentUser = MParticle.getInstance()?.Identity()?.currentUser + if (currentUser != null) { + val consent = convertToCCPAConsent(map) + if (consent != null) { + val consentState = + ConsentState + .withConsentState(currentUser.consentState) + .setCCPAConsentState(consent) + .build() + currentUser.setConsentState(consentState) + Logger.info("CCPAConsentState added, \n$consentState") + } else { + Logger.warning("CCPAConsentState was not able to be deserialized, will not be added") + } + } + } + + @ReactMethod + fun removeCCPAConsentState() { + val currentUser = MParticle.getInstance()?.Identity()?.currentUser + if (currentUser != null) { + val consentState = + ConsentState + .withConsentState(currentUser.consentState) + .removeCCPAConsentState() + .build() + currentUser.setConsentState(consentState) + } + } + + protected fun getWritableMap(): WritableMap = WritableNativeMap() + + private fun convertIdentityAPIRequest(map: ReadableMap?): IdentityApiRequest { + val identityRequest = IdentityApiRequest.withEmptyUser() + val userIdentities = convertUserIdentities(map) + identityRequest.userIdentities(userIdentities) + return identityRequest.build() + } + + private fun convertMPEvent(map: ReadableMap?): MPEvent? { + if (map?.hasKey("name") == true && map.hasKey("type")) { + val name = map.getString("name") ?: return null + val type = map.getInt("type") + + val builder = MPEvent.Builder(name, convertEventType(type)) + + if (map.hasKey("category")) { + builder.category(map.getString("category")) + } + + if (map.hasKey("duration")) { + builder.duration(map.getDouble("duration")) + } + + if (map.hasKey("info")) { + val customInfoMap = map.getMap("info") + val customInfo = convertStringMap(customInfoMap) + builder.customAttributes(customInfo) + } + + if (map.hasKey("customFlags")) { + val customFlagsMap = map.getMap("customFlags") + val customFlags = convertStringMap(customFlagsMap) + for ((key, value) in customFlags) { + builder.addCustomFlag(key, value) + } + } + + if (map.hasKey("shouldUploadEvent")) { + builder.shouldUploadEvent(map.getBoolean("shouldUploadEvent")) + } + + return builder.build() + } + return null + } + + private fun convertIdentityHttpResponse(response: IdentityHttpResponse?): ReadableMap { + val map = Arguments.createMap() + map.putInt("httpCode", response?.httpCode ?: 0) + if (response?.mpId != 0L) { + map.putString("mpid", response?.mpId.toString()) + } + val stringBuilder = StringBuilder() + response?.errors?.let { errors -> + for (error in errors) { + error?.let { + stringBuilder.append("Code: ${it.code}\n") + stringBuilder.append("Message: ${it.message}\n") + } + } + } + map.putString("errors", stringBuilder.toString()) + return map + } + + private fun convertCommerceEvent(map: ReadableMap): CommerceEvent? { + val isProductAction = map.hasKey("productActionType") + val isPromotion = map.hasKey("promotionActionType") + val isImpression = map.hasKey("impressions") + + if (!isProductAction && !isPromotion && !isImpression) { + Log.e(LOG_TAG, "Invalid commerce event: $map") + return null + } + + var builder: CommerceEvent.Builder? + + when { + isProductAction -> { + val productActionInt = map.getInt("productActionType") + val productAction = convertProductActionType(productActionInt) + val productsArray = map.getArray("products") ?: return null + val productMap = productsArray.getMap(0) + val product = convertProduct(productMap) ?: return null + val transactionAttributesMap = map.getMap("transactionAttributes") + val transactionAttributes = convertTransactionAttributes(transactionAttributesMap) + + builder = CommerceEvent.Builder(productAction, product) + transactionAttributes?.let { builder?.transactionAttributes(it) } + + for (i in 1 until productsArray.size()) { + val nextProductMap = productsArray.getMap(i) + val nextProduct = convertProduct(nextProductMap) + if (nextProduct != null) { + builder.addProduct(nextProduct) + } + } + } + isPromotion -> { + val promotionActionTypeInt = map.getInt("promotionActionType") + val promotionAction = convertPromotionActionType(promotionActionTypeInt) + val promotionsReadableArray = map.getArray("promotions") ?: return null + val promotionMap = promotionsReadableArray.getMap(0) + val promotion = convertPromotion(promotionMap) + builder = CommerceEvent.Builder(promotionAction, promotion) + + for (i in 1 until promotionsReadableArray.size()) { + val nextPromotionMap = promotionsReadableArray.getMap(i) + val nextPromotion = convertPromotion(nextPromotionMap) + builder.addPromotion(nextPromotion) + } + } + else -> { + val impressionsArray = map.getArray("impressions") ?: return null + val impressionMap = impressionsArray.getMap(0) + val impression = convertImpression(impressionMap) ?: return null + builder = CommerceEvent.Builder(impression) + + for (i in 1 until impressionsArray.size()) { + val nextImpressionMap = impressionsArray.getMap(i) + val nextImpression = convertImpression(nextImpressionMap) + if (nextImpression != null) { + builder.addImpression(nextImpression) + } + } + } + } + + return builder.let { b -> + if (map.hasKey("shouldUploadEvent")) { + b.shouldUploadEvent(map.getBoolean("shouldUploadEvent")) + } + if (map.hasKey("customAttributes")) { + b.customAttributes(convertStringMap(map.getMap("customAttributes"))) + } + if (map.hasKey("currency")) { + b.currency(map.getString("currency")) + } + if (map.hasKey("checkoutStep")) { + b.checkoutStep(map.getInt("checkoutStep")) + } + if (map.hasKey("checkoutOptions")) { + b.checkoutOptions(map.getString("checkoutOptions")) + } + b.build() + } + } + + private fun convertProduct(map: ReadableMap?): Product? { + if (map == null) return null + + val name = map.getString("name") ?: return null + val sku = map.getString("sku") ?: return null + val unitPrice = map.getDouble("price") + val builder = Product.Builder(name, sku, unitPrice) + + if (map.hasKey("brand")) { + val brand = map.getString("brand") + builder.brand(brand) + } + + if (map.hasKey("category")) { + val category = map.getString("category") + builder.category(category) + } + + if (map.hasKey("couponCode")) { + val couponCode = map.getString("couponCode") + builder.couponCode(couponCode) + } + + if (map.hasKey("customAttributes")) { + val customAttributesMap = map.getMap("customAttributes") + val customAttributes = convertStringMap(customAttributesMap) + builder.customAttributes(customAttributes) + } + + if (map.hasKey("position")) { + val position = map.getInt("position") + builder.position(position) + } + + if (map.hasKey("quantity")) { + val quantity = map.getDouble("quantity") + builder.quantity(quantity) + } + + if (map.hasKey("variant")) { + val variant = map.getString("variant") + builder.variant(variant) + } + + return builder.build() + } + + private fun convertTransactionAttributes(map: ReadableMap?): TransactionAttributes? { + if (map?.hasKey("transactionId") != true) { + return null + } + + val transactionId = map.getString("transactionId") ?: return null + val transactionAttributes = TransactionAttributes(transactionId) + + if (map.hasKey("affiliation")) { + transactionAttributes.affiliation = map.getString("affiliation") + } + + if (map.hasKey("revenue")) { + transactionAttributes.revenue = map.getDouble("revenue") + } + + if (map.hasKey("shipping")) { + transactionAttributes.shipping = map.getDouble("shipping") + } + + if (map.hasKey("tax")) { + transactionAttributes.tax = map.getDouble("tax") + } + + if (map.hasKey("couponCode")) { + transactionAttributes.couponCode = map.getString("couponCode") + } + + return transactionAttributes + } + + private fun convertPromotion(map: ReadableMap?): Promotion { + val promotion = Promotion() + + map?.let { m -> + if (m.hasKey("id")) { + promotion.id = m.getString("id") + } + + if (m.hasKey("name")) { + promotion.name = m.getString("name") + } + + if (m.hasKey("creative")) { + promotion.creative = m.getString("creative") + } + + if (m.hasKey("position")) { + promotion.position = m.getString("position") + } + } + + return promotion + } + + private fun convertImpression(map: ReadableMap?): Impression? { + if (map == null) return null + + val listName = map.getString("impressionListName") ?: return null + val productsArray = map.getArray("products") ?: return null + val productMap = productsArray.getMap(0) + val product = convertProduct(productMap) ?: return null + val impression = Impression(listName, product) + + for (i in 1 until productsArray.size()) { + val nextProductMap = productsArray.getMap(i) + val nextProduct = convertProduct(nextProductMap) + if (nextProduct != null) { + impression.addProduct(nextProduct) + } + } + + return impression + } + + private fun convertStringMap(readableMap: ReadableMap?): Map { + val map = mutableMapOf() + + readableMap?.let { rm -> + val iterator = rm.keySetIterator() + while (iterator.hasNextKey()) { + val key = iterator.nextKey() + when (rm.getType(key)) { + ReadableType.Null -> map[key] = "" + ReadableType.Boolean -> map[key] = rm.getBoolean(key).toString() + ReadableType.Number -> { + try { + map[key] = rm.getInt(key).toString() + } catch (_: Exception) { + try { + map[key] = rm.getDouble(key).toString() + } catch (_: Exception) { + Logger.warning("Unable to parse value for \"$key\"") + } + } + } + ReadableType.String -> map[key] = rm.getString(key) ?: "" + ReadableType.Map -> Logger.warning("Maps are not supported Attribute value types") + ReadableType.Array -> Logger.warning("Lists are not supported Attribute value types") + } + } + } + + return map + } + + private fun convertUserIdentities(readableMap: ReadableMap?): Map { + val map = mutableMapOf() + readableMap?.let { rm -> + val iterator = rm.keySetIterator() + while (iterator.hasNextKey()) { + val key = iterator.nextKey() + val identity = + when (key) { + "email" -> MParticle.IdentityType.Email + "customerId" -> MParticle.IdentityType.CustomerId + else -> MParticle.IdentityType.parseInt(key.toIntOrNull() ?: 0) + } + identity.let { identityType -> + rm.getString(key)?.let { value -> + map[identityType] = value + } + } + } + } + return map + } + + private fun convertToUserIdentities(userIdentities: Map): WritableMap { + val nativeMap = getWritableMap() + for ((identityType, value) in userIdentities) { + nativeMap.putString(identityType.value.toString(), value) + } + return nativeMap + } + + private fun convertEventType(eventType: Int): MParticle.EventType = + when (eventType) { + 1 -> MParticle.EventType.Navigation + 2 -> MParticle.EventType.Location + 3 -> MParticle.EventType.Search + 4 -> MParticle.EventType.Transaction + 5 -> MParticle.EventType.UserContent + 6 -> MParticle.EventType.UserPreference + 7 -> MParticle.EventType.Social + 8 -> MParticle.EventType.Other + 9 -> MParticle.EventType.Media + else -> MParticle.EventType.Other + } + + private fun convertProductActionType(productActionType: Int): String = + when (productActionType) { + 1 -> Product.ADD_TO_CART + 2 -> Product.REMOVE_FROM_CART + 3 -> Product.CHECKOUT + 4 -> Product.CHECKOUT_OPTION + 5 -> Product.CLICK + 6 -> Product.DETAIL + 7 -> Product.PURCHASE + 8 -> Product.REFUND + 9 -> Product.ADD_TO_WISHLIST + else -> Product.REMOVE_FROM_WISHLIST + } + + private fun convertPromotionActionType(promotionActionType: Int): String = + when (promotionActionType) { + 0 -> Promotion.VIEW + else -> Promotion.CLICK + } + + private fun parseMpid(longString: String): Long = + try { + longString.toLong() + } catch (_: NumberFormatException) { + 0L + } + + @Nullable + private fun convertToGDPRConsent(map: ReadableMap?): GDPRConsent? { + if (map == null) return null + + val consented: Boolean = + try { + when (map.getType("consented")) { + ReadableType.Boolean -> map.getBoolean("consented") + else -> map.getString("consented")?.toBoolean() ?: false + } + } catch (_: Exception) { + Logger.error("failed to convert \"consented\" value to a Boolean, unable to process addGDPRConsentState") + return null + } + + val builder = GDPRConsent.builder(consented) + + if (map.hasKey("document")) { + val document = map.getString("document") + builder.document(document) + } + if (map.hasKey("hardwareId")) { + val hardwareId = map.getString("hardwareId") + builder.hardwareId(hardwareId) + } + if (map.hasKey("location")) { + val location = map.getString("location") + builder.location(location) + } + if (map.hasKey("timestamp")) { + try { + val timestampString = map.getString("timestamp") + val timestamp = timestampString?.toLong() + timestamp?.let { builder.timestamp(it) } + } catch (_: Exception) { + Logger.warning("failed to convert \"timestamp\" value to Long") + } + } + return builder.build() + } + + @Nullable + private fun convertToCCPAConsent(map: ReadableMap?): CCPAConsent? { + if (map == null) return null + + val consented: Boolean = + try { + when (map.getType("consented")) { + ReadableType.Boolean -> map.getBoolean("consented") + else -> map.getString("consented")?.toBoolean() ?: false + } + } catch (_: Exception) { + Logger.error("failed to convert \"consented\" value to a Boolean, unable to process addCCPAConsentState") + return null + } + + val builder = CCPAConsent.builder(consented) + + if (map.hasKey("document")) { + val document = map.getString("document") + builder.document(document) + } + if (map.hasKey("hardwareId")) { + val hardwareId = map.getString("hardwareId") + builder.hardwareId(hardwareId) + } + if (map.hasKey("location")) { + val location = map.getString("location") + builder.location(location) + } + if (map.hasKey("timestamp")) { + try { + val timestampString = map.getString("timestamp") + val timestamp = timestampString?.toLong() + timestamp?.let { builder.timestamp(it) } + } catch (_: Exception) { + Logger.warning("failed to convert \"timestamp\" value to Long") + } + } + return builder.build() + } +} diff --git a/android/src/main/java/com/mparticle/react/rokt/MPRoktModuleImpl.kt b/android/src/main/java/com/mparticle/react/rokt/MPRoktModuleImpl.kt index f3a1423..e6340e3 100644 --- a/android/src/main/java/com/mparticle/react/rokt/MPRoktModuleImpl.kt +++ b/android/src/main/java/com/mparticle/react/rokt/MPRoktModuleImpl.kt @@ -1,7 +1,6 @@ package com.mparticle.react.rokt import android.app.Activity -import android.util.Log import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope @@ -26,7 +25,6 @@ class MPRoktModuleImpl( private val reactContext: ReactApplicationContext, ) { private var roktEventHandler: MpRoktEventCallback? = null - private val debug = false private val eventSubscriptions = mutableMapOf() private val listeners: MutableMap = @@ -95,12 +93,6 @@ class MPRoktModuleImpl( attributes?.toHashMap()?.filter { it.value is String }?.mapValues { it.value as String } ?: emptyMap() - fun logDebug(message: String) { - if (debug) { - Log.d("Rokt", message) - } - } - fun String.toColorMode(): RoktConfig.ColorMode = when (this) { "dark" -> RoktConfig.ColorMode.DARK diff --git a/android/src/oldarch/java/com/mparticle/react/rokt/MPRoktModule.kt b/android/src/oldarch/java/com/mparticle/react/rokt/MPRoktModule.kt index 2733060..15135bc 100644 --- a/android/src/oldarch/java/com/mparticle/react/rokt/MPRoktModule.kt +++ b/android/src/oldarch/java/com/mparticle/react/rokt/MPRoktModule.kt @@ -31,7 +31,6 @@ class MPRoktModule( fontFilesMap: ReadableMap?, ) { if (identifier.isBlank()) { - impl.logDebug("selectPlacements failed. identifier cannot be empty") return } val uiManager = reactContext.getNativeModule(UIManagerModule::class.java) diff --git a/android/src/test/java/com/mparticle/react/MParticleUserTest.java b/android/src/test/java/com/mparticle/react/MParticleUserTest.java index aea66fd..c82852b 100644 --- a/android/src/test/java/com/mparticle/react/MParticleUserTest.java +++ b/android/src/test/java/com/mparticle/react/MParticleUserTest.java @@ -28,6 +28,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; @RunWith(MockitoJUnitRunner.class) public class MParticleUserTest { @@ -40,25 +41,9 @@ public void before() { MParticle.setInstance(Mockito.mock(MParticle.class)); Mockito.when(MParticle.getInstance().Identity()).thenReturn(Mockito.mock(IdentityApi.class)); Mockito.lenient().when(MParticle.getInstance().Identity().getUser(0L)).thenReturn(null); - mParticleUser = new MParticleModule(Mockito.mock(ReactApplicationContext.class)) { - @Override - public WritableMap getWritableMap() { - return new MockMap(); - } - }; - } - - @Test - public void tesNullMParticleUserSetters() { - Exception exception = null; - try { - mParticleUser.setUserAttribute(null, "key", "values"); - mParticleUser.setUserTag(null, "test"); - mParticleUser.setUserAttributeArray(null, "keuy", new MockReadableArray()); - } catch (Exception e) { - exception = e; - } - assertNull(exception); + MParticleModule realModule = new MParticleModule(Mockito.mock(ReactApplicationContext.class)); + mParticleUser = Mockito.spy(realModule); + doReturn(new MockMap()).when(mParticleUser).getWritableMap(); } @Test @@ -116,21 +101,6 @@ public void invoke(Object... args) { } } - @Test - public void testGetUserIdentitiesNullUser() { - final Mutable callbackInvoked = new Mutable<>(false); - - mParticleUser.getUserIdentities(null, new Callback() { - @Override - public void invoke(Object... args) { - assertEquals(0, args.length); - callbackInvoked.value = true; - } - }); - - assertTrue(callbackInvoked.value); - } - @Test public void testSetUserTag() { final Mutable tag = new Mutable<>(); @@ -177,21 +147,6 @@ public void invoke(Object... args) { assertTrue(callbackInvoked.value); } - @Test - public void getFirstSeenTimeNullUser() { - final Mutable callbackInvoked = new Mutable<>(false); - - mParticleUser.getFirstSeen(null, new Callback() { - @Override - public void invoke(Object... args) { - assertEquals(0, args.length); - callbackInvoked.value = true; - } - }); - - assertTrue(callbackInvoked.value); - } - @Test public void getLastSeenTime() { final Mutable callbackInvoked = new Mutable<>(false); @@ -215,19 +170,4 @@ public void invoke(Object... args) { assertTrue(callbackInvoked.value); } - - @Test - public void getLastSeenTimeNullUser() { - final Mutable callbackInvoked = new Mutable<>(false); - - mParticleUser.getLastSeen(null , new Callback() { - @Override - public void invoke(Object... args) { - assertEquals(0, args.length); - callbackInvoked.value = true; - } - }); - - assertTrue(callbackInvoked.value); - } }