From fbe0aef5c3034865d13197b2bb97500b82865b9d Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Wed, 20 Jan 2021 16:56:52 -0300 Subject: [PATCH 01/23] Create base UserStateSMSSynchronizer * Creation of base UserStateSMSSynchronizer for the new SMS channel --- .../onesignal/OneSignalStateSynchronizer.java | 7 +- .../onesignal/UserStateSMSSynchronizer.java | 93 +++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java index 6b6624f138..8b542792b2 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java @@ -40,7 +40,8 @@ class OneSignalStateSynchronizer { enum UserStateSynchronizerType { PUSH, - EMAIL; + EMAIL, + SMS; public boolean isPush() { return this.equals(PUSH); @@ -49,6 +50,10 @@ public boolean isPush() { public boolean isEmail() { return this.equals(EMAIL); } + + public boolean isSMS() { + return this.equals(SMS); + } } // Each class abstracts from UserStateSynchronizer and this will allow us to handle different channels for specific method calls and requests diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java new file mode 100644 index 0000000000..8b2a718907 --- /dev/null +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java @@ -0,0 +1,93 @@ +package com.onesignal; + +import androidx.annotation.Nullable; + +import org.json.JSONObject; + +public class UserStateSMSSynchronizer extends UserStateSynchronizer { + + UserStateSMSSynchronizer(OneSignalStateSynchronizer.UserStateSynchronizerType channel) { + super(OneSignalStateSynchronizer.UserStateSynchronizerType.SMS); + } + + @Override + boolean getSubscribed() { + return false; + } + + @Override + GetTagsResult getTags(boolean fromServer) { + return null; + } + + @Nullable + @Override + String getExternalId(boolean fromServer) { + return null; + } + + @Override + protected UserState newUserState(String inPersistKey, boolean load) { + return null; + } + + @Override + protected OneSignal.LOG_LEVEL getLogLevel() { + return null; + } + + @Override + protected String getId() { + return null; + } + + @Override + protected void onSuccessfulSync(JSONObject jsonField) { + + } + + @Override + protected void fireEventsForUpdateFailure(JSONObject jsonFields) { + + } + + @Override + protected void addOnSessionOrCreateExtras(JSONObject jsonBody) { + + } + + @Override + protected void scheduleSyncToServer() { + + } + + @Override + void updateState(JSONObject state) { + + } + + @Override + void setSubscription(boolean enable) { + + } + + @Override + public boolean getUserSubscribePreference() { + return false; + } + + @Override + public void setPermission(boolean enable) { + + } + + @Override + void updateIdDependents(String id) { + + } + + @Override + void logoutEmail() { + + } +} From cbaed96779d9606ff6480198c9b0c7bba158bf90 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 25 Jan 2021 12:54:14 -0300 Subject: [PATCH 02/23] Add SMS Synchronizer calls * Call SMS Synchronizer methods under OneSignalStateSynchronizer * Add UserStateSecondaryChannelSynchronizer class * Move shared methods between SMS and Email to UserStateSecondaryChannelSynchronizer * Add OneSignal.java setSMSNumber public method * Add UserStateSMS --- .../java/com/onesignal/OSTaskController.java | 2 + .../main/java/com/onesignal/OneSignal.java | 22 ++++ .../onesignal/OneSignalStateSynchronizer.java | 24 +++- .../main/java/com/onesignal/UserState.java | 1 + .../onesignal/UserStateEmailSynchronizer.java | 111 ++++------------- .../onesignal/UserStatePushSynchronizer.java | 3 + .../main/java/com/onesignal/UserStateSMS.java | 26 ++++ .../onesignal/UserStateSMSSynchronizer.java | 65 ++-------- ...UserStateSecondaryChannelSynchronizer.java | 113 ++++++++++++++++++ 9 files changed, 223 insertions(+), 144 deletions(-) create mode 100644 OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMS.java create mode 100644 OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSTaskController.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSTaskController.java index a11af7e2dc..d083d20a5b 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSTaskController.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSTaskController.java @@ -17,6 +17,7 @@ class OSTaskController { // Available task for delay static final String GET_TAGS = "getTags()"; + static final String SET_SMS_NUMBER = "setSMSNumber()"; static final String SET_EMAIL = "setEmail()"; static final String LOGOUT_EMAIL = "logoutEmail()"; static final String SYNC_HASHED_EMAIL = "syncHashedEmail()"; @@ -38,6 +39,7 @@ class OSTaskController { static final String SEND_OUTCOME_WITH_VALUE = "sendOutcomeWithValue()"; static final HashSet METHODS_AVAILABLE_FOR_DELAY = new HashSet<>(Arrays.asList( GET_TAGS, + SET_SMS_NUMBER, SET_EMAIL, LOGOUT_EMAIL, SYNC_HASHED_EMAIL, diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java index c140814908..c32f008ee8 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java @@ -1391,6 +1391,28 @@ private static void registerUserTask() throws JSONException { waitingToPostStateSync = false; } + public static void setSMSNumber(@NonNull final String smsNumber) { + if (taskController.shouldQueueTaskForInit(OSTaskController.SET_SMS_NUMBER)) { + logger.error("Waiting for remote params. " + + "Moving " + OSTaskController.SET_SMS_NUMBER + " operation to a pending task queue."); + taskController.addTaskToQueue(new Runnable() { + @Override + public void run() { + logger.debug("Running " + OSTaskController.SET_SMS_NUMBER + " operation from a pending task queue."); + setSMSNumber(smsNumber); + } + }); + return; + } + + if (smsNumber == null || smsNumber.isEmpty()) { + OneSignal.Log(LOG_LEVEL.ERROR, "SMS number id can't be null nor empty"); + return; + } + + OneSignalStateSynchronizer.setSMSNumber(smsNumber, null); + } + public static void setEmail(@NonNull final String email, EmailUpdateHandler callback) { setEmail(email, null, callback); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java index 8b542792b2..8b8223f5f1 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java @@ -61,6 +61,7 @@ public boolean isSMS() { // Currently we have 2 channels: // 1. Push // 2. Email + // 3. SMS // Add more channels... private static HashMap userStateSynchronizers = new HashMap<>(); @@ -79,29 +80,42 @@ static UserStateEmailSynchronizer getEmailStateSynchronizer() { return (UserStateEmailSynchronizer) userStateSynchronizers.get(UserStateSynchronizerType.EMAIL); } + + // #3 UserStateSynchronizer -> SMS Channel + static UserStateSMSSynchronizer getSMSStateSynchronizer() { + if (!userStateSynchronizers.containsKey(UserStateSynchronizerType.SMS) || userStateSynchronizers.get(UserStateSynchronizerType.SMS) == null) + userStateSynchronizers.put(UserStateSynchronizerType.SMS, new UserStateSMSSynchronizer()); + + return (UserStateSMSSynchronizer) userStateSynchronizers.get(UserStateSynchronizerType.SMS); + } static boolean persist() { boolean pushPersisted = getPushStateSynchronizer().persist(); boolean emailPersisted = getEmailStateSynchronizer().persist(); + boolean smsPersisted = getSMSStateSynchronizer().persist(); + if (emailPersisted) emailPersisted = getEmailStateSynchronizer().getRegistrationId() != null; - return pushPersisted || emailPersisted; + return pushPersisted || emailPersisted || smsPersisted; } static void clearLocation() { getPushStateSynchronizer().clearLocation(); getEmailStateSynchronizer().clearLocation(); + getSMSStateSynchronizer().clearLocation(); } static void initUserState() { getPushStateSynchronizer().initUserState(); getEmailStateSynchronizer().initUserState(); + getSMSStateSynchronizer().initUserState(); } static void syncUserState(boolean fromSyncService) { getPushStateSynchronizer().syncUserState(fromSyncService); getEmailStateSynchronizer().syncUserState(fromSyncService); + getSMSStateSynchronizer().syncUserState(fromSyncService); } static void sendTags(JSONObject newTags, @Nullable ChangeTagsUpdateHandler handler) { @@ -109,6 +123,7 @@ static void sendTags(JSONObject newTags, @Nullable ChangeTagsUpdateHandler handl JSONObject jsonField = new JSONObject().put("tags", newTags); getPushStateSynchronizer().sendTags(jsonField, handler); getEmailStateSynchronizer().sendTags(jsonField, handler); + getSMSStateSynchronizer().sendTags(jsonField, handler); } catch (JSONException e) { if (handler != null) handler.onFailure(new OneSignal.SendTagsError(-1, "Encountered an error attempting to serialize your tags into JSON: " + e.getMessage() + "\n" + e.getStackTrace())); @@ -116,9 +131,16 @@ static void sendTags(JSONObject newTags, @Nullable ChangeTagsUpdateHandler handl } } + static void setSMSNumber(String smsNumber, String smsAuthHash) { + getPushStateSynchronizer().setSMSNumber(smsNumber, smsAuthHash); + getSMSStateSynchronizer().setSMSNumber(smsNumber, smsAuthHash); + // Should SMS be linked to email? + } + static void setEmail(String email, String emailAuthHash) { getPushStateSynchronizer().setEmail(email, emailAuthHash); getEmailStateSynchronizer().setEmail(email, emailAuthHash); + // Should email be linked to SMS? } static void setSubscription(boolean enable) { diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserState.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserState.java index 6c53781c68..4665004be4 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserState.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserState.java @@ -21,6 +21,7 @@ abstract class UserState { public static final int DEVICE_TYPE_FIREOS = 2; public static final int DEVICE_TYPE_EMAIL = 11; public static final int DEVICE_TYPE_HUAWEI = 13; + public static final int DEVICE_TYPE_SMS = 14; public static final int PUSH_STATUS_SUBSCRIBED = 1; static final int PUSH_STATUS_NO_PERMISSION = 0; diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java index a62f036cce..90f0fe247b 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java @@ -1,7 +1,5 @@ package com.onesignal; -import androidx.annotation.Nullable; - import com.onesignal.OneSignalStateSynchronizer.UserStateSynchronizerType; import org.json.JSONException; @@ -10,7 +8,7 @@ import java.util.ArrayList; import java.util.List; -class UserStateEmailSynchronizer extends UserStateSynchronizer { +class UserStateEmailSynchronizer extends UserStateSecondaryChannelSynchronizer { UserStateEmailSynchronizer() { super(UserStateSynchronizerType.EMAIL); @@ -22,58 +20,44 @@ protected UserState newUserState(String inPersistKey, boolean load) { } @Override - protected OneSignal.LOG_LEVEL getLogLevel() { - return OneSignal.LOG_LEVEL.INFO; + protected String getId() { + return OneSignal.getEmailId(); } - // Email subscription not readable from SDK @Override - boolean getSubscribed() { - return false; - } + void logoutEmail() { + OneSignal.saveEmailId(""); - // Email tags not readable from SDK - @Override - GetTagsResult getTags(boolean fromServer) { - return null; - } + resetCurrentState(); + getToSyncUserState().removeFromSyncValues("identifier"); + List keysToRemove = new ArrayList<>(); + keysToRemove.add("email_auth_hash"); + keysToRemove.add("device_player_id"); + keysToRemove.add("external_user_id"); + getToSyncUserState().removeFromSyncValues(keysToRemove); + getToSyncUserState().persistState(); - // Email external id not readable from SDK - @Override - @Nullable - String getExternalId(boolean fromServer) { - return null; + OneSignal.getEmailSubscriptionState().clearEmailAndId(); } - // Email subscription not settable from SDK - @Override - void setSubscription(boolean enable) {} - - // Email does not have a user preference on the SDK @Override - public boolean getUserSubscribePreference() { - return false; + protected int getDeviceType() { + return UserState.DEVICE_TYPE_EMAIL; } - // Email subscription not readable from SDK @Override - public void setPermission(boolean enable) {} + void fireUpdateSuccess() { + OneSignal.fireEmailUpdateSuccess(); + } @Override - void updateState(JSONObject state) {} - - void refresh() { - scheduleSyncToServer(); + void fireUpdateFailure() { + OneSignal.fireEmailUpdateFailure(); } @Override - protected void scheduleSyncToServer() { - // Don't make a POST / PUT network call if we never set an email. - boolean neverEmail = getId() == null && getRegistrationId() == null; - if (neverEmail || OneSignal.getUserId() == null) - return; - - getNetworkHandlerThread(NetworkHandlerThread.NETWORK_HANDLER_USERSTATE).runNewJobDelayed(); + void updateIdDependents(String id) { + OneSignal.updateEmailIdDependents(id); } void setEmail(String email, String emailAuthHash) { @@ -83,7 +67,7 @@ void setEmail(String email, String emailAuthHash) { boolean noChange = email.equals(syncValues.optString("identifier")) && syncValues.optString("email_auth_hash").equals(emailAuthHash == null ? "" : emailAuthHash); if (noChange) { - OneSignal.fireEmailUpdateSuccess(); + fireUpdateSuccess(); return; } @@ -115,51 +99,4 @@ void setEmail(String email, String emailAuthHash) { } } - @Override - protected String getId() { - return OneSignal.getEmailId(); - } - - @Override - void updateIdDependents(String id) { - OneSignal.updateEmailIdDependents(id); - } - - @Override - protected void addOnSessionOrCreateExtras(JSONObject jsonBody) { - try { - jsonBody.put("device_type", UserState.DEVICE_TYPE_EMAIL); - jsonBody.putOpt("device_player_id", OneSignal.getUserId()); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - @Override - void logoutEmail() { - OneSignal.saveEmailId(""); - - resetCurrentState(); - getToSyncUserState().removeFromSyncValues("identifier"); - List keysToRemove = new ArrayList<>(); - keysToRemove.add("email_auth_hash"); - keysToRemove.add("device_player_id"); - keysToRemove.add("external_user_id"); - getToSyncUserState().removeFromSyncValues(keysToRemove); - getToSyncUserState().persistState(); - - OneSignal.getEmailSubscriptionState().clearEmailAndId(); - } - - @Override - protected void fireEventsForUpdateFailure(JSONObject jsonFields) { - if (jsonFields.has("identifier")) - OneSignal.fireEmailUpdateFailure(); - } - - @Override - protected void onSuccessfulSync(JSONObject jsonFields) { - if (jsonFields.has("identifier")) - OneSignal.fireEmailUpdateSuccess(); - } } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java index 3d9c0dfd82..621c29906c 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java @@ -128,6 +128,9 @@ void setEmail(String email, String emailAuthHash) { } } + void setSMSNumber(String smsNumber, String smsAuthHash) { + } + @Override void setSubscription(boolean enable) { try { diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMS.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMS.java new file mode 100644 index 0000000000..da822632f0 --- /dev/null +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMS.java @@ -0,0 +1,26 @@ +package com.onesignal; + +class UserStateSMS extends UserState { + + private static final String SMS = "sms"; + + UserStateSMS(String inPersistKey, boolean load) { + super(SMS + inPersistKey, load); + } + + @Override + UserState newInstance(String persistKey) { + return new UserStateSMS(persistKey, false); + } + + @Override + protected void addDependFields() { + // No depended fields for sms + } + + @Override + boolean isSubscribed() { + // No subscription setting, always true + return true; + } +} diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java index 8b2a718907..c93bbdcd39 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java @@ -1,39 +1,14 @@ package com.onesignal; -import androidx.annotation.Nullable; +public class UserStateSMSSynchronizer extends UserStateSecondaryChannelSynchronizer { -import org.json.JSONObject; - -public class UserStateSMSSynchronizer extends UserStateSynchronizer { - - UserStateSMSSynchronizer(OneSignalStateSynchronizer.UserStateSynchronizerType channel) { + UserStateSMSSynchronizer() { super(OneSignalStateSynchronizer.UserStateSynchronizerType.SMS); } - @Override - boolean getSubscribed() { - return false; - } - - @Override - GetTagsResult getTags(boolean fromServer) { - return null; - } - - @Nullable - @Override - String getExternalId(boolean fromServer) { - return null; - } - @Override protected UserState newUserState(String inPersistKey, boolean load) { - return null; - } - - @Override - protected OneSignal.LOG_LEVEL getLogLevel() { - return null; + return new UserStateSMS(inPersistKey, load); } @Override @@ -42,42 +17,22 @@ protected String getId() { } @Override - protected void onSuccessfulSync(JSONObject jsonField) { - - } - - @Override - protected void fireEventsForUpdateFailure(JSONObject jsonFields) { - - } - - @Override - protected void addOnSessionOrCreateExtras(JSONObject jsonBody) { - - } - - @Override - protected void scheduleSyncToServer() { + void logoutEmail() { } @Override - void updateState(JSONObject state) { - + protected int getDeviceType() { + return UserState.DEVICE_TYPE_SMS; } @Override - void setSubscription(boolean enable) { + void fireUpdateSuccess() { } @Override - public boolean getUserSubscribePreference() { - return false; - } - - @Override - public void setPermission(boolean enable) { + void fireUpdateFailure() { } @@ -86,8 +41,6 @@ void updateIdDependents(String id) { } - @Override - void logoutEmail() { - + void setSMSNumber(String smsNumber, String smsAuthHash) { } } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java new file mode 100644 index 0000000000..d347bcf3bb --- /dev/null +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java @@ -0,0 +1,113 @@ +package com.onesignal; + +import androidx.annotation.Nullable; + +import com.onesignal.OneSignalStateSynchronizer.UserStateSynchronizerType; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +abstract class UserStateSecondaryChannelSynchronizer extends UserStateSynchronizer { + + UserStateSecondaryChannelSynchronizer(UserStateSynchronizerType channel) { + super(channel); + } + + @Override + protected abstract UserState newUserState(String inPersistKey, boolean load); + + @Override + abstract protected String getId(); + + @Override + abstract void logoutEmail(); + + abstract protected int getDeviceType(); + + abstract void fireUpdateSuccess(); + + abstract void fireUpdateFailure(); + + @Override + abstract void updateIdDependents(String id); + + @Override + protected OneSignal.LOG_LEVEL getLogLevel() { + return OneSignal.LOG_LEVEL.INFO; + } + + // Secondary channel subscriptions are not readable from SDK + @Override + boolean getSubscribed() { + return false; + } + + // Secondary channel tags not readable from SDK + @Override + GetTagsResult getTags(boolean fromServer) { + return null; + } + + // Secondary channel external id not readable from SDK + @Override + @Nullable + String getExternalId(boolean fromServer) { + return null; + } + + // Secondary channel subscription not settable from SDK + @Override + void setSubscription(boolean enable) {} + + // Secondary channel does not have a user preference on the SDK + @Override + public boolean getUserSubscribePreference() { + return false; + } + + // Secondary channel subscription not readable from SDK + @Override + public void setPermission(boolean enable) {} + + @Override + void updateState(JSONObject state) {} + + void refresh() { + scheduleSyncToServer(); + } + + @Override + protected void scheduleSyncToServer() { + // Don't make a POST / PUT network call if we never set an email/SMS. + boolean userNotRegistered = getId() == null && getRegistrationId() == null; + if (userNotRegistered || OneSignal.getUserId() == null) + return; + + getNetworkHandlerThread(NetworkHandlerThread.NETWORK_HANDLER_USERSTATE).runNewJobDelayed(); + } + + @Override + protected void addOnSessionOrCreateExtras(JSONObject jsonBody) { + try { + jsonBody.put("device_type", getDeviceType()); + jsonBody.putOpt("device_player_id", OneSignal.getUserId()); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + @Override + protected void fireEventsForUpdateFailure(JSONObject jsonFields) { + if (jsonFields.has("identifier")) + fireUpdateFailure(); + } + + @Override + protected void onSuccessfulSync(JSONObject jsonFields) { + if (jsonFields.has("identifier")) + fireUpdateSuccess(); + } +} From 4006dd51a29597a2a60ddf59394d374e6eba8cec Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 25 Jan 2021 13:19:14 -0300 Subject: [PATCH 03/23] Add SMS Number public methods * Add all combinations for setSMSNumber --- .../main/java/com/onesignal/OneSignal.java | 78 ++++++++++++++++++- .../com/onesignal/OneSignalRemoteParams.java | 1 + 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java index c32f008ee8..74fb54acc9 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java @@ -56,6 +56,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.w3c.dom.Text; import java.io.PrintWriter; import java.io.StringWriter; @@ -262,6 +263,36 @@ interface OSInternalExternalUserIdUpdateCompletionHandler { void onComplete(String channel, boolean success); } + public enum SMSErrorType { + VALIDATION, REQUIRES_EMAIL_AUTH, INVALID_OPERATION, NETWORK + } + + public static class OSSMSUpdateError { + private SMSErrorType type; + private String message; + + OSSMSUpdateError(SMSErrorType type, String message) { + this.type = type; + this.message = message; + } + + public SMSErrorType getType() { + return type; + } + + public String getMessage() { + return message; + } + } + + public interface OSSMSUpdateHandler { + void onSuccess(JSONObject result); + void onFailure(OSSMSUpdateError error); + } + + private static OSSMSUpdateHandler smsUpdateHandler; + private static OSSMSUpdateHandler smsLogoutHandler; + public enum EmailErrorType { VALIDATION, REQUIRES_EMAIL_AUTH, INVALID_OPERATION, NETWORK } @@ -1391,7 +1422,29 @@ private static void registerUserTask() throws JSONException { waitingToPostStateSync = false; } + public static void setSMSNumber(@NonNull final String smsNumber, OSSMSUpdateHandler callback) { + setSMSNumber(smsNumber, null, callback); + } + public static void setSMSNumber(@NonNull final String smsNumber) { + setSMSNumber(smsNumber, null, null); + } + + public static void setSMSNumber(@NonNull final String smsNumber, @Nullable final String smsAuthHash) { + setSMSNumber(smsNumber, smsAuthHash, null); + } + + /** + * Set an sms number for the device to later send sms to this number + * @param smsNumber The sms number that you want subscribe and associate with the device + * @param smsAuthHash Generated auth hash from your server to authorize. (Recommended) + * Create and send this hash from your backend to your app after + * the user logs into your app. + * DO NOT generate this from your app! + * Omit this value if you do not have a backend to authenticate the user. + * @param callback Fire onSuccess or onFailure depending if the update successes or fails + */ + public static void setSMSNumber(@NonNull final String smsNumber, final String smsAuthHash, final OSSMSUpdateHandler callback) { if (taskController.shouldQueueTaskForInit(OSTaskController.SET_SMS_NUMBER)) { logger.error("Waiting for remote params. " + "Moving " + OSTaskController.SET_SMS_NUMBER + " operation to a pending task queue."); @@ -1399,18 +1452,35 @@ public static void setSMSNumber(@NonNull final String smsNumber) { @Override public void run() { logger.debug("Running " + OSTaskController.SET_SMS_NUMBER + " operation from a pending task queue."); - setSMSNumber(smsNumber); + setSMSNumber(smsNumber, smsAuthHash, callback); } }); return; } - if (smsNumber == null || smsNumber.isEmpty()) { - OneSignal.Log(LOG_LEVEL.ERROR, "SMS number id can't be null nor empty"); + // If applicable, check if the user provided privacy consent + if (shouldLogUserPrivacyConsentErrorMessageForMethodName(OSTaskController.SET_EMAIL)) + return; + + if (TextUtils.isEmpty(smsNumber)) { + String errorMessage = "SMS number is invalid"; + if (callback != null) + callback.onFailure(new OSSMSUpdateError(SMSErrorType.VALIDATION, errorMessage)); + logger.error(errorMessage); + return; + } + + if (getRemoteParams().useSMSAuth && (smsAuthHash == null || smsAuthHash.length() == 0)) { + String errorMessage = "SMS authentication (auth token) is set to REQUIRED for this application. Please provide an auth token from your backend server or change the setting in the OneSignal dashboard."; + if (callback != null) + callback.onFailure(new OSSMSUpdateError(SMSErrorType.REQUIRES_EMAIL_AUTH, errorMessage)); + logger.error(errorMessage); return; } - OneSignalStateSynchronizer.setSMSNumber(smsNumber, null); + smsUpdateHandler = callback; + + OneSignalStateSynchronizer.setSMSNumber(smsNumber, smsAuthHash); } public static void setEmail(@NonNull final String email, EmailUpdateHandler callback) { diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalRemoteParams.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalRemoteParams.java index 53cc552741..eb8423d9fc 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalRemoteParams.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalRemoteParams.java @@ -75,6 +75,7 @@ public String toString() { static class Params { String googleProjectNumber; boolean enterprise; + boolean useSMSAuth; boolean useEmailAuth; boolean useUserIdAuth; JSONArray notificationChannels; From 8c29787cc0b46c996745a7530e361556b2e839af Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 25 Jan 2021 17:33:43 -0300 Subject: [PATCH 04/23] Add SynchronizerIntegrationTests file * Move External Id tests to SynchronizerIntegrationTests --- .../onesignal/MainOneSignalClassRunner.java | 563 -------------- .../SynchronizerIntegrationTests.java | 695 ++++++++++++++++++ 2 files changed, 695 insertions(+), 563 deletions(-) create mode 100644 OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java index 0c367af6dd..756b85e7b2 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java @@ -128,7 +128,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; -import static com.onesignal.OneSignal.ExternalIdErrorType.REQUIRES_EXTERNAL_ID_AUTH; import static com.onesignal.OneSignalPackagePrivateHelper.FCMBroadcastReceiver_processBundle; import static com.onesignal.OneSignalPackagePrivateHelper.NotificationBundleProcessor_Process; import static com.onesignal.OneSignalPackagePrivateHelper.NotificationOpenedProcessor_processFromContext; @@ -216,38 +215,6 @@ private static OneSignal.OSNotificationOpenedHandler getNotificationOpenedHandle }; } - private static JSONObject lastExternalUserIdResponse; - private static OneSignal.ExternalIdError lastExternalUserIdError; - private static OneSignal.OSExternalUserIdUpdateCompletionHandler getExternalUserIdUpdateCompletionHandler() { - return new OneSignal.OSExternalUserIdUpdateCompletionHandler() { - @Override - public void onSuccess(JSONObject results) { - lastExternalUserIdResponse = results; - } - - @Override - public void onFailure(OneSignal.ExternalIdError error) { - lastExternalUserIdError = error; - } - }; - } - - private static boolean didEmailUpdateSucceed; - private static OneSignal.EmailUpdateError lastEmailUpdateFailure; - private static OneSignal.EmailUpdateHandler getEmailUpdateHandler() { - return new OneSignal.EmailUpdateHandler() { - @Override - public void onSuccess() { - didEmailUpdateSucceed = true; - } - - @Override - public void onFailure(OneSignal.EmailUpdateError error) { - lastEmailUpdateFailure = error; - } - }; - } - private static JSONObject lastGetTags; private static void getGetTagsHandler() { OneSignal.getTags(tags -> lastGetTags = tags); @@ -256,10 +223,6 @@ private static void getGetTagsHandler() { private static void cleanUp() throws Exception { lastNotificationOpenedBody = null; lastGetTags = null; - lastExternalUserIdResponse = null; - lastExternalUserIdError = null; - lastEmailUpdateFailure = null; - didEmailUpdateSucceed = false; ShadowGMSLocationController.reset(); @@ -4324,532 +4287,6 @@ public void shouldSendFirebaseAnalyticsNotificationReceived() throws Exception { assertNull(ShadowFirebaseAnalytics.lastEventString); } - @Test - public void shouldSendExternalUserIdAfterRegistration() throws Exception { - OneSignalInit(); - threadAndTaskWait(); - - String testExternalId = "test_ext_id"; - - OneSignal.setExternalUserId(testExternalId); - - threadAndTaskWait(); - - assertEquals(3, ShadowOneSignalRestClient.networkCallCount); - - ShadowOneSignalRestClient.Request externalIdRequest = ShadowOneSignalRestClient.requests.get(2); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, externalIdRequest.method); - assertEquals(testExternalId, externalIdRequest.payload.getString("external_user_id")); - } - - @Test - public void shouldSendExternalUserIdBeforeRegistration() throws Exception { - String testExternalId = "test_ext_id"; - - OneSignal.setExternalUserId(testExternalId); - - OneSignalInit(); - threadAndTaskWait(); - - assertEquals(2, ShadowOneSignalRestClient.networkCallCount); - - ShadowOneSignalRestClient.Request registrationRequest = ShadowOneSignalRestClient.requests.get(1); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, registrationRequest.method); - assertEquals(testExternalId, registrationRequest.payload.getString("external_user_id")); - } - - @Test - public void shouldSetExternalIdWithAuthHash() throws Exception { - ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse(new JSONObject().put("require_user_id_auth", true)); - - OneSignalInit(); - threadAndTaskWait(); - - String testExternalId = "test_ext_id"; - - OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); - threadAndTaskWait(); - - assertNotNull(lastExternalUserIdError); - assertEquals(REQUIRES_EXTERNAL_ID_AUTH, lastExternalUserIdError.getType()); - } - - @Test - public void shouldSetExternalIdWithAuthHashAfterRegistration() throws Exception { - OneSignalInit(); - threadAndTaskWait(); - - String testExternalId = "test_ext_id"; - String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); - - OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null); - threadAndTaskWait(); - - assertEquals(3, ShadowOneSignalRestClient.networkCallCount); - - ShadowOneSignalRestClient.Request externalIdRequest = ShadowOneSignalRestClient.requests.get(2); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, externalIdRequest.method); - assertEquals(testExternalId, externalIdRequest.payload.getString("external_user_id")); - assertEquals(mockExternalIdHash, externalIdRequest.payload.getString("external_user_id_auth_hash")); - } - - @Test - public void shouldSetExternalIdWithAuthHashBeforeRegistration() throws Exception { - String testExternalId = "test_ext_id"; - String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); - - OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null); - - OneSignalInit(); - threadAndTaskWait(); - - assertEquals(2, ShadowOneSignalRestClient.networkCallCount); - - ShadowOneSignalRestClient.Request registrationRequest = ShadowOneSignalRestClient.requests.get(1); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, registrationRequest.method); - assertEquals(testExternalId, registrationRequest.payload.getString("external_user_id")); - assertEquals(mockExternalIdHash, registrationRequest.payload.getString("external_user_id_auth_hash")); - } - - @Test - public void shouldAlwaysSetExternalIdWithAuthHashAAfterRegistration() throws Exception { - OneSignalInit(); - threadAndTaskWait(); - - String testExternalId = "test_ext_id"; - String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); - - OneSignal.setExternalUserId(testExternalId, mockExternalIdHash); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request registrationRequest = ShadowOneSignalRestClient.requests.get(2); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, registrationRequest.method); - assertEquals(testExternalId, registrationRequest.payload.getString("external_user_id")); - assertEquals(mockExternalIdHash, registrationRequest.payload.getString("external_user_id_auth_hash")); - - fastColdRestartApp(); - - time.advanceSystemTimeBy(60); - OneSignalInit(); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request registrationRequestAfterColdStart = ShadowOneSignalRestClient.requests.get(4); - assertEquals(REST_METHOD.POST, registrationRequestAfterColdStart.method); - assertEquals(mockExternalIdHash, registrationRequestAfterColdStart.payload.getString("external_user_id_auth_hash")); - } - - @Test - public void shouldAlwaysSetExternalIdAndEmailWithAuthHashAfterRegistration() throws Exception { - OneSignalInit(); - threadAndTaskWait(); - - String testExternalId = "test_ext_id"; - String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); - - String email = "josh@onesignal.com"; - String mockEmailHash = new String(new char[64]).replace('\0', '0'); - - OneSignal.setExternalUserId(testExternalId, mockExternalIdHash); - OneSignal.setEmail(email, mockEmailHash); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request registrationRequest = ShadowOneSignalRestClient.requests.get(2); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, registrationRequest.method); - assertEquals(testExternalId, registrationRequest.payload.getString("external_user_id")); - assertEquals(mockExternalIdHash, registrationRequest.payload.getString("external_user_id_auth_hash")); - - ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(3); - assertEquals(REST_METHOD.POST, emailPost.method); - assertEquals(email, emailPost.payload.getString("identifier")); - assertEquals(11, emailPost.payload.getInt("device_type")); - assertEquals(mockEmailHash, emailPost.payload.getString("email_auth_hash")); - - fastColdRestartApp(); - - time.advanceSystemTimeBy(60); - OneSignalInit(); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request registrationRequestAfterColdStart = ShadowOneSignalRestClient.requests.get(6); - assertEquals(REST_METHOD.POST, registrationRequestAfterColdStart.method); - assertEquals(mockExternalIdHash, registrationRequestAfterColdStart.payload.getString("external_user_id_auth_hash")); - - ShadowOneSignalRestClient.Request emailPostAfterColdStart = ShadowOneSignalRestClient.requests.get(7); - assertEquals(REST_METHOD.POST, emailPostAfterColdStart.method); - assertEquals(11, emailPostAfterColdStart.payload.getInt("device_type")); - assertEquals(mockEmailHash, emailPostAfterColdStart.payload.getString("email_auth_hash")); - } - - @Test - public void shouldRemoveExternalUserId() throws Exception { - OneSignal.setExternalUserId("test_ext_id"); - - OneSignalInit(); - threadAndTaskWait(); - - OneSignal.removeExternalUserId(); - threadAndTaskWait(); - - assertEquals(3, ShadowOneSignalRestClient.networkCallCount); - - ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(2); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method); - assertEquals(removeIdRequest.payload.getString("external_user_id"), ""); - } - - @Test - public void shouldRemoveExternalUserIdFromPushWithAuthHash() throws Exception { - ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse(new JSONObject().put("require_user_id_auth", true)); - - String testExternalId = "test_ext_id"; - String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); - - OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null); - OneSignalInit(); - threadAndTaskWait(); - - OneSignal.removeExternalUserId(getExternalUserIdUpdateCompletionHandler()); - threadAndTaskWait(); - - JSONObject expectedExternalUserIdResponse = new JSONObject( - "{" + - " \"push\" : {" + - " \"success\" : true" + - " }" + - "}" - ); - assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); - - assertEquals(3, ShadowOneSignalRestClient.networkCallCount); - - ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(2); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method); - assertEquals(removeIdRequest.payload.getString("external_user_id"), ""); - assertEquals(mockExternalIdHash, removeIdRequest.payload.getString("external_user_id_auth_hash")); - } - - @Test - public void shouldRemoveExternalUserIdFromEmailWithAuthHash() throws Exception { - String testEmail = "test@test.com"; - String mockEmailHash = new String(new char[64]).replace('\0', '0'); - - OneSignal.setEmail(testEmail, mockEmailHash, getEmailUpdateHandler()); - OneSignalInit(); - threadAndTaskWait(); - - OneSignal.removeExternalUserId(getExternalUserIdUpdateCompletionHandler()); - threadAndTaskWait(); - - JSONObject expectedExternalUserIdResponse = new JSONObject( - "{" + - " \"push\" : {" + - " \"success\" : true" + - " }" + ", " + - " \"email\" : {" + - " \"success\" : true" + - " }" + - "}" - ); - assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); - assertTrue(didEmailUpdateSucceed); - assertNull(lastEmailUpdateFailure); - - assertEquals(6, ShadowOneSignalRestClient.networkCallCount); - - ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(4); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method); - assertEquals(removeIdRequest.payload.getString("external_user_id"), ""); - assertFalse(removeIdRequest.payload.has("external_user_id_auth_hash")); - - ShadowOneSignalRestClient.Request removeIdEmailRequest = ShadowOneSignalRestClient.requests.get(5); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdEmailRequest.method); - assertEquals(removeIdEmailRequest.payload.getString("external_user_id"), ""); - assertEquals(mockEmailHash, removeIdEmailRequest.payload.getString("email_auth_hash")); - } - - @Test - public void shouldRemoveExternalUserIdFromPushAndEmailWithAuthHash() throws Exception { - String testExternalId = "test_ext_id"; - String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); - String testEmail = "test@test.com"; - String mockEmailHash = new String(new char[64]).replace('\0', '0'); - - OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null); - OneSignal.setEmail(testEmail, mockEmailHash, null); - OneSignalInit(); - threadAndTaskWait(); - - OneSignal.removeExternalUserId(); - threadAndTaskWait(); - - assertEquals(6, ShadowOneSignalRestClient.networkCallCount); - - ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(4); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method); - assertEquals(removeIdRequest.payload.getString("external_user_id"), ""); - assertEquals(mockExternalIdHash, removeIdRequest.payload.getString("external_user_id_auth_hash")); - - ShadowOneSignalRestClient.Request removeIdEmailRequest = ShadowOneSignalRestClient.requests.get(5); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdEmailRequest.method); - assertEquals(removeIdEmailRequest.payload.getString("external_user_id"), ""); - assertEquals(mockEmailHash, removeIdEmailRequest.payload.getString("email_auth_hash")); - } - - @Test - public void doesNotSendSameExternalId() throws Exception { - String testExternalId = "test_ext_id"; - OneSignal.setExternalUserId(testExternalId); - - OneSignalInit(); - threadAndTaskWait(); - - assertEquals(2, ShadowOneSignalRestClient.networkCallCount); - - OneSignal.setExternalUserId(testExternalId); - threadAndTaskWait(); - - // Setting the same ID again should not generate a duplicate API request - // The SDK should detect it is the same and not generate a request - assertEquals(2, ShadowOneSignalRestClient.networkCallCount); - } - - @Test - public void sendsExternalIdOnEmailPlayers() throws Exception { - String testExternalId = "test_ext_id"; - - OneSignalInit(); - threadAndTaskWait(); - - OneSignal.setEmail("brad@onesignal.com"); - threadAndTaskWait(); - - int currentRequestCount = ShadowOneSignalRestClient.networkCallCount; - - OneSignal.setExternalUserId(testExternalId); - threadAndTaskWait(); - - // the SDK should have made two additional API calls - // One to set extID on the push player record, - // and another for the email player record - assertEquals(ShadowOneSignalRestClient.networkCallCount, currentRequestCount + 2); - - int externalIdRequests = 0; - - for (ShadowOneSignalRestClient.Request request : ShadowOneSignalRestClient.requests) { - if (request.payload != null && request.payload.has("external_user_id")) { - externalIdRequests += 1; - assertEquals(request.payload.getString("external_user_id"), testExternalId); - } - } - - assertEquals(externalIdRequests, 2); - } - - @Test - public void sendExternalUserId_withCompletionHandler() throws Exception { - String testExternalId = "test_ext_id"; - - // 1. Init OneSignal - OneSignalInit(); - threadAndTaskWait(); - - // 2. Attempt to set external user id with callback - OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); - threadAndTaskWait(); - - // 3. Make sure lastExternalUserIdResponse is equal to the expected response - JSONObject expectedExternalUserIdResponse = new JSONObject( - "{" + - " \"push\" : {" + - " \"success\" : true" + - " }" + - "}" - ); - assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); - } - - @Test - public void sendDifferentExternalUserId_withCompletionHandler() throws Exception { - String testExternalId = "test_ext_id_1"; - - // 1. Init OneSignal - OneSignalInit(); - threadAndTaskWait(); - - // 2. Attempt to set external user id with callback - OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); - threadAndTaskWait(); - - // 3. Make sure lastExternalUserIdResponse is equal to the expected response - JSONObject expectedExternalUserIdResponse = new JSONObject( - "{" + - " \"push\" : {" + - " \"success\" : true" + - " }" + - "}" - ); - assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); - - // 4. Change test external user id to send - testExternalId = "test_ext_id_2"; - - // 5. Attempt to set same exact external user id with callback - OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); - threadAndTaskWait(); - - // 6. Make sure lastExternalUserIdResponse is equal to the expected response - assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); - } - - @Test - public void sendSameExternalUserId_withCompletionHandler() throws Exception { - String testExternalId = "test_ext_id"; - - // 1. Init OneSignal - OneSignalInit(); - threadAndTaskWait(); - - // 2. Attempt to set external user id with callback - OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); - threadAndTaskWait(); - - // 3. Make sure lastExternalUserIdResponse is equal to the expected response - JSONObject expectedExternalUserIdResponse = new JSONObject( - "{" + - " \"push\" : {" + - " \"success\" : true" + - " }" + - "}" - ); - assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); - - // 4. Attempt to set same exact external user id with callback - OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); - threadAndTaskWait(); - - // 5. Make sure lastExternalUserIdResponse is equal to the expected response - assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); - } - - @Test - public void sendExternalUserId_withFailure_withCompletionHandler() throws Exception { - String testExternalId = "test_ext_id"; - - // 1. Init OneSignal - OneSignalInit(); - threadAndTaskWait(); - - // 2. Attempt to set external user id with callback and force failure on the network requests - ShadowOneSignalRestClient.failAll = true; - OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); - threadAndTaskWait(); - - // 3. Make sure lastExternalUserIdResponse is equal to the expected response - JSONObject expectedExternalUserIdResponse = new JSONObject( - "{" + - " \"push\" : {" + - " \"success\" : false" + - " }" + - "}" - ); - assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); - - // 4. Flip ShadowOneSignalRestClient.failAll flag back to false - ShadowOneSignalRestClient.failAll = false; - - // 5. Attempt a second set external user id with callback - OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); - threadAndTaskWait(); - - // 6. Make sure lastExternalUserIdResponse is equal to the expected response - expectedExternalUserIdResponse = new JSONObject( - "{" + - " \"push\" : {" + - " \"success\" : true" + - " }" + - "}" - ); - assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); - } - - @Test - public void sendExternalUserId_forPushAndEmail_withFailure_withCompletionHandler() throws Exception { - String testEmail = "test@onesignal.com"; - String testExternalId = "test_ext_id"; - - // 1. Init OneSignal - OneSignalInit(); - OneSignal.setEmail(testEmail); - threadAndTaskWait(); - - // 2. Attempt to set external user id with callback and force failure on the network requests - ShadowOneSignalRestClient.failAll = true; - OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); - threadAndTaskWait(); - - // 3. Make sure lastExternalUserIdResponse has push and email with success : false - JSONObject expectedExternalUserIdResponse = new JSONObject( - "{" + - " \"push\" : {" + - " \"success\" : false" + - " }," + - " \"email\" : {" + - " \"success\" : false" + - " }" + - "}" - ); - assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); - - // 4. Flip ShadowOneSignalRestClient.failAll flag back to false - ShadowOneSignalRestClient.failAll = false; - - // 5. Attempt a second set external user id with callback - OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); - threadAndTaskWait(); - - // 6. Make sure lastExternalUserIdResponse has push and email with success : true - expectedExternalUserIdResponse = new JSONObject( - "{" + - " \"push\" : {" + - " \"success\" : true" + - " }," + - " \"email\" : {" + - " \"success\" : true" + - " }" + - "}" - ); - assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); - } - - @Test - public void sendExternalUserId_forPush_afterLoggingOutEmail_withCompletion() throws Exception { - String testEmail = "test@onesignal.com"; - String testExternalId = "test_ext_id"; - - // 1. Init OneSignal and set email - OneSignalInit(); - OneSignal.setEmail(testEmail); - threadAndTaskWait(); - - // 2. Logout Email - OneSignal.logoutEmail(); - threadAndTaskWait(); - - // 4. Attempt a set external user id with callback - OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); - threadAndTaskWait(); - - // 5. Make sure lastExternalUserIdResponse has push with success : true - JSONObject expectedExternalUserIdResponse = new JSONObject( - "{" + - " \"push\" : {" + - " \"success\" : true" + - " }" + - "}" - ); - assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); - } - @Test public void testDeviceStateHasEmailAddress() throws Exception { String testEmail = "test@onesignal.com"; diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java new file mode 100644 index 0000000000..6de5134226 --- /dev/null +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java @@ -0,0 +1,695 @@ +package com.test.onesignal; + +import android.annotation.SuppressLint; +import android.app.Activity; + +import androidx.test.core.app.ApplicationProvider; + +import com.onesignal.MockOSLog; +import com.onesignal.MockOSSharedPreferences; +import com.onesignal.MockOSTimeImpl; +import com.onesignal.MockOneSignalDBHelper; +import com.onesignal.MockSessionManager; +import com.onesignal.OneSignal; +import com.onesignal.ShadowCustomTabsClient; +import com.onesignal.ShadowCustomTabsSession; +import com.onesignal.ShadowGMSLocationController; +import com.onesignal.ShadowOSUtils; +import com.onesignal.ShadowOneSignalRestClient; +import com.onesignal.StaticResetHelper; +import com.onesignal.example.BlankActivity; +import com.onesignal.influence.data.OSTrackerFactory; + +import org.json.JSONObject; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ActivityController; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowLog; + +import java.lang.reflect.Field; + +import static com.onesignal.OneSignal.ExternalIdErrorType.REQUIRES_EXTERNAL_ID_AUTH; +import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_getSessionListener; +import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setSessionManager; +import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTime; +import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTrackerFactory; +import static com.onesignal.ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse; +import static com.test.onesignal.TestHelpers.afterTestCleanup; +import static com.test.onesignal.TestHelpers.fastColdRestartApp; +import static com.test.onesignal.TestHelpers.threadAndTaskWait; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; + +@Config(packageName = "com.onesignal.example", + shadows = { + ShadowOneSignalRestClient.class, + ShadowOSUtils.class, + ShadowCustomTabsClient.class, + ShadowCustomTabsSession.class + }, + sdk = 21 +) +@RunWith(RobolectricTestRunner.class) +public class SynchronizerIntegrationTests { + private static final String ONESIGNAL_APP_ID = "b4f7f966-d8cc-11e4-bed1-df8f05be55ba"; + + @SuppressLint("StaticFieldLeak") + private static Activity blankActivity; + private static ActivityController blankActivityController; + + private MockOSTimeImpl time; + private OSTrackerFactory trackerFactory; + private MockSessionManager sessionManager; + private MockOneSignalDBHelper dbHelper; + + private static JSONObject lastExternalUserIdResponse; + private static OneSignal.ExternalIdError lastExternalUserIdError; + private static OneSignal.OSExternalUserIdUpdateCompletionHandler getExternalUserIdUpdateCompletionHandler() { + return new OneSignal.OSExternalUserIdUpdateCompletionHandler() { + @Override + public void onSuccess(JSONObject results) { + lastExternalUserIdResponse = results; + } + + @Override + public void onFailure(OneSignal.ExternalIdError error) { + lastExternalUserIdError = error; + } + }; + } + + private static boolean didEmailUpdateSucceed; + private static OneSignal.EmailUpdateError lastEmailUpdateFailure; + private static OneSignal.EmailUpdateHandler getEmailUpdateHandler() { + return new OneSignal.EmailUpdateHandler() { + @Override + public void onSuccess() { + didEmailUpdateSucceed = true; + } + + @Override + public void onFailure(OneSignal.EmailUpdateError error) { + lastEmailUpdateFailure = error; + } + }; + } + + + private static void cleanUp() throws Exception { + lastExternalUserIdResponse = null; + lastExternalUserIdError = null; + lastEmailUpdateFailure = null; + didEmailUpdateSucceed = false; + + ShadowGMSLocationController.reset(); + + TestHelpers.beforeTestInitAndCleanup(); + + // Set remote_params GET response + setRemoteParamsGetHtmlResponse(); + } + + @BeforeClass // Runs only once, before any tests + public static void setUpClass() throws Exception { + ShadowLog.stream = System.out; + + TestHelpers.beforeTestSuite(); + + Field OneSignal_CurrentSubscription = OneSignal.class.getDeclaredField("subscribableStatus"); + OneSignal_CurrentSubscription.setAccessible(true); + + OneSignal.setLogLevel(OneSignal.LOG_LEVEL.VERBOSE, OneSignal.LOG_LEVEL.NONE); + StaticResetHelper.saveStaticValues(); + } + + @Before + public void beforeEachTest() throws Exception { + blankActivityController = Robolectric.buildActivity(BlankActivity.class).create(); + blankActivity = blankActivityController.get(); + time = new MockOSTimeImpl(); + trackerFactory = new OSTrackerFactory(new MockOSSharedPreferences(), new MockOSLog(), time); + sessionManager = new MockSessionManager(OneSignal_getSessionListener(), trackerFactory, new MockOSLog()); + dbHelper = new MockOneSignalDBHelper(ApplicationProvider.getApplicationContext()); + + TestHelpers.setupTestWorkManager(blankActivity); + + cleanUp(); + + OneSignal_setTime(time); + } + + @After + public void afterEachTest() throws Exception { + afterTestCleanup(); + } + + @AfterClass + public static void afterEverything() throws Exception { + cleanUp(); + } + + @Test + public void shouldSendExternalUserIdAfterRegistration() throws Exception { + OneSignalInit(); + threadAndTaskWait(); + + String testExternalId = "test_ext_id"; + + OneSignal.setExternalUserId(testExternalId); + + threadAndTaskWait(); + + assertEquals(3, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request externalIdRequest = ShadowOneSignalRestClient.requests.get(2); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, externalIdRequest.method); + assertEquals(testExternalId, externalIdRequest.payload.getString("external_user_id")); + } + + @Test + public void shouldSendExternalUserIdBeforeRegistration() throws Exception { + String testExternalId = "test_ext_id"; + + OneSignal.setExternalUserId(testExternalId); + + OneSignalInit(); + threadAndTaskWait(); + + assertEquals(2, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request registrationRequest = ShadowOneSignalRestClient.requests.get(1); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, registrationRequest.method); + assertEquals(testExternalId, registrationRequest.payload.getString("external_user_id")); + } + + @Test + public void shouldSetExternalIdWithAuthHash() throws Exception { + ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse(new JSONObject().put("require_user_id_auth", true)); + + OneSignalInit(); + threadAndTaskWait(); + + String testExternalId = "test_ext_id"; + + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + assertNotNull(lastExternalUserIdError); + assertEquals(REQUIRES_EXTERNAL_ID_AUTH, lastExternalUserIdError.getType()); + } + + @Test + public void shouldSetExternalIdWithAuthHashAfterRegistration() throws Exception { + OneSignalInit(); + threadAndTaskWait(); + + String testExternalId = "test_ext_id"; + String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null); + threadAndTaskWait(); + + assertEquals(3, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request externalIdRequest = ShadowOneSignalRestClient.requests.get(2); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, externalIdRequest.method); + assertEquals(testExternalId, externalIdRequest.payload.getString("external_user_id")); + assertEquals(mockExternalIdHash, externalIdRequest.payload.getString("external_user_id_auth_hash")); + } + + @Test + public void shouldSetExternalIdWithAuthHashBeforeRegistration() throws Exception { + String testExternalId = "test_ext_id"; + String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null); + + OneSignalInit(); + threadAndTaskWait(); + + assertEquals(2, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request registrationRequest = ShadowOneSignalRestClient.requests.get(1); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, registrationRequest.method); + assertEquals(testExternalId, registrationRequest.payload.getString("external_user_id")); + assertEquals(mockExternalIdHash, registrationRequest.payload.getString("external_user_id_auth_hash")); + } + + @Test + public void shouldAlwaysSetExternalIdWithAuthHashAAfterRegistration() throws Exception { + OneSignalInit(); + threadAndTaskWait(); + + String testExternalId = "test_ext_id"; + String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setExternalUserId(testExternalId, mockExternalIdHash); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request registrationRequest = ShadowOneSignalRestClient.requests.get(2); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, registrationRequest.method); + assertEquals(testExternalId, registrationRequest.payload.getString("external_user_id")); + assertEquals(mockExternalIdHash, registrationRequest.payload.getString("external_user_id_auth_hash")); + + fastColdRestartApp(); + + time.advanceSystemTimeBy(60); + OneSignalInit(); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request registrationRequestAfterColdStart = ShadowOneSignalRestClient.requests.get(4); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, registrationRequestAfterColdStart.method); + assertEquals(mockExternalIdHash, registrationRequestAfterColdStart.payload.getString("external_user_id_auth_hash")); + } + + @Test + public void shouldAlwaysSetExternalIdAndEmailWithAuthHashAfterRegistration() throws Exception { + OneSignalInit(); + threadAndTaskWait(); + + String testExternalId = "test_ext_id"; + String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); + + String email = "josh@onesignal.com"; + String mockEmailHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setExternalUserId(testExternalId, mockExternalIdHash); + OneSignal.setEmail(email, mockEmailHash); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request registrationRequest = ShadowOneSignalRestClient.requests.get(2); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, registrationRequest.method); + assertEquals(testExternalId, registrationRequest.payload.getString("external_user_id")); + assertEquals(mockExternalIdHash, registrationRequest.payload.getString("external_user_id_auth_hash")); + + ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(3); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); + assertEquals(email, emailPost.payload.getString("identifier")); + assertEquals(11, emailPost.payload.getInt("device_type")); + assertEquals(mockEmailHash, emailPost.payload.getString("email_auth_hash")); + + fastColdRestartApp(); + + time.advanceSystemTimeBy(60); + OneSignalInit(); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request registrationRequestAfterColdStart = ShadowOneSignalRestClient.requests.get(6); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, registrationRequestAfterColdStart.method); + assertEquals(mockExternalIdHash, registrationRequestAfterColdStart.payload.getString("external_user_id_auth_hash")); + + ShadowOneSignalRestClient.Request emailPostAfterColdStart = ShadowOneSignalRestClient.requests.get(7); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPostAfterColdStart.method); + assertEquals(11, emailPostAfterColdStart.payload.getInt("device_type")); + assertEquals(mockEmailHash, emailPostAfterColdStart.payload.getString("email_auth_hash")); + } + + @Test + public void shouldRemoveExternalUserId() throws Exception { + OneSignal.setExternalUserId("test_ext_id"); + + OneSignalInit(); + threadAndTaskWait(); + + OneSignal.removeExternalUserId(); + threadAndTaskWait(); + + assertEquals(3, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(2); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method); + assertEquals(removeIdRequest.payload.getString("external_user_id"), ""); + } + + @Test + public void shouldRemoveExternalUserIdFromPushWithAuthHash() throws Exception { + String testExternalId = "test_ext_id"; + String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null); + OneSignalInit(); + threadAndTaskWait(); + + OneSignal.removeExternalUserId(getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + JSONObject expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : true" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + + assertEquals(3, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(2); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method); + assertEquals(removeIdRequest.payload.getString("external_user_id"), ""); + assertEquals(mockExternalIdHash, removeIdRequest.payload.getString("external_user_id_auth_hash")); + } + + @Test + public void shouldRemoveExternalUserIdFromEmailWithAuthHash() throws Exception { + String testEmail = "test@test.com"; + String mockEmailHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setEmail(testEmail, mockEmailHash, getEmailUpdateHandler()); + OneSignalInit(); + threadAndTaskWait(); + + OneSignal.removeExternalUserId(getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + JSONObject expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : true" + + " }" + ", " + + " \"email\" : {" + + " \"success\" : true" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + assertTrue(didEmailUpdateSucceed); + assertNull(lastEmailUpdateFailure); + + assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(4); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method); + assertEquals(removeIdRequest.payload.getString("external_user_id"), ""); + assertFalse(removeIdRequest.payload.has("external_user_id_auth_hash")); + + ShadowOneSignalRestClient.Request removeIdEmailRequest = ShadowOneSignalRestClient.requests.get(5); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdEmailRequest.method); + assertEquals(removeIdEmailRequest.payload.getString("external_user_id"), ""); + assertEquals(mockEmailHash, removeIdEmailRequest.payload.getString("email_auth_hash")); + } + + @Test + public void shouldRemoveExternalUserIdFromPushAndEmailWithAuthHash() throws Exception { + String testExternalId = "test_ext_id"; + String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); + String testEmail = "test@test.com"; + String mockEmailHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null); + OneSignal.setEmail(testEmail, mockEmailHash, null); + OneSignalInit(); + threadAndTaskWait(); + + OneSignal.removeExternalUserId(); + threadAndTaskWait(); + + assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(4); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method); + assertEquals(removeIdRequest.payload.getString("external_user_id"), ""); + assertEquals(mockExternalIdHash, removeIdRequest.payload.getString("external_user_id_auth_hash")); + + ShadowOneSignalRestClient.Request removeIdEmailRequest = ShadowOneSignalRestClient.requests.get(5); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdEmailRequest.method); + assertEquals(removeIdEmailRequest.payload.getString("external_user_id"), ""); + assertEquals(mockEmailHash, removeIdEmailRequest.payload.getString("email_auth_hash")); + } + + @Test + public void doesNotSendSameExternalId() throws Exception { + String testExternalId = "test_ext_id"; + OneSignal.setExternalUserId(testExternalId); + + OneSignalInit(); + threadAndTaskWait(); + + assertEquals(2, ShadowOneSignalRestClient.networkCallCount); + + OneSignal.setExternalUserId(testExternalId); + threadAndTaskWait(); + + // Setting the same ID again should not generate a duplicate API request + // The SDK should detect it is the same and not generate a request + assertEquals(2, ShadowOneSignalRestClient.networkCallCount); + } + + @Test + public void sendsExternalIdOnEmailPlayers() throws Exception { + String testExternalId = "test_ext_id"; + + OneSignalInit(); + threadAndTaskWait(); + + OneSignal.setEmail("brad@onesignal.com"); + threadAndTaskWait(); + + int currentRequestCount = ShadowOneSignalRestClient.networkCallCount; + + OneSignal.setExternalUserId(testExternalId); + threadAndTaskWait(); + + // the SDK should have made two additional API calls + // One to set extID on the push player record, + // and another for the email player record + assertEquals(ShadowOneSignalRestClient.networkCallCount, currentRequestCount + 2); + + int externalIdRequests = 0; + + for (ShadowOneSignalRestClient.Request request : ShadowOneSignalRestClient.requests) { + if (request.payload != null && request.payload.has("external_user_id")) { + externalIdRequests += 1; + assertEquals(request.payload.getString("external_user_id"), testExternalId); + } + } + + assertEquals(externalIdRequests, 2); + } + + @Test + public void sendExternalUserId_withCompletionHandler() throws Exception { + String testExternalId = "test_ext_id"; + + // 1. Init OneSignal + OneSignalInit(); + threadAndTaskWait(); + + // 2. Attempt to set external user id with callback + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 3. Make sure lastExternalUserIdResponse is equal to the expected response + JSONObject expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : true" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + } + + @Test + public void sendDifferentExternalUserId_withCompletionHandler() throws Exception { + String testExternalId = "test_ext_id_1"; + + // 1. Init OneSignal + OneSignalInit(); + threadAndTaskWait(); + + // 2. Attempt to set external user id with callback + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 3. Make sure lastExternalUserIdResponse is equal to the expected response + JSONObject expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : true" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + + // 4. Change test external user id to send + testExternalId = "test_ext_id_2"; + + // 5. Attempt to set same exact external user id with callback + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 6. Make sure lastExternalUserIdResponse is equal to the expected response + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + } + + @Test + public void sendSameExternalUserId_withCompletionHandler() throws Exception { + String testExternalId = "test_ext_id"; + + // 1. Init OneSignal + OneSignalInit(); + threadAndTaskWait(); + + // 2. Attempt to set external user id with callback + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 3. Make sure lastExternalUserIdResponse is equal to the expected response + JSONObject expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : true" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + + // 4. Attempt to set same exact external user id with callback + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 5. Make sure lastExternalUserIdResponse is equal to the expected response + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + } + + @Test + public void sendExternalUserId_withFailure_withCompletionHandler() throws Exception { + String testExternalId = "test_ext_id"; + + // 1. Init OneSignal + OneSignalInit(); + threadAndTaskWait(); + + // 2. Attempt to set external user id with callback and force failure on the network requests + ShadowOneSignalRestClient.failAll = true; + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 3. Make sure lastExternalUserIdResponse is equal to the expected response + JSONObject expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : false" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + + // 4. Flip ShadowOneSignalRestClient.failAll flag back to false + ShadowOneSignalRestClient.failAll = false; + + // 5. Attempt a second set external user id with callback + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 6. Make sure lastExternalUserIdResponse is equal to the expected response + expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : true" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + } + + @Test + public void sendExternalUserId_forPushAndEmail_withFailure_withCompletionHandler() throws Exception { + String testEmail = "test@onesignal.com"; + String testExternalId = "test_ext_id"; + + // 1. Init OneSignal + OneSignalInit(); + OneSignal.setEmail(testEmail); + threadAndTaskWait(); + + // 2. Attempt to set external user id with callback and force failure on the network requests + ShadowOneSignalRestClient.failAll = true; + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 3. Make sure lastExternalUserIdResponse has push and email with success : false + JSONObject expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : false" + + " }," + + " \"email\" : {" + + " \"success\" : false" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + + // 4. Flip ShadowOneSignalRestClient.failAll flag back to false + ShadowOneSignalRestClient.failAll = false; + + // 5. Attempt a second set external user id with callback + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 6. Make sure lastExternalUserIdResponse has push and email with success : true + expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : true" + + " }," + + " \"email\" : {" + + " \"success\" : true" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + } + + @Test + public void sendExternalUserId_forPush_afterLoggingOutEmail_withCompletion() throws Exception { + String testEmail = "test@onesignal.com"; + String testExternalId = "test_ext_id"; + + // 1. Init OneSignal and set email + OneSignalInit(); + OneSignal.setEmail(testEmail); + threadAndTaskWait(); + + // 2. Logout Email + OneSignal.logoutEmail(); + threadAndTaskWait(); + + // 4. Attempt a set external user id with callback + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 5. Make sure lastExternalUserIdResponse has push with success : true + JSONObject expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : true" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + } + + private void OneSignalInit() { + OneSignal.setLogLevel(OneSignal.LOG_LEVEL.VERBOSE, OneSignal.LOG_LEVEL.NONE); + ShadowOSUtils.subscribableStatus = 1; + OneSignal_setTime(time); + OneSignal_setTrackerFactory(trackerFactory); + OneSignal_setSessionManager(sessionManager); + OneSignal.setAppId(ONESIGNAL_APP_ID); + OneSignal.initWithContext(blankActivity); + blankActivityController.resume(); + } +} From 0d4fde422c518f27fa3db5056414a50a834cb38e Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 25 Jan 2021 20:23:12 -0300 Subject: [PATCH 05/23] Move email tests to SynchronizerIntegrationTests * Move email user state tests to SynchronizerIntegrationTests --- .../onesignal/MainOneSignalClassRunner.java | 391 +----------------- .../SynchronizerIntegrationTests.java | 367 ++++++++++++++++ 2 files changed, 370 insertions(+), 388 deletions(-) diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java index 756b85e7b2..6684507f8d 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java @@ -1383,7 +1383,6 @@ public void shouldUpdateNotificationTypesCorrectlyEvenWhenSetSubscriptionIsCalle assertEquals(1, ShadowOneSignalRestClient.lastPost.getInt("notification_types")); } - @Test public void shouldAllowMultipleSetSubscription() throws Exception { OneSignalInit(); @@ -1399,8 +1398,6 @@ public void shouldAllowMultipleSetSubscription() throws Exception { OneSignal.disablePush(true); assertNull(ShadowOneSignalRestClient.lastPost); - - OneSignal.disablePush(false); threadAndTaskWait(); assertEquals(1, ShadowOneSignalRestClient.lastPost.getInt("notification_types")); @@ -1597,349 +1594,6 @@ public void remoteNotificationReceived(Context context, OSNotificationReceivedEv } } - @Test - public void shouldSetEmail() throws Exception { - OneSignalInit(); - String email = "josh@onesignal.com"; - - OneSignal.setEmail(email); - threadAndTaskWait(); - - assertEquals(4, ShadowOneSignalRestClient.networkCallCount); - - JSONObject pushPost = ShadowOneSignalRestClient.requests.get(1).payload; - assertEquals(email, pushPost.getString("email")); - assertEquals(1, pushPost.getInt("device_type")); - - JSONObject emailPost = ShadowOneSignalRestClient.requests.get(2).payload; - assertEquals(email, emailPost.getString("identifier")); - assertEquals(11, emailPost.getInt("device_type")); - assertEquals(ShadowOneSignalRestClient.pushUserId, emailPost.getString("device_player_id")); - - JSONObject pushPut = ShadowOneSignalRestClient.requests.get(3).payload; - assertEquals(ShadowOneSignalRestClient.emailUserId, pushPut.getString("parent_player_id")); - assertFalse(pushPut.has("identifier")); - } - - @Test - public void shouldSendTagsToEmailBeforeCreate() throws Exception { - OneSignalInit(); - OneSignal.setEmail("josh@onesignal.com"); - JSONObject tagsJson = new JSONObject("{\"test1\": \"value1\", \"test2\": \"value2\"}"); - OneSignal.sendTags(tagsJson); - threadAndTaskWait(); - - assertEquals(4, ShadowOneSignalRestClient.networkCallCount); - - ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(2); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); - assertEquals("josh@onesignal.com", emailPost.payload.get("identifier")); - assertEquals(tagsJson.toString(), emailPost.payload.getJSONObject("tags").toString()); - } - - @Test - public void shouldWaitBeforeCreateEmailIfPushCreateFails() throws Exception { - ShadowOneSignalRestClient.failPosts = true; - - OneSignalInit(); - OneSignal.setEmail("josh@onesignal.com"); - threadAndTaskWait(); - - // Assert we are sending / retry for the push player first. - assertEquals(5, ShadowOneSignalRestClient.networkCallCount); - for(int i = 1; i < ShadowOneSignalRestClient.networkCallCount; i++) { - ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(i); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); - assertEquals(1, emailPost.payload.getInt("device_type")); - } - - // Turn off fail mocking, call sendTags to trigger another retry - ShadowOneSignalRestClient.failPosts = false; - OneSignal.sendTag("test", "test"); - threadAndTaskWait(); - - // Should now POST to create device_type 11 (email) - ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(6); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); - assertEquals("josh@onesignal.com", emailPost.payload.get("identifier")); - } - - @Test - public void shouldSendTagsToEmailAfterCreate() throws Exception { - OneSignalInit(); - OneSignal.setEmail("josh@onesignal.com"); - threadAndTaskWait(); - - JSONObject tagsJson = new JSONObject("{\"test1\": \"value1\", \"test2\": \"value2\"}"); - OneSignal.sendTags(tagsJson); - threadAndTaskWait(); - - assertEquals(6, ShadowOneSignalRestClient.networkCallCount); - - ShadowOneSignalRestClient.Request emailPut = ShadowOneSignalRestClient.requests.get(5); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, emailPut.method); - assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522", emailPut.url); - assertEquals(tagsJson.toString(), emailPut.payload.getJSONObject("tags").toString()); - } - - @Test - public void shouldSetEmailWithAuthHash() throws Exception { - OneSignalInit(); - String email = "josh@onesignal.com"; - String mockEmailHash = new String(new char[64]).replace('\0', '0'); - - OneSignal.setEmail(email, mockEmailHash); - threadAndTaskWait(); - - JSONObject emailPost = ShadowOneSignalRestClient.requests.get(2).payload; - assertEquals(email, emailPost.getString("identifier")); - assertEquals(11, emailPost.getInt("device_type")); - assertEquals(mockEmailHash, emailPost.getString("email_auth_hash")); - } - - private class TestEmailUpdateHandler implements OneSignal.EmailUpdateHandler { - boolean emailFiredSuccess = false; - OneSignal.EmailUpdateError emailFiredFailure = null; - - @Override - public void onSuccess() { - emailFiredSuccess = true; - } - - @Override - public void onFailure(OneSignal.EmailUpdateError error) { - emailFiredFailure = error; - } - } - - @Test - public void shouldFireOnSuccessOfEmailUpdate() throws Exception { - OneSignalInit(); - TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); - OneSignal.setEmail("josh@onesignal.com", testEmailUpdateHandler); - assertFalse(testEmailUpdateHandler.emailFiredSuccess); - threadAndTaskWait(); - - assertTrue(testEmailUpdateHandler.emailFiredSuccess); - assertNull(testEmailUpdateHandler.emailFiredFailure); - } - - @Test - public void shouldFireOnSuccessOfEmailEvenWhenNoChanges() throws Exception { - OneSignalInit(); - String email = "josh@onesignal.com"; - OneSignal.setEmail(email); - threadAndTaskWait(); - - TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); - OneSignal.setEmail(email, testEmailUpdateHandler); - threadAndTaskWait(); - - assertTrue(testEmailUpdateHandler.emailFiredSuccess); - assertNull(testEmailUpdateHandler.emailFiredFailure); - } - - @Test - public void shouldFireOnFailureOfEmailUpdateOnNetworkFailure() throws Exception { - OneSignalInit(); - TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); - OneSignal.setEmail("josh@onesignal.com", testEmailUpdateHandler); - ShadowOneSignalRestClient.failAll = true; - threadAndTaskWait(); - - assertFalse(testEmailUpdateHandler.emailFiredSuccess); - assertEquals(OneSignal.EmailErrorType.NETWORK, testEmailUpdateHandler.emailFiredFailure.getType()); - } - - @Test - public void shouldFireOnSuccessOnlyAfterNetworkCallAfterLogout() throws Exception { - OneSignalInit(); - emailSetThenLogout(); - TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); - OneSignal.setEmail("josh@onesignal.com", testEmailUpdateHandler); - assertFalse(testEmailUpdateHandler.emailFiredSuccess); - threadAndTaskWait(); - - assertTrue(testEmailUpdateHandler.emailFiredSuccess); - assertNull(testEmailUpdateHandler.emailFiredFailure); - } - - // Should create a new email instead of updating existing player record when no auth hash - @Test - public void shouldDoPostOnEmailChange() throws Exception { - OneSignalInit(); - - OneSignal.setEmail("josh@onesignal.com"); - threadAndTaskWait(); - - String newMockEmailPlayerId = "c007f967-98cc-11e4-bed1-118f05be4533"; - ShadowOneSignalRestClient.emailUserId = newMockEmailPlayerId; - String newEmail = "different@email.com"; - OneSignal.setEmail(newEmail); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(5); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); - assertEquals(newEmail, emailPost.payload.get("identifier")); - - ShadowOneSignalRestClient.Request playerPut = ShadowOneSignalRestClient.requests.get(6); - assertEquals(newMockEmailPlayerId, playerPut.payload.get("parent_player_id")); - } - - // Should update player with new email instead of creating a new one when auth hash is provided - @Test - public void shouldUpdateEmailWhenAuthHashIsUsed() throws Exception { - OneSignalInit(); - String email = "josh@onesignal.com"; - String mockEmailHash = new String(new char[64]).replace('\0', '0'); - - OneSignal.setEmail(email, mockEmailHash); - threadAndTaskWait(); - OneSignal.setEmail("different@email.com", mockEmailHash); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request pushPut = ShadowOneSignalRestClient.requests.get(4); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, pushPut.method); - assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511", pushPut.url); - assertEquals("different@email.com", pushPut.payload.get("email")); - - ShadowOneSignalRestClient.Request emailPut = ShadowOneSignalRestClient.requests.get(5); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, emailPut.method); - assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522", emailPut.url); - assertEquals("different@email.com", emailPut.payload.get("identifier")); - } - - @Test - public void shouldSendEmailAuthHashWithLogout() throws Exception { - OneSignalInit(); - threadAndTaskWait(); - String mockEmailHash = new String(new char[64]).replace('\0', '0'); - OneSignal.setEmail("josh@onesignal.com", mockEmailHash); - threadAndTaskWait(); - - OneSignal.logoutEmail(); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request emailPut = ShadowOneSignalRestClient.requests.get(5); - assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/email_logout", emailPut.url); - assertEquals(mockEmailHash, emailPut.payload.get("email_auth_hash")); - } - - private void emailSetThenLogout() throws Exception { - OneSignal.setEmail("josh@onesignal.com"); - threadAndTaskWait(); - - OneSignal.logoutEmail(); - threadAndTaskWait(); - } - - @Test - public void shouldLogoutOfEmail() throws Exception { - OneSignalInit(); - - emailSetThenLogout(); - - ShadowOneSignalRestClient.Request logoutEmailPost = ShadowOneSignalRestClient.requests.get(4); - assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/email_logout", logoutEmailPost.url); - assertEquals("b007f967-98cc-11e4-bed1-118f05be4522", logoutEmailPost.payload.get("parent_player_id")); - assertEquals("b4f7f966-d8cc-11e4-bed1-df8f05be55ba", logoutEmailPost.payload.get("app_id")); - } - - @Test - public void logoutEmailShouldNotSendEmailPlayersRequest() throws Exception { - // 1. Init OneSignal and set email - OneSignalInit(); - - emailSetThenLogout(); - - time.advanceSystemAndElapsedTimeBy(60); - pauseActivity(blankActivityController); - assertAndRunSyncService(); - - List requests = ShadowOneSignalRestClient.requests; - assertEquals(6, ShadowOneSignalRestClient.networkCallCount); - - ShadowOneSignalRestClient.Request postPush = ShadowOneSignalRestClient.requests.get(4); - assertNotEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511", postPush.url); - - ShadowOneSignalRestClient.Request postEmail = ShadowOneSignalRestClient.requests.get(5); - assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_focus", postEmail.url); - } - - @Test - public void shouldFireOnSuccessOfLogoutEmail() throws Exception { - OneSignalInit(); - TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); - - OneSignalInit(); - OneSignal.setEmail("josh@onesignal.com"); - threadAndTaskWait(); - OneSignal.logoutEmail(testEmailUpdateHandler); - threadAndTaskWait(); - - assertTrue(testEmailUpdateHandler.emailFiredSuccess); - assertNull(testEmailUpdateHandler.emailFiredFailure); - } - - @Test - public void shouldFireOnFailureOfLogoutEmailOnNetworkFailure() throws Exception { - OneSignalInit(); - TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); - - OneSignalInit(); - OneSignal.setEmail("josh@onesignal.com"); - threadAndTaskWait(); - - ShadowOneSignalRestClient.failAll = true; - OneSignal.logoutEmail(testEmailUpdateHandler); - threadAndTaskWait(); - - assertFalse(testEmailUpdateHandler.emailFiredSuccess); - assertEquals(OneSignal.EmailErrorType.NETWORK, testEmailUpdateHandler.emailFiredFailure.getType()); - } - - @Test - public void shouldCreateNewEmailAfterLogout() throws Exception { - OneSignalInit(); - - emailSetThenLogout(); - - String newMockEmailPlayerId = "c007f967-98cc-11e4-bed1-118f05be4533"; - ShadowOneSignalRestClient.emailUserId = newMockEmailPlayerId; - OneSignal.setEmail("different@email.com"); - threadAndTaskWait(); - - // Update Push record's email field. - ShadowOneSignalRestClient.Request putPushEmail = ShadowOneSignalRestClient.requests.get(5); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, putPushEmail.method); - assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511", putPushEmail.url); - assertEquals("different@email.com", putPushEmail.payload.get("email")); - - // Create new Email record - ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(6); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); - assertEquals("different@email.com", emailPost.payload.get("identifier")); - - // Update Push record's parent_player_id - ShadowOneSignalRestClient.Request playerPut2 = ShadowOneSignalRestClient.requests.get(7); - assertEquals(newMockEmailPlayerId, playerPut2.payload.get("parent_player_id")); - } - - @Test - public void shouldSendOnSessionToEmail() throws Exception { - OneSignalInit(); - OneSignal.setEmail("josh@onesignal.com"); - threadAndTaskWait(); - - restartAppAndElapseTimeToNextSession(time); - OneSignalInit(); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(6); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); - assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522/on_session", emailPost.url); - } - @Test public void shouldNotSendTagOnRepeats() throws Exception { OneSignalInit(); @@ -2936,8 +2590,9 @@ public void testOmitDeletesOfNonExistingKeys() throws Exception { assertFalse(ShadowOneSignalRestClient.lastPost.has("tags")); } - + // ####### End DeleteTags Tests ###### // ####### GetTags Tests ######## + @Test public void testGetTagsWithNoTagsShouldBeNull() throws Exception { OneSignalInit(); @@ -3050,45 +2705,7 @@ public void getTagsDelayedAfterRegistering() throws Exception { assertTrue(ShadowOneSignalRestClient.lastUrl.contains(ShadowOneSignalRestClient.pushUserId)); } - // ####### on_focus Tests ######## - - @Test - public void sendsOnFocus() throws Exception { - time.advanceSystemAndElapsedTimeBy(0); - OneSignalInit(); - threadAndTaskWait(); - - time.advanceSystemAndElapsedTimeBy(60); - pauseActivity(blankActivityController); - assertAndRunSyncService(); - - assertOnFocusAtIndex(2, 60); - assertRestCalls(3); - } - - @Test - public void sendsOnFocusToEmail() throws Exception { - time.advanceSystemAndElapsedTimeBy(0); - OneSignalInit(); - OneSignal.setEmail("josh@onesignal.com"); - threadAndTaskWait(); - - blankActivityController.resume(); - threadAndTaskWait(); - time.advanceSystemAndElapsedTimeBy(60); - pauseActivity(blankActivityController); - assertAndRunSyncService(); - - assertEquals(6, ShadowOneSignalRestClient.networkCallCount); - - ShadowOneSignalRestClient.Request postPush = ShadowOneSignalRestClient.requests.get(4); - assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_focus", postPush.url); - assertEquals(60, postPush.payload.getInt("active_time")); - - ShadowOneSignalRestClient.Request postEmail = ShadowOneSignalRestClient.requests.get(5); - assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522/on_focus", postEmail.url); - assertEquals(60, postEmail.payload.getInt("active_time")); - } + // ####### End GetTags Tests ######## /** * Similar workflow to testLocationPermissionPromptWithPrivacyConsent() @@ -3225,7 +2842,6 @@ public void shouldReturnCorrectConsentRequiredStatusWhenSetBeforeInit() throws E assertTrue(OneSignal.userProvidedPrivacyConsent()); } - // Functions to add observers (like addSubscriptionObserver) should continue // to work even if privacy consent has not been granted. @Test @@ -3809,7 +3425,6 @@ public void onFailure(JSONObject response) { assertNotNull(postNotificationFailure); } - @Test @Config(shadows = { ShadowRoboNotificationManager.class, ShadowBadgeCountUpdater.class, ShadowGenerateNotification.class }) public void shouldCancelAndClearNotifications() throws Exception { diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java index 6de5134226..681a14622c 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java @@ -41,8 +41,13 @@ import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTime; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTrackerFactory; import static com.onesignal.ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse; +import static com.test.onesignal.RestClientAsserts.assertOnFocusAtIndex; +import static com.test.onesignal.RestClientAsserts.assertRestCalls; import static com.test.onesignal.TestHelpers.afterTestCleanup; +import static com.test.onesignal.TestHelpers.assertAndRunSyncService; import static com.test.onesignal.TestHelpers.fastColdRestartApp; +import static com.test.onesignal.TestHelpers.pauseActivity; +import static com.test.onesignal.TestHelpers.restartAppAndElapseTimeToNextSession; import static com.test.onesignal.TestHelpers.threadAndTaskWait; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -158,6 +163,328 @@ public static void afterEverything() throws Exception { cleanUp(); } + @Test + public void shouldSetEmail() throws Exception { + OneSignalInit(); + String email = "josh@onesignal.com"; + + OneSignal.setEmail(email); + threadAndTaskWait(); + + assertEquals(4, ShadowOneSignalRestClient.networkCallCount); + + JSONObject pushPost = ShadowOneSignalRestClient.requests.get(1).payload; + assertEquals(email, pushPost.getString("email")); + assertEquals(1, pushPost.getInt("device_type")); + + JSONObject emailPost = ShadowOneSignalRestClient.requests.get(2).payload; + assertEquals(email, emailPost.getString("identifier")); + assertEquals(11, emailPost.getInt("device_type")); + assertEquals(ShadowOneSignalRestClient.pushUserId, emailPost.getString("device_player_id")); + + JSONObject pushPut = ShadowOneSignalRestClient.requests.get(3).payload; + assertEquals(ShadowOneSignalRestClient.emailUserId, pushPut.getString("parent_player_id")); + assertFalse(pushPut.has("identifier")); + } + + @Test + public void shouldSendTagsToEmailBeforeCreate() throws Exception { + OneSignalInit(); + OneSignal.setEmail("josh@onesignal.com"); + JSONObject tagsJson = new JSONObject("{\"test1\": \"value1\", \"test2\": \"value2\"}"); + OneSignal.sendTags(tagsJson); + threadAndTaskWait(); + + assertEquals(4, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(2); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); + assertEquals("josh@onesignal.com", emailPost.payload.get("identifier")); + assertEquals(tagsJson.toString(), emailPost.payload.getJSONObject("tags").toString()); + } + + @Test + public void shouldWaitBeforeCreateEmailIfPushCreateFails() throws Exception { + ShadowOneSignalRestClient.failPosts = true; + + OneSignalInit(); + OneSignal.setEmail("josh@onesignal.com"); + threadAndTaskWait(); + + // Assert we are sending / retry for the push player first. + assertEquals(5, ShadowOneSignalRestClient.networkCallCount); + for(int i = 1; i < ShadowOneSignalRestClient.networkCallCount; i++) { + ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(i); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); + assertEquals(1, emailPost.payload.getInt("device_type")); + } + + // Turn off fail mocking, call sendTags to trigger another retry + ShadowOneSignalRestClient.failPosts = false; + OneSignal.sendTag("test", "test"); + threadAndTaskWait(); + + // Should now POST to create device_type 11 (email) + ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(6); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); + assertEquals("josh@onesignal.com", emailPost.payload.get("identifier")); + } + + @Test + public void shouldSendTagsToEmailAfterCreate() throws Exception { + OneSignalInit(); + OneSignal.setEmail("josh@onesignal.com"); + threadAndTaskWait(); + + JSONObject tagsJson = new JSONObject("{\"test1\": \"value1\", \"test2\": \"value2\"}"); + OneSignal.sendTags(tagsJson); + threadAndTaskWait(); + + assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request emailPut = ShadowOneSignalRestClient.requests.get(5); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, emailPut.method); + assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522", emailPut.url); + assertEquals(tagsJson.toString(), emailPut.payload.getJSONObject("tags").toString()); + } + + @Test + public void shouldSetEmailWithAuthHash() throws Exception { + OneSignalInit(); + String email = "josh@onesignal.com"; + String mockEmailHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setEmail(email, mockEmailHash); + threadAndTaskWait(); + + JSONObject emailPost = ShadowOneSignalRestClient.requests.get(2).payload; + assertEquals(email, emailPost.getString("identifier")); + assertEquals(11, emailPost.getInt("device_type")); + assertEquals(mockEmailHash, emailPost.getString("email_auth_hash")); + } + + private class TestEmailUpdateHandler implements OneSignal.EmailUpdateHandler { + boolean emailFiredSuccess = false; + OneSignal.EmailUpdateError emailFiredFailure = null; + + @Override + public void onSuccess() { + emailFiredSuccess = true; + } + + @Override + public void onFailure(OneSignal.EmailUpdateError error) { + emailFiredFailure = error; + } + } + + @Test + public void shouldFireOnSuccessOfEmailUpdate() throws Exception { + OneSignalInit(); + TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); + OneSignal.setEmail("josh@onesignal.com", testEmailUpdateHandler); + assertFalse(testEmailUpdateHandler.emailFiredSuccess); + threadAndTaskWait(); + + assertTrue(testEmailUpdateHandler.emailFiredSuccess); + assertNull(testEmailUpdateHandler.emailFiredFailure); + } + + @Test + public void shouldFireOnSuccessOfEmailEvenWhenNoChanges() throws Exception { + OneSignalInit(); + String email = "josh@onesignal.com"; + OneSignal.setEmail(email); + threadAndTaskWait(); + + TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); + OneSignal.setEmail(email, testEmailUpdateHandler); + threadAndTaskWait(); + + assertTrue(testEmailUpdateHandler.emailFiredSuccess); + assertNull(testEmailUpdateHandler.emailFiredFailure); + } + + @Test + public void shouldFireOnFailureOfEmailUpdateOnNetworkFailure() throws Exception { + OneSignalInit(); + TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); + OneSignal.setEmail("josh@onesignal.com", testEmailUpdateHandler); + ShadowOneSignalRestClient.failAll = true; + threadAndTaskWait(); + + assertFalse(testEmailUpdateHandler.emailFiredSuccess); + assertEquals(OneSignal.EmailErrorType.NETWORK, testEmailUpdateHandler.emailFiredFailure.getType()); + } + + @Test + public void shouldFireOnSuccessOnlyAfterNetworkCallAfterLogout() throws Exception { + OneSignalInit(); + emailSetThenLogout(); + TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); + OneSignal.setEmail("josh@onesignal.com", testEmailUpdateHandler); + assertFalse(testEmailUpdateHandler.emailFiredSuccess); + threadAndTaskWait(); + + assertTrue(testEmailUpdateHandler.emailFiredSuccess); + assertNull(testEmailUpdateHandler.emailFiredFailure); + } + + // Should create a new email instead of updating existing player record when no auth hash + @Test + public void shouldDoPostOnEmailChange() throws Exception { + OneSignalInit(); + + OneSignal.setEmail("josh@onesignal.com"); + threadAndTaskWait(); + + String newMockEmailPlayerId = "c007f967-98cc-11e4-bed1-118f05be4533"; + ShadowOneSignalRestClient.emailUserId = newMockEmailPlayerId; + String newEmail = "different@email.com"; + OneSignal.setEmail(newEmail); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(5); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); + assertEquals(newEmail, emailPost.payload.get("identifier")); + + ShadowOneSignalRestClient.Request playerPut = ShadowOneSignalRestClient.requests.get(6); + assertEquals(newMockEmailPlayerId, playerPut.payload.get("parent_player_id")); + } + + // Should update player with new email instead of creating a new one when auth hash is provided + @Test + public void shouldUpdateEmailWhenAuthHashIsUsed() throws Exception { + OneSignalInit(); + String email = "josh@onesignal.com"; + String mockEmailHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setEmail(email, mockEmailHash); + threadAndTaskWait(); + OneSignal.setEmail("different@email.com", mockEmailHash); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request pushPut = ShadowOneSignalRestClient.requests.get(4); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, pushPut.method); + assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511", pushPut.url); + assertEquals("different@email.com", pushPut.payload.get("email")); + + ShadowOneSignalRestClient.Request emailPut = ShadowOneSignalRestClient.requests.get(5); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, emailPut.method); + assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522", emailPut.url); + assertEquals("different@email.com", emailPut.payload.get("identifier")); + } + + @Test + public void shouldSendEmailAuthHashWithLogout() throws Exception { + OneSignalInit(); + threadAndTaskWait(); + String mockEmailHash = new String(new char[64]).replace('\0', '0'); + OneSignal.setEmail("josh@onesignal.com", mockEmailHash); + threadAndTaskWait(); + + OneSignal.logoutEmail(); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request emailPut = ShadowOneSignalRestClient.requests.get(5); + assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/email_logout", emailPut.url); + assertEquals(mockEmailHash, emailPut.payload.get("email_auth_hash")); + } + + private void emailSetThenLogout() throws Exception { + OneSignal.setEmail("josh@onesignal.com"); + threadAndTaskWait(); + + OneSignal.logoutEmail(); + threadAndTaskWait(); + } + + @Test + public void shouldLogoutOfEmail() throws Exception { + OneSignalInit(); + + emailSetThenLogout(); + + ShadowOneSignalRestClient.Request logoutEmailPost = ShadowOneSignalRestClient.requests.get(4); + assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/email_logout", logoutEmailPost.url); + assertEquals("b007f967-98cc-11e4-bed1-118f05be4522", logoutEmailPost.payload.get("parent_player_id")); + assertEquals("b4f7f966-d8cc-11e4-bed1-df8f05be55ba", logoutEmailPost.payload.get("app_id")); + } + + @Test + public void shouldFireOnSuccessOfLogoutEmail() throws Exception { + OneSignalInit(); + TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); + + OneSignalInit(); + OneSignal.setEmail("josh@onesignal.com"); + threadAndTaskWait(); + OneSignal.logoutEmail(testEmailUpdateHandler); + threadAndTaskWait(); + + assertTrue(testEmailUpdateHandler.emailFiredSuccess); + assertNull(testEmailUpdateHandler.emailFiredFailure); + } + + @Test + public void shouldFireOnFailureOfLogoutEmailOnNetworkFailure() throws Exception { + OneSignalInit(); + TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); + + OneSignalInit(); + OneSignal.setEmail("josh@onesignal.com"); + threadAndTaskWait(); + + ShadowOneSignalRestClient.failAll = true; + OneSignal.logoutEmail(testEmailUpdateHandler); + threadAndTaskWait(); + + assertFalse(testEmailUpdateHandler.emailFiredSuccess); + assertEquals(OneSignal.EmailErrorType.NETWORK, testEmailUpdateHandler.emailFiredFailure.getType()); + } + + @Test + public void shouldCreateNewEmailAfterLogout() throws Exception { + OneSignalInit(); + + emailSetThenLogout(); + + String newMockEmailPlayerId = "c007f967-98cc-11e4-bed1-118f05be4533"; + ShadowOneSignalRestClient.emailUserId = newMockEmailPlayerId; + OneSignal.setEmail("different@email.com"); + threadAndTaskWait(); + + // Update Push record's email field. + ShadowOneSignalRestClient.Request putPushEmail = ShadowOneSignalRestClient.requests.get(5); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, putPushEmail.method); + assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511", putPushEmail.url); + assertEquals("different@email.com", putPushEmail.payload.get("email")); + + // Create new Email record + ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(6); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); + assertEquals("different@email.com", emailPost.payload.get("identifier")); + + // Update Push record's parent_player_id + ShadowOneSignalRestClient.Request playerPut2 = ShadowOneSignalRestClient.requests.get(7); + assertEquals(newMockEmailPlayerId, playerPut2.payload.get("parent_player_id")); + } + + @Test + public void shouldSendOnSessionToEmail() throws Exception { + OneSignalInit(); + OneSignal.setEmail("josh@onesignal.com"); + threadAndTaskWait(); + + restartAppAndElapseTimeToNextSession(time); + OneSignalInit(); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(6); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); + assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522/on_session", emailPost.url); + } + @Test public void shouldSendExternalUserIdAfterRegistration() throws Exception { OneSignalInit(); @@ -682,6 +1009,46 @@ public void sendExternalUserId_forPush_afterLoggingOutEmail_withCompletion() thr assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); } + // ####### on_focus Tests ######## + + @Test + public void sendsOnFocus() throws Exception { + time.advanceSystemAndElapsedTimeBy(0); + OneSignalInit(); + threadAndTaskWait(); + + time.advanceSystemAndElapsedTimeBy(60); + pauseActivity(blankActivityController); + assertAndRunSyncService(); + + assertOnFocusAtIndex(2, 60); + assertRestCalls(3); + } + + @Test + public void sendsOnFocusToEmail() throws Exception { + time.advanceSystemAndElapsedTimeBy(0); + OneSignalInit(); + OneSignal.setEmail("josh@onesignal.com"); + threadAndTaskWait(); + + blankActivityController.resume(); + threadAndTaskWait(); + time.advanceSystemAndElapsedTimeBy(60); + pauseActivity(blankActivityController); + assertAndRunSyncService(); + + assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request postPush = ShadowOneSignalRestClient.requests.get(4); + assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_focus", postPush.url); + assertEquals(60, postPush.payload.getInt("active_time")); + + ShadowOneSignalRestClient.Request postEmail = ShadowOneSignalRestClient.requests.get(5); + assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522/on_focus", postEmail.url); + assertEquals(60, postEmail.payload.getInt("active_time")); + } + private void OneSignalInit() { OneSignal.setLogLevel(OneSignal.LOG_LEVEL.VERBOSE, OneSignal.LOG_LEVEL.NONE); ShadowOSUtils.subscribableStatus = 1; From 854581ded5a762de6105f713801dfde95ddaf39d Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 25 Jan 2021 21:45:04 -0300 Subject: [PATCH 06/23] Add OneSignalStateSynchronizer sms methods integration * Add SMS State classes * Add Onesignal SMS state observers * Add OneSignal get and save sms id --- ...MSSubscriptionChangedInternalObserver.java | 50 ++++++ .../onesignal/OSSMSSubscriptionObserver.java | 32 ++++ .../com/onesignal/OSSMSSubscriptionState.java | 148 ++++++++++++++++++ .../OSSMSSubscriptionStateChanges.java | 67 ++++++++ .../main/java/com/onesignal/OneSignal.java | 118 +++++++++++++- .../java/com/onesignal/OneSignalPrefs.java | 4 + .../onesignal/OneSignalStateSynchronizer.java | 5 + .../onesignal/UserStateEmailSynchronizer.java | 5 + .../onesignal/UserStatePushSynchronizer.java | 5 + .../onesignal/UserStateSMSSynchronizer.java | 22 ++- ...UserStateSecondaryChannelSynchronizer.java | 3 + .../com/onesignal/UserStateSynchronizer.java | 2 + .../SynchronizerIntegrationTests.java | 24 ++- 13 files changed, 478 insertions(+), 7 deletions(-) create mode 100644 OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionChangedInternalObserver.java create mode 100644 OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionObserver.java create mode 100644 OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionState.java create mode 100644 OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionStateChanges.java diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionChangedInternalObserver.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionChangedInternalObserver.java new file mode 100644 index 0000000000..d34ea487f0 --- /dev/null +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionChangedInternalObserver.java @@ -0,0 +1,50 @@ +/** + * Modified MIT License + * + * Copyright 2021 OneSignal + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. All copies of substantial portions of the Software may only be used in connection + * with services provided by OneSignal. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.onesignal; + +class OSSMSSubscriptionChangedInternalObserver { + void changed(OSSMSSubscriptionState state) { + fireChangesToPublicObserver(state); + } + + // Handles firing a public facing OSSMSSubscriptionStateChangesObserver + // 1. Generates a OSSMSSubscriptionStateChanges object and sets to and from states + // 2. Persists acknowledgement + // - Prevents duplicated events + // - Notifies if changes were made outside of the app + static void fireChangesToPublicObserver(OSSMSSubscriptionState state) { + OSSMSSubscriptionStateChanges stateChanges = + new OSSMSSubscriptionStateChanges(OneSignal.lastSMSSubscriptionState, (OSSMSSubscriptionState) state.clone()); + + boolean hasReceiver = OneSignal.getSMSSubscriptionStateChangesObserver().notifyChange(stateChanges); + if (hasReceiver) { + OneSignal.lastSMSSubscriptionState = (OSSMSSubscriptionState)state.clone(); + OneSignal.lastSMSSubscriptionState.persistAsFrom(); + } + } +} diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionObserver.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionObserver.java new file mode 100644 index 0000000000..5679ac2594 --- /dev/null +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionObserver.java @@ -0,0 +1,32 @@ +/** + * Modified MIT License + * + * Copyright 2021 OneSignal + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. All copies of substantial portions of the Software may only be used in connection + * with services provided by OneSignal. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.onesignal; + +public interface OSSMSSubscriptionObserver { + void onSMSSubscriptionChanged(OSSMSSubscriptionStateChanges stateChanges); +} \ No newline at end of file diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionState.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionState.java new file mode 100644 index 0000000000..4b2c33c182 --- /dev/null +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionState.java @@ -0,0 +1,148 @@ +/** + * Modified MIT License + *

+ * Copyright 2021 OneSignal + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * 2. All copies of substantial portions of the Software may only be used in connection + * with services provided by OneSignal. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.onesignal; + +import androidx.annotation.NonNull; + +import org.json.JSONObject; + +public class OSSMSSubscriptionState implements Cloneable { + + private static final String CHANGED_KEY = "changed"; + private static final String SMS_USER_ID = "smsUserId"; + private static final String SMS_NUMBER = "smsNumber"; + private static final String SUBSCRIBED = "isSubscribed"; + + private OSObservable observable; + private String smsUserId; + private String smsNumber; + + OSSMSSubscriptionState(boolean asFrom) { + observable = new OSObservable<>(CHANGED_KEY, false); + + if (asFrom) { + smsUserId = OneSignalPrefs.getString(OneSignalPrefs.PREFS_ONESIGNAL, + OneSignalPrefs.PREFS_OS_SMS_ID_LAST, null); + smsNumber = OneSignalPrefs.getString(OneSignalPrefs.PREFS_ONESIGNAL, + OneSignalPrefs.PREFS_OS_SMS_NUMBER_LAST, null); + } else { + smsUserId = OneSignal.getSMSId(); + smsNumber = OneSignalStateSynchronizer.getSMSStateSynchronizer().getRegistrationId(); + } + } + + void clearSMSAndId() { + boolean changed = smsUserId != null || smsNumber != null; + smsUserId = null; + smsNumber = null; + if (changed) + observable.notifyChange(this); + } + + public String getSmsUserId() { + return smsUserId; + } + + void setSMSUserId(@NonNull String id) { + boolean changed = false; + if (id == null) + changed = smsUserId != null; + else if (!id.equals(smsUserId)) + changed = true; + + smsUserId = id; + if (changed) + observable.notifyChange(this); + } + + public String getSMSNumber() { + return smsNumber; + } + + void setSMSNumber(@NonNull String number) { + boolean changed = !number.equals(smsNumber); + smsNumber = number; + if (changed) + observable.notifyChange(this); + } + + public boolean isSubscribed() { + return smsUserId != null && smsNumber != null; + } + + void persistAsFrom() { + OneSignalPrefs.saveString(OneSignalPrefs.PREFS_ONESIGNAL, + OneSignalPrefs.PREFS_OS_SMS_ID_LAST, smsUserId); + OneSignalPrefs.saveString(OneSignalPrefs.PREFS_ONESIGNAL, + OneSignalPrefs.PREFS_OS_SMS_NUMBER_LAST, smsNumber); + } + + boolean compare(OSSMSSubscriptionState from) { + return !(smsUserId != null ? smsUserId : "").equals(from.smsUserId != null ? from.smsUserId : "") + || !(smsNumber != null ? smsNumber : "").equals(from.smsNumber != null ? from.smsNumber : ""); + } + + public OSObservable getObservable() { + return observable; + } + + protected Object clone() { + try { + return super.clone(); + } catch (Throwable t) { + } + return null; + } + + public JSONObject toJSONObject() { + JSONObject mainObj = new JSONObject(); + + try { + if (smsUserId != null) + mainObj.put(SMS_USER_ID, smsUserId); + else + mainObj.put(SMS_USER_ID, JSONObject.NULL); + + if (smsNumber != null) + mainObj.put(SMS_NUMBER, smsNumber); + else + mainObj.put(SMS_NUMBER, JSONObject.NULL); + + mainObj.put(SUBSCRIBED, isSubscribed()); + } catch (Throwable t) { + t.printStackTrace(); + } + + return mainObj; + } + + @Override + public String toString() { + return toJSONObject().toString(); + } +} diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionStateChanges.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionStateChanges.java new file mode 100644 index 0000000000..6495cce53b --- /dev/null +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionStateChanges.java @@ -0,0 +1,67 @@ +/** + * Modified MIT License + * + * Copyright 2021 OneSignal + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. All copies of substantial portions of the Software may only be used in connection + * with services provided by OneSignal. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.onesignal; + +import org.json.JSONException; +import org.json.JSONObject; + +public class OSSMSSubscriptionStateChanges { + private OSSMSSubscriptionState from, to; + + public OSSMSSubscriptionStateChanges(OSSMSSubscriptionState from, OSSMSSubscriptionState to) { + this.from = from; + this.to = to; + } + + public OSSMSSubscriptionState getTo() { + return to; + } + + public OSSMSSubscriptionState getFrom() { + return from; + } + + public JSONObject toJSONObject() { + JSONObject mainObj = new JSONObject(); + + try { + mainObj.put("from", from.toJSONObject()); + mainObj.put("to", to.toJSONObject()); + } catch (JSONException e) { + e.printStackTrace(); + } + + return mainObj; + } + + @Override + public String toString() { + return toJSONObject().toString(); + } + +} diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java index 74fb54acc9..f202f5884a 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java @@ -56,7 +56,6 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import org.w3c.dom.Text; import java.io.PrintWriter; import java.io.StringWriter; @@ -264,7 +263,7 @@ interface OSInternalExternalUserIdUpdateCompletionHandler { } public enum SMSErrorType { - VALIDATION, REQUIRES_EMAIL_AUTH, INVALID_OPERATION, NETWORK + VALIDATION, REQUIRES_SMS_AUTH, INVALID_OPERATION, NETWORK } public static class OSSMSUpdateError { @@ -370,6 +369,7 @@ static Activity getCurrentActivity() { private static String userId = null; private static String emailId = null; + private static String smsId = null; private static int subscribableStatus = Integer.MAX_VALUE; static OSRemoteNotificationReceivedHandler remoteNotificationReceivedHandler; @@ -567,6 +567,42 @@ static OSObservable smsSubscriptionStateChangesObserver; + static OSObservable getSMSSubscriptionStateChangesObserver() { + if (smsSubscriptionStateChangesObserver == null) + smsSubscriptionStateChangesObserver = new OSObservable<>("onOSSMSSubscriptionChanged", true); + return smsSubscriptionStateChangesObserver; + } + // End SMSSubscriptionState + /** * Get the current user data, notification and permissions state. */ @@ -1459,7 +1495,7 @@ public void run() { } // If applicable, check if the user provided privacy consent - if (shouldLogUserPrivacyConsentErrorMessageForMethodName(OSTaskController.SET_EMAIL)) + if (shouldLogUserPrivacyConsentErrorMessageForMethodName(OSTaskController.SET_SMS_NUMBER)) return; if (TextUtils.isEmpty(smsNumber)) { @@ -1473,13 +1509,14 @@ public void run() { if (getRemoteParams().useSMSAuth && (smsAuthHash == null || smsAuthHash.length() == 0)) { String errorMessage = "SMS authentication (auth token) is set to REQUIRED for this application. Please provide an auth token from your backend server or change the setting in the OneSignal dashboard."; if (callback != null) - callback.onFailure(new OSSMSUpdateError(SMSErrorType.REQUIRES_EMAIL_AUTH, errorMessage)); + callback.onFailure(new OSSMSUpdateError(SMSErrorType.REQUIRES_SMS_AUTH, errorMessage)); logger.error(errorMessage); return; } smsUpdateHandler = callback; + getCurrentSMSSubscriptionState(appContext).setSMSNumber(smsNumber); OneSignalStateSynchronizer.setSMSNumber(smsNumber, smsAuthHash); } @@ -2429,6 +2466,35 @@ static void saveEmailId(String id) { "".equals(emailId) ? null : emailId); } + static boolean hasSMSlId() { + return !TextUtils.isEmpty(smsId); + } + + static String getSMSId() { + if (smsId == null && appContext != null) { + smsId = OneSignalPrefs.getString( + OneSignalPrefs.PREFS_ONESIGNAL, + OneSignalPrefs.PREFS_OS_SMS_ID, + null); + } + + if (TextUtils.isEmpty(smsId)) + return null; + + return smsId; + } + + static void saveSMSId(String id) { + smsId = id; + if (appContext == null) + return; + + OneSignalPrefs.saveString( + OneSignalPrefs.PREFS_ONESIGNAL, + OneSignalPrefs.PREFS_OS_SMS_ID, + "".equals(smsId) ? null : smsId); + } + // Called when a player id is returned from OneSignal // Updates anything else that might have been waiting for this id. static void updateUserIdDependents(String userId) { @@ -2458,6 +2524,17 @@ static void updateEmailIdDependents(String emailId) { } } + static void updateSMSIdDependents(String smsId) { + saveSMSId(smsId); + getCurrentSMSSubscriptionState(appContext).setSMSUserId(smsId); + try { + JSONObject updateJson = new JSONObject().put("parent_player_id", smsId); + OneSignalStateSynchronizer.updatePushState(updateJson); + } catch (JSONException e) { + e.printStackTrace(); + } + } + // Start Remote params getters static boolean getFirebaseAnalyticsEnabled() { return remoteParamController.getFirebaseAnalyticsEnabled(); @@ -2881,6 +2958,39 @@ public static void removeEmailSubscriptionObserver(@NonNull OSEmailSubscriptionO getEmailSubscriptionStateChangesObserver().removeObserver(observer); } + /** + * The {@link OSSMSSubscriptionObserver#onSMSSubscriptionChanged(OSSMSSubscriptionStateChanges)} + * method will be fired on the passed-in object when a sms subscription property changes. + *

+ * This includes the following events: + *
+ * - SMS number set + *
+ * - Getting a player/user ID from OneSignal + * @param observer the instance of {@link OSSubscriptionObserver} that acts as the observer + */ + public static void addSMSSubscriptionObserver(@NonNull OSSMSSubscriptionObserver observer) { + + if (appContext == null) { + logger.error("OneSignal.initWithContext has not been called. Could not add sms subscription observer"); + return; + } + + getSMSSubscriptionStateChangesObserver().addObserver(observer); + + if (getCurrentSMSSubscriptionState(appContext).compare(getLastSMSSubscriptionState(appContext))) + OSSMSSubscriptionChangedInternalObserver.fireChangesToPublicObserver(getCurrentSMSSubscriptionState(appContext)); + } + + public static void removeSMSSubscriptionObserver(@NonNull OSSMSSubscriptionObserver observer) { + if (appContext == null) { + logger.error("OneSignal.initWithContext has not been called. Could not modify sms subscription observer"); + return; + } + + getSMSSubscriptionStateChangesObserver().removeObserver(observer); + } + /** In-App Message Triggers */ /** diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalPrefs.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalPrefs.java index fc1c7d460c..cdd39f4603 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalPrefs.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalPrefs.java @@ -103,6 +103,10 @@ class OneSignalPrefs { public static final String PREFS_OS_EMAIL_ID = "OS_EMAIL_ID"; public static final String PREFS_ONESIGNAL_EMAIL_ID_LAST = "PREFS_ONESIGNAL_EMAIL_ID_LAST"; public static final String PREFS_ONESIGNAL_EMAIL_ADDRESS_LAST = "PREFS_ONESIGNAL_EMAIL_ADDRESS_LAST"; + // SMS + public static final String PREFS_OS_SMS_ID = "PREFS_OS_SMS_ID"; + public static final String PREFS_OS_SMS_ID_LAST = "PREFS_OS_SMS_ID_LAST"; + public static final String PREFS_OS_SMS_NUMBER_LAST = "PREFS_OS_SMS_NUMBER_LAST"; // In-App Messaging public static final String PREFS_OS_CACHED_IAMS = "PREFS_OS_CACHED_IAMS"; public static final String PREFS_OS_DISMISSED_IAMS = "PREFS_OS_DISPLAYED_IAMS"; diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java index 8b8223f5f1..258925ded3 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java @@ -97,6 +97,9 @@ static boolean persist() { if (emailPersisted) emailPersisted = getEmailStateSynchronizer().getRegistrationId() != null; + if (smsPersisted) + smsPersisted = false; // TODO: define + return pushPersisted || emailPersisted || smsPersisted; } @@ -175,9 +178,11 @@ static UserStateSynchronizer.GetTagsResult getTags(boolean fromServer) { static void resetCurrentState() { getPushStateSynchronizer().resetCurrentState(); getEmailStateSynchronizer().resetCurrentState(); + getSMSStateSynchronizer().resetCurrentState(); OneSignal.saveUserId(null); OneSignal.saveEmailId(null); + OneSignal.saveSMSId(null); OneSignal.setLastSessionTime(-60 * 61); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java index 90f0fe247b..36ad8960cb 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java @@ -40,6 +40,11 @@ void logoutEmail() { OneSignal.getEmailSubscriptionState().clearEmailAndId(); } + @Override + void logoutSMS() { + + } + @Override protected int getDeviceType() { return UserState.DEVICE_TYPE_EMAIL; diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java index 621c29906c..e50a5e9798 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java @@ -176,6 +176,11 @@ void logoutEmail() { } } + @Override + void logoutSMS() { + + } + @Override protected void fireEventsForUpdateFailure(JSONObject jsonFields) { if (jsonFields.has("email")) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java index c93bbdcd39..a49ca385d7 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java @@ -1,5 +1,8 @@ package com.onesignal; +import java.util.ArrayList; +import java.util.List; + public class UserStateSMSSynchronizer extends UserStateSecondaryChannelSynchronizer { UserStateSMSSynchronizer() { @@ -13,12 +16,27 @@ protected UserState newUserState(String inPersistKey, boolean load) { @Override protected String getId() { - return null; + return OneSignal.getSMSId(); } @Override void logoutEmail() { + } + + @Override + void logoutSMS() { + OneSignal.saveSMSId(""); + + resetCurrentState(); + getToSyncUserState().removeFromSyncValues("identifier"); + List keysToRemove = new ArrayList<>(); + keysToRemove.add("sms_auth_hash"); + keysToRemove.add("device_player_id"); + keysToRemove.add("external_user_id"); + getToSyncUserState().removeFromSyncValues(keysToRemove); + getToSyncUserState().persistState(); + OneSignal.getSMSSubscriptionState().clearSMSAndId(); } @Override @@ -38,7 +56,7 @@ void fireUpdateFailure() { @Override void updateIdDependents(String id) { - + OneSignal.updateSMSIdDependents(id); } void setSMSNumber(String smsNumber, String smsAuthHash) { diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java index d347bcf3bb..daf12fd9df 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java @@ -25,6 +25,9 @@ abstract class UserStateSecondaryChannelSynchronizer extends UserStateSynchroniz @Override abstract void logoutEmail(); + @Override + abstract void logoutSMS(); + abstract protected int getDeviceType(); abstract void fireUpdateSuccess(); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java index 38957e25a7..47f38f1ae1 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java @@ -546,6 +546,8 @@ void updateLocation(LocationController.LocationPoint point) { abstract void logoutEmail(); + abstract void logoutSMS(); + void readyToUpdate(boolean canMakeUpdates) { boolean changed = this.canMakeUpdates != canMakeUpdates; this.canMakeUpdates = canMakeUpdates; diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java index 681a14622c..f211cc9ec4 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java @@ -34,6 +34,7 @@ import org.robolectric.shadows.ShadowLog; import java.lang.reflect.Field; +import java.util.List; import static com.onesignal.OneSignal.ExternalIdErrorType.REQUIRES_EXTERNAL_ID_AUTH; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_getSessionListener; @@ -54,6 +55,7 @@ import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertNotEquals; @Config(packageName = "com.onesignal.example", shadows = { @@ -109,7 +111,6 @@ public void onFailure(OneSignal.EmailUpdateError error) { }; } - private static void cleanUp() throws Exception { lastExternalUserIdResponse = null; lastExternalUserIdError = null; @@ -411,6 +412,27 @@ public void shouldLogoutOfEmail() throws Exception { assertEquals("b4f7f966-d8cc-11e4-bed1-df8f05be55ba", logoutEmailPost.payload.get("app_id")); } + @Test + public void logoutEmailShouldNotSendEmailPlayersRequest() throws Exception { + // 1. Init OneSignal and set email + OneSignalInit(); + + emailSetThenLogout(); + + time.advanceSystemAndElapsedTimeBy(60); + pauseActivity(blankActivityController); + assertAndRunSyncService(); + + List requests = ShadowOneSignalRestClient.requests; + assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request postPush = ShadowOneSignalRestClient.requests.get(4); + assertNotEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511", postPush.url); + + ShadowOneSignalRestClient.Request postEmail = ShadowOneSignalRestClient.requests.get(5); + assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_focus", postEmail.url); + } + @Test public void shouldFireOnSuccessOfLogoutEmail() throws Exception { OneSignalInit(); From ec0f741a0b156c8bde062bc4f96cd77cfd746000 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Wed, 27 Jan 2021 17:45:43 -0300 Subject: [PATCH 07/23] Add on_session and on_focus SMS tests --- .../com/onesignal/FocusTimeController.java | 3 ++ .../main/java/com/onesignal/OneSignal.java | 2 +- .../onesignal/OneSignalStateSynchronizer.java | 12 +++-- .../onesignal/UserStatePushSynchronizer.java | 8 ++++ .../onesignal/UserStateSMSSynchronizer.java | 38 +++++++++++++++ .../com/onesignal/UserStateSynchronizer.java | 2 +- .../OneSignalPackagePrivateHelper.java | 4 ++ .../onesignal/ShadowOneSignalRestClient.java | 25 ++++++++-- .../com/test/onesignal/RestClientAsserts.java | 3 +- .../SynchronizerIntegrationTests.java | 47 +++++++++++++++++++ 10 files changed, 134 insertions(+), 10 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/FocusTimeController.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/FocusTimeController.java index 808df87993..c181d5f0f8 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/FocusTimeController.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/FocusTimeController.java @@ -326,6 +326,9 @@ private void sendOnFocus(long totalTimeActive) { if (OneSignal.hasEmailId()) sendOnFocusToPlayer(OneSignal.getEmailId(), generateOnFocusPayload(totalTimeActive)); + if (OneSignal.hasSMSlId()) + sendOnFocusToPlayer(OneSignal.getSMSId(), generateOnFocusPayload(totalTimeActive)); + saveInfluences(new ArrayList()); } catch (JSONException t) { diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java index f202f5884a..8a3311303d 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java @@ -2508,7 +2508,7 @@ static void updateUserIdDependents(String userId) { iapUpdateJob = null; } - OneSignalStateSynchronizer.refreshEmailState(); + OneSignalStateSynchronizer.refreshSecondaryChannelState(); OneSignalChromeTabAndroidFrame.setup(appId, userId, AdvertisingIdProviderGPS.getLastValue(), getRemoteParams()); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java index 258925ded3..83bdf29377 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java @@ -98,7 +98,7 @@ static boolean persist() { emailPersisted = getEmailStateSynchronizer().getRegistrationId() != null; if (smsPersisted) - smsPersisted = false; // TODO: define + smsPersisted = getSMSStateSynchronizer().getRegistrationId() != null; return pushPersisted || emailPersisted || smsPersisted; } @@ -161,6 +161,7 @@ static void setPermission(boolean enable) { static void updateLocation(LocationController.LocationPoint point) { getPushStateSynchronizer().updateLocation(point); getEmailStateSynchronizer().updateLocation(point); + getSMSStateSynchronizer().updateLocation(point); } static boolean getSubscribed() { @@ -190,24 +191,28 @@ static void resetCurrentState() { static void updateDeviceInfo(JSONObject deviceInfo) { getPushStateSynchronizer().updateDeviceInfo(deviceInfo); getEmailStateSynchronizer().updateDeviceInfo(deviceInfo); + getSMSStateSynchronizer().updateDeviceInfo(deviceInfo); } static void updatePushState(JSONObject pushState) { getPushStateSynchronizer().updateState(pushState); } - static void refreshEmailState() { + static void refreshSecondaryChannelState() { getEmailStateSynchronizer().refresh(); + getSMSStateSynchronizer().refresh(); } static void setNewSession() { getPushStateSynchronizer().setNewSession(); getEmailStateSynchronizer().setNewSession(); + getSMSStateSynchronizer().setNewSession(); } static boolean getSyncAsNewSession() { return getPushStateSynchronizer().getSyncAsNewSession() || - getEmailStateSynchronizer().getSyncAsNewSession(); + getEmailStateSynchronizer().getSyncAsNewSession() || + getSMSStateSynchronizer().getSyncAsNewSession(); } static void setNewSessionForEmail() { @@ -267,6 +272,7 @@ public void run() { static void readyToUpdate(boolean canMakeUpdates) { getPushStateSynchronizer().readyToUpdate(canMakeUpdates); getEmailStateSynchronizer().readyToUpdate(canMakeUpdates); + getSMSStateSynchronizer().readyToUpdate(canMakeUpdates); } } \ No newline at end of file diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java index e50a5e9798..379ba2fd18 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java @@ -129,6 +129,14 @@ void setEmail(String email, String emailAuthHash) { } void setSMSNumber(String smsNumber, String smsAuthHash) { + try { + UserState userState = getUserStateForModification(); + + userState.putOnDependValues("sms_auth_hash", smsAuthHash); + userState.generateJsonDiffFromIntoSyncValued(new JSONObject().put("sms_number", smsNumber), null); + } catch (JSONException e) { + e.printStackTrace(); + } } @Override diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java index a49ca385d7..443b14d9d7 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java @@ -1,5 +1,8 @@ package com.onesignal; +import org.json.JSONException; +import org.json.JSONObject; + import java.util.ArrayList; import java.util.List; @@ -60,5 +63,40 @@ void updateIdDependents(String id) { } void setSMSNumber(String smsNumber, String smsAuthHash) { + UserState userState = getUserStateForModification(); + ImmutableJSONObject syncValues = userState.getSyncValues(); + + boolean noChange = smsNumber.equals(syncValues.optString("identifier")) && + syncValues.optString("sms_auth_hash").equals(smsAuthHash == null ? "" : smsAuthHash); + if (noChange) { + fireUpdateSuccess(); + return; + } + + String existingsms = syncValues.optString("identifier", null); + + if (existingsms == null) + setNewSession(); + + try { + JSONObject smsJSON = new JSONObject(); + smsJSON.put("identifier", smsNumber); + + if (smsAuthHash != null) + smsJSON.put("sms_auth_hash", smsAuthHash); + + if (smsAuthHash == null) { + if (existingsms != null && !existingsms.equals(smsNumber)) { + OneSignal.saveSMSId(""); + resetCurrentState(); + setNewSession(); + } + } + + userState.generateJsonDiffFromIntoSyncValued(smsJSON, null); + scheduleSyncToServer(); + } catch (JSONException e) { + e.printStackTrace(); + } } } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java index 47f38f1ae1..38bd7e4659 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java @@ -82,7 +82,7 @@ class NetworkHandlerThread extends HandlerThread { int currentRetry; NetworkHandlerThread(int type) { - super("OSH_NetworkHandlerThread"); + super("OSH_NetworkHandlerThread_" + UserStateSynchronizer.this.channel); mType = type; start(); mHandler = new Handler(getLooper()); diff --git a/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java b/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java index d8ecdf2b97..3929c43687 100644 --- a/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java +++ b/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java @@ -44,6 +44,10 @@ private static void processNetworkHandles(RunnableArg runnable) throws Exception entrySet = OneSignalStateSynchronizer.getEmailStateSynchronizer().networkHandlerThreads.entrySet(); for (Map.Entry handlerThread : entrySet) runnable.run(handlerThread.getValue()); + + entrySet = OneSignalStateSynchronizer.getSMSStateSynchronizer().networkHandlerThreads.entrySet(); + for (Map.Entry handlerThread : entrySet) + runnable.run(handlerThread.getValue()); } private static boolean startedRunnable; diff --git a/OneSignalSDK/unittest/src/test/java/com/onesignal/ShadowOneSignalRestClient.java b/OneSignalSDK/unittest/src/test/java/com/onesignal/ShadowOneSignalRestClient.java index 6f4fdd874d..6a5403d39a 100644 --- a/OneSignalSDK/unittest/src/test/java/com/onesignal/ShadowOneSignalRestClient.java +++ b/OneSignalSDK/unittest/src/test/java/com/onesignal/ShadowOneSignalRestClient.java @@ -39,9 +39,15 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; +import static com.onesignal.UserState.DEVICE_TYPE_EMAIL; +import static com.onesignal.UserState.DEVICE_TYPE_SMS; + @Implements(OneSignalRestClient.class) public class ShadowOneSignalRestClient { + public static final String PUSH_USER_ID = "a2f7f967-e8cc-11e4-bed1-118f05be4511"; + public static final String EMAIL_USER_ID = "b007f967-98cc-11e4-bed1-118f05be4522"; + public static final String SMS_USER_ID = "d007f967-98cc-11e4-bed1-118f05be4522"; public enum REST_METHOD { GET, POST, PUT } @@ -86,7 +92,7 @@ private static class PendingResponse { public static List successfulGETResponses = new ArrayList<>(); public static ArrayList requests; - public static String lastUrl, failResponse, nextSuccessResponse, nextSuccessfulGETResponse, nextSuccessfulRegistrationResponse, pushUserId, emailUserId, failMethod; + public static String lastUrl, failResponse, nextSuccessResponse, nextSuccessfulGETResponse, nextSuccessfulRegistrationResponse, pushUserId, emailUserId, smsUserId, failMethod; public static Pattern nextSuccessfulGETResponsePattern; public static JSONObject lastPost; public static boolean failNext, failNextPut, failAll, failPosts, failGetParams; @@ -134,8 +140,9 @@ private static class PendingResponse { } public static void resetStatics() { - pushUserId = "a2f7f967-e8cc-11e4-bed1-118f05be4511"; - emailUserId = "b007f967-98cc-11e4-bed1-118f05be4522"; + pushUserId = PUSH_USER_ID; + emailUserId = EMAIL_USER_ID; + smsUserId = SMS_USER_ID; requests = new ArrayList<>(); lastPost = null; @@ -304,7 +311,17 @@ else if (url.contains("on_session")) retJson = "{}"; else { int device_type = jsonBody.optInt("device_type", 0); - String id = device_type == 11 ? emailUserId : pushUserId; + String id; + switch (device_type) { + case DEVICE_TYPE_EMAIL: + id = emailUserId; + break; + case DEVICE_TYPE_SMS: + id = smsUserId; + break; + default: + id = pushUserId; + } retJson = "{\"id\": \"" + id + "\"}"; } diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/RestClientAsserts.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/RestClientAsserts.java index 01824ae8a8..9df09ddd25 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/RestClientAsserts.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/RestClientAsserts.java @@ -33,7 +33,8 @@ class RestClientAsserts { is(UserState.DEVICE_TYPE_ANDROID), is(UserState.DEVICE_TYPE_FIREOS), is(UserState.DEVICE_TYPE_EMAIL), - is(UserState.DEVICE_TYPE_HUAWEI) + is(UserState.DEVICE_TYPE_HUAWEI), + is(UserState.DEVICE_TYPE_SMS) ); private static final AnyOf ANY_OF_PUSH_DEVICE_TYPES = anyOf( diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java index f211cc9ec4..731c59c80f 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java @@ -41,6 +41,7 @@ import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setSessionManager; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTime; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTrackerFactory; +import static com.onesignal.ShadowOneSignalRestClient.SMS_USER_ID; import static com.onesignal.ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse; import static com.test.onesignal.RestClientAsserts.assertOnFocusAtIndex; import static com.test.onesignal.RestClientAsserts.assertRestCalls; @@ -69,6 +70,7 @@ @RunWith(RobolectricTestRunner.class) public class SynchronizerIntegrationTests { private static final String ONESIGNAL_APP_ID = "b4f7f966-d8cc-11e4-bed1-df8f05be55ba"; + private static final String ONESIGNAL_SMS_NUMBER = "123456789"; @SuppressLint("StaticFieldLeak") private static Activity blankActivity; @@ -507,6 +509,21 @@ public void shouldSendOnSessionToEmail() throws Exception { assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522/on_session", emailPost.url); } + @Test + public void shouldSendOnSessionToSMS() throws Exception { + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + restartAppAndElapseTimeToNextSession(time); + OneSignalInit(); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(6); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); + assertEquals("players/" + ShadowOneSignalRestClient.SMS_USER_ID + "/on_session", smsPost.url); + } + @Test public void shouldSendExternalUserIdAfterRegistration() throws Exception { OneSignalInit(); @@ -1062,6 +1079,9 @@ public void sendsOnFocusToEmail() throws Exception { assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + ShadowOneSignalRestClient.Request post = ShadowOneSignalRestClient.requests.get(3); + assertFalse(post.url.contains("on_focus")); + ShadowOneSignalRestClient.Request postPush = ShadowOneSignalRestClient.requests.get(4); assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_focus", postPush.url); assertEquals(60, postPush.payload.getInt("active_time")); @@ -1071,6 +1091,33 @@ public void sendsOnFocusToEmail() throws Exception { assertEquals(60, postEmail.payload.getInt("active_time")); } + @Test + public void sendsOnFocusToSMS() throws Exception { + time.advanceSystemAndElapsedTimeBy(0); + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + blankActivityController.resume(); + threadAndTaskWait(); + time.advanceSystemAndElapsedTimeBy(60); + pauseActivity(blankActivityController); + assertAndRunSyncService(); + + assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request post = ShadowOneSignalRestClient.requests.get(3); + assertFalse(post.url.contains("on_focus")); + + ShadowOneSignalRestClient.Request postPush = ShadowOneSignalRestClient.requests.get(4); + assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_focus", postPush.url); + assertEquals(60, postPush.payload.getInt("active_time")); + + ShadowOneSignalRestClient.Request postSMSl = ShadowOneSignalRestClient.requests.get(5); + assertEquals("players/" + SMS_USER_ID + "/on_focus", postSMSl.url); + assertEquals(60, postSMSl.payload.getInt("active_time")); + } + private void OneSignalInit() { OneSignal.setLogLevel(OneSignal.LOG_LEVEL.VERBOSE, OneSignal.LOG_LEVEL.NONE); ShadowOSUtils.subscribableStatus = 1; From aad40988c350e0ea5e7580ba65a400b04eca4dd7 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 1 Feb 2021 12:02:12 -0300 Subject: [PATCH 08/23] Add sms set and update tags tests * Create tests for sms only * Create tests for sms and email --- .../SynchronizerIntegrationTests.java | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java index 731c59c80f..88c44a3ef5 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java @@ -11,6 +11,7 @@ import com.onesignal.MockOneSignalDBHelper; import com.onesignal.MockSessionManager; import com.onesignal.OneSignal; +import com.onesignal.OneSignalPackagePrivateHelper; import com.onesignal.ShadowCustomTabsClient; import com.onesignal.ShadowCustomTabsSession; import com.onesignal.ShadowGMSLocationController; @@ -70,6 +71,7 @@ @RunWith(RobolectricTestRunner.class) public class SynchronizerIntegrationTests { private static final String ONESIGNAL_APP_ID = "b4f7f966-d8cc-11e4-bed1-df8f05be55ba"; + private static final String ONESIGNAL_EMAIL_ADDRESS = "test@onesignal.com"; private static final String ONESIGNAL_SMS_NUMBER = "123456789"; @SuppressLint("StaticFieldLeak") @@ -190,6 +192,61 @@ public void shouldSetEmail() throws Exception { assertFalse(pushPut.has("identifier")); } + @Test + public void shouldSetSMS() throws Exception { + OneSignalInit(); + + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + assertEquals(4, ShadowOneSignalRestClient.networkCallCount); + + JSONObject pushPost = ShadowOneSignalRestClient.requests.get(1).payload; + assertEquals(ONESIGNAL_SMS_NUMBER, pushPost.getString("sms_number")); + assertEquals(1, pushPost.getInt("device_type")); + + JSONObject smsPost = ShadowOneSignalRestClient.requests.get(2).payload; + assertEquals(ONESIGNAL_SMS_NUMBER, smsPost.getString("identifier")); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_SMS, smsPost.getInt("device_type")); + assertEquals(ShadowOneSignalRestClient.pushUserId, smsPost.getString("device_player_id")); + + JSONObject pushPut = ShadowOneSignalRestClient.requests.get(3).payload; + assertEquals(ShadowOneSignalRestClient.smsUserId, pushPut.getString("parent_player_id")); + assertFalse(pushPut.has("identifier")); + } + + @Test + public void shouldSetSMSAndEmail() throws Exception { + OneSignalInit(); + + OneSignal.setEmail(ONESIGNAL_EMAIL_ADDRESS); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + assertEquals(5, ShadowOneSignalRestClient.networkCallCount); + + JSONObject pushPost = ShadowOneSignalRestClient.requests.get(1).payload; + assertEquals(ONESIGNAL_EMAIL_ADDRESS, pushPost.getString("email")); + assertEquals(ONESIGNAL_SMS_NUMBER, pushPost.getString("sms_number")); + assertEquals(1, pushPost.getInt("device_type")); + + JSONObject emailPost = ShadowOneSignalRestClient.requests.get(2).payload; + assertEquals(ONESIGNAL_EMAIL_ADDRESS, emailPost.getString("identifier")); + assertEquals(11, emailPost.getInt("device_type")); + assertEquals(ShadowOneSignalRestClient.pushUserId, emailPost.getString("device_player_id")); + + JSONObject smsPost = ShadowOneSignalRestClient.requests.get(3).payload; + assertEquals(ONESIGNAL_SMS_NUMBER, smsPost.getString("identifier")); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_SMS, smsPost.getInt("device_type")); + assertEquals(ShadowOneSignalRestClient.pushUserId, smsPost.getString("device_player_id")); + + JSONObject pushPut = ShadowOneSignalRestClient.requests.get(4).payload; + assertEquals(ShadowOneSignalRestClient.smsUserId, pushPut.getString("parent_player_id")); + // add email parent id call + // TODO: check if this behaviour is not dropped + assertFalse(pushPut.has("identifier")); + } + @Test public void shouldSendTagsToEmailBeforeCreate() throws Exception { OneSignalInit(); @@ -206,6 +263,22 @@ public void shouldSendTagsToEmailBeforeCreate() throws Exception { assertEquals(tagsJson.toString(), emailPost.payload.getJSONObject("tags").toString()); } + @Test + public void shouldSendTagsToSMSBeforeCreate() throws Exception { + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + JSONObject tagsJson = new JSONObject("{\"test1\": \"value1\", \"test2\": \"value2\"}"); + OneSignal.sendTags(tagsJson); + threadAndTaskWait(); + + assertEquals(4, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(2); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); + assertEquals(ONESIGNAL_SMS_NUMBER, smsPost.payload.get("identifier")); + assertEquals(tagsJson.toString(), smsPost.payload.getJSONObject("tags").toString()); + } + @Test public void shouldWaitBeforeCreateEmailIfPushCreateFails() throws Exception { ShadowOneSignalRestClient.failPosts = true; @@ -233,6 +306,72 @@ public void shouldWaitBeforeCreateEmailIfPushCreateFails() throws Exception { assertEquals("josh@onesignal.com", emailPost.payload.get("identifier")); } + @Test + public void shouldWaitBeforeCreateSMSIfPushCreateFails() throws Exception { + ShadowOneSignalRestClient.failPosts = true; + + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + // Assert we are sending / retry for the push player first. + assertEquals(5, ShadowOneSignalRestClient.networkCallCount); + for(int i = 1; i < ShadowOneSignalRestClient.networkCallCount; i++) { + ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(i); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); + assertEquals(ONESIGNAL_SMS_NUMBER, smsPost.payload.getString("sms_number")); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_ANDROID, smsPost.payload.getInt("device_type")); + } + + // Turn off fail mocking, call sendTags to trigger another retry + ShadowOneSignalRestClient.failPosts = false; + OneSignal.sendTag("test", "test"); + threadAndTaskWait(); + + // Should now POST to create device_type 14 (sms) + ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(6); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); + assertEquals(ONESIGNAL_SMS_NUMBER, smsPost.payload.get("identifier")); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_SMS, smsPost.payload.get("device_type")); + } + + @Test + public void shouldWaitBeforeCreateEmailAndSMSIfPushCreateFails() throws Exception { + ShadowOneSignalRestClient.failPosts = true; + + OneSignalInit(); + OneSignal.setEmail(ONESIGNAL_EMAIL_ADDRESS); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + // Assert we are sending / retry for the push player first. + assertEquals(5, ShadowOneSignalRestClient.networkCallCount); + for(int i = 1; i < ShadowOneSignalRestClient.networkCallCount; i++) { + ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(i); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); + assertEquals(ONESIGNAL_EMAIL_ADDRESS, smsPost.payload.getString("email")); + assertEquals(ONESIGNAL_SMS_NUMBER, smsPost.payload.getString("sms_number")); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_ANDROID, smsPost.payload.getInt("device_type")); + } + + // Turn off fail mocking, call sendTags to trigger another retry + ShadowOneSignalRestClient.failPosts = false; + OneSignal.sendTag("test", "test"); + threadAndTaskWait(); + + // Should now POST to create device_type 11 (email) + ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(6); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); + assertEquals(ONESIGNAL_EMAIL_ADDRESS, emailPost.payload.get("identifier")); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_EMAIL, emailPost.payload.get("device_type")); + + // Should now POST to create device_type 14 (email) + ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(7); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); + assertEquals(ONESIGNAL_SMS_NUMBER, smsPost.payload.get("identifier")); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_SMS, smsPost.payload.get("device_type")); + } + @Test public void shouldSendTagsToEmailAfterCreate() throws Exception { OneSignalInit(); @@ -251,6 +390,48 @@ public void shouldSendTagsToEmailAfterCreate() throws Exception { assertEquals(tagsJson.toString(), emailPut.payload.getJSONObject("tags").toString()); } + @Test + public void shouldSendTagsToSMSAfterCreate() throws Exception { + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + JSONObject tagsJson = new JSONObject("{\"test1\": \"value1\", \"test2\": \"value2\"}"); + OneSignal.sendTags(tagsJson); + threadAndTaskWait(); + + assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request smsPut = ShadowOneSignalRestClient.requests.get(5); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, smsPut.method); + assertEquals("players/" + ShadowOneSignalRestClient.smsUserId, smsPut.url); + assertEquals(tagsJson.toString(), smsPut.payload.getJSONObject("tags").toString()); + } + + @Test + public void shouldSendTagsToEmailAndSMSAfterCreate() throws Exception { + OneSignalInit(); + OneSignal.setEmail(ONESIGNAL_EMAIL_ADDRESS); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + JSONObject tagsJson = new JSONObject("{\"test1\": \"value1\", \"test2\": \"value2\"}"); + OneSignal.sendTags(tagsJson); + threadAndTaskWait(); + + assertEquals(8, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request emailPut = ShadowOneSignalRestClient.requests.get(6); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, emailPut.method); + assertEquals("players/" + ShadowOneSignalRestClient.emailUserId, emailPut.url); + assertEquals(tagsJson.toString(), emailPut.payload.getJSONObject("tags").toString()); + + ShadowOneSignalRestClient.Request smsPut = ShadowOneSignalRestClient.requests.get(7); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, smsPut.method); + assertEquals("players/" + ShadowOneSignalRestClient.smsUserId, smsPut.url); + assertEquals(tagsJson.toString(), smsPut.payload.getJSONObject("tags").toString()); + } + @Test public void shouldSetEmailWithAuthHash() throws Exception { OneSignalInit(); From 9190fe95ef7cee97923fd1983e83bdc0de17a2dd Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 1 Feb 2021 15:53:18 -0300 Subject: [PATCH 09/23] Add SMS update callback tests * Add failure and success callbacks for SMS * Add JSONObject response for SMS success callback --- .../main/java/com/onesignal/OneSignal.java | 14 +++ .../onesignal/UserStateEmailSynchronizer.java | 31 +++-- .../onesignal/UserStatePushSynchronizer.java | 15 +++ .../onesignal/UserStateSMSSynchronizer.java | 38 ++++-- ...UserStateSecondaryChannelSynchronizer.java | 25 ++-- .../java/com/onesignal/StaticResetHelper.java | 3 - .../SynchronizerIntegrationTests.java | 111 ++++++++++++++++++ 7 files changed, 209 insertions(+), 28 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java index 8a3311303d..63e0ded346 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java @@ -3225,6 +3225,20 @@ static void fireEmailUpdateFailure() { } } + static void fireSMSUpdateSuccess(JSONObject result) { + if (smsUpdateHandler != null) { + smsUpdateHandler.onSuccess(result); + smsUpdateHandler = null; + } + } + + static void fireSMSUpdateFailure() { + if (smsUpdateHandler != null) { + smsUpdateHandler.onFailure(new OSSMSUpdateError(SMSErrorType.NETWORK, "Failed due to network failure. Will retry on next sync.")); + smsUpdateHandler = null; + } + } + @NonNull static OSTime getTime() { return time; diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java index 36ad8960cb..62db15a31f 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java @@ -10,6 +10,9 @@ class UserStateEmailSynchronizer extends UserStateSecondaryChannelSynchronizer { + private static final String EMAIL_KEY = "email"; + private static final String EMAIL_AUTH_HASH_KEY = "email_auth_hash"; + UserStateEmailSynchronizer() { super(UserStateSynchronizerType.EMAIL); } @@ -29,9 +32,9 @@ void logoutEmail() { OneSignal.saveEmailId(""); resetCurrentState(); - getToSyncUserState().removeFromSyncValues("identifier"); + getToSyncUserState().removeFromSyncValues(IDENTIFIER); List keysToRemove = new ArrayList<>(); - keysToRemove.add("email_auth_hash"); + keysToRemove.add(EMAIL_AUTH_HASH_KEY); keysToRemove.add("device_player_id"); keysToRemove.add("external_user_id"); getToSyncUserState().removeFromSyncValues(keysToRemove); @@ -45,13 +48,23 @@ void logoutSMS() { } + @Override + protected String getChannelKey() { + return EMAIL_KEY; + } + + @Override + protected String getAuthHashKey() { + return EMAIL_AUTH_HASH_KEY; + } + @Override protected int getDeviceType() { return UserState.DEVICE_TYPE_EMAIL; } @Override - void fireUpdateSuccess() { + void fireUpdateSuccess(JSONObject result) { OneSignal.fireEmailUpdateSuccess(); } @@ -69,24 +82,24 @@ void setEmail(String email, String emailAuthHash) { UserState userState = getUserStateForModification(); ImmutableJSONObject syncValues = userState.getSyncValues(); - boolean noChange = email.equals(syncValues.optString("identifier")) && - syncValues.optString("email_auth_hash").equals(emailAuthHash == null ? "" : emailAuthHash); + boolean noChange = email.equals(syncValues.optString(IDENTIFIER)) && + syncValues.optString(getAuthHashKey()).equals(emailAuthHash == null ? "" : emailAuthHash); if (noChange) { - fireUpdateSuccess(); + fireUpdateSuccess(null); return; } - String existingEmail = syncValues.optString("identifier", null); + String existingEmail = syncValues.optString(IDENTIFIER, null); if (existingEmail == null) setNewSession(); try { JSONObject emailJSON = new JSONObject(); - emailJSON.put("identifier", email); + emailJSON.put(IDENTIFIER, email); if (emailAuthHash != null) - emailJSON.put("email_auth_hash", emailAuthHash); + emailJSON.put(getAuthHashKey(), emailAuthHash); if (emailAuthHash == null) { if (existingEmail != null && !existingEmail.equals(email)) { diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java index 379ba2fd18..5fd8328bec 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java @@ -193,11 +193,26 @@ void logoutSMS() { protected void fireEventsForUpdateFailure(JSONObject jsonFields) { if (jsonFields.has("email")) OneSignal.fireEmailUpdateFailure(); + + if (jsonFields.has("sms_number")) + OneSignal.fireSMSUpdateFailure(); } @Override protected void onSuccessfulSync(JSONObject jsonFields) { if (jsonFields.has("email")) OneSignal.fireEmailUpdateSuccess(); + + if (jsonFields.has("sms_number")) { + JSONObject result = new JSONObject(); + try { + result.put("sms_number", jsonFields.get("sms_number")); + if (jsonFields.has("sms_auth_hash")) + result.put("sms_auth_hash", jsonFields.get("sms_auth_hash")); + } catch (JSONException e) { + e.printStackTrace(); + } + OneSignal.fireSMSUpdateSuccess(result); + } } } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java index 443b14d9d7..91f894dc33 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java @@ -8,6 +8,9 @@ public class UserStateSMSSynchronizer extends UserStateSecondaryChannelSynchronizer { + private static final String SMS_NUMBER_KEY = "sms_number"; + private static final String SMS_AUTH_HASH_KEY = "sms_auth_hash"; + UserStateSMSSynchronizer() { super(OneSignalStateSynchronizer.UserStateSynchronizerType.SMS); } @@ -42,19 +45,29 @@ void logoutSMS() { OneSignal.getSMSSubscriptionState().clearSMSAndId(); } + @Override + protected String getChannelKey() { + return SMS_NUMBER_KEY; + } + + @Override + protected String getAuthHashKey() { + return SMS_AUTH_HASH_KEY; + } + @Override protected int getDeviceType() { return UserState.DEVICE_TYPE_SMS; } @Override - void fireUpdateSuccess() { - + void fireUpdateSuccess(JSONObject result) { + OneSignal.fireSMSUpdateSuccess(result); } @Override void fireUpdateFailure() { - + OneSignal.fireSMSUpdateFailure(); } @Override @@ -66,24 +79,31 @@ void setSMSNumber(String smsNumber, String smsAuthHash) { UserState userState = getUserStateForModification(); ImmutableJSONObject syncValues = userState.getSyncValues(); - boolean noChange = smsNumber.equals(syncValues.optString("identifier")) && - syncValues.optString("sms_auth_hash").equals(smsAuthHash == null ? "" : smsAuthHash); + boolean noChange = smsNumber.equals(syncValues.optString(IDENTIFIER)) && + syncValues.optString(getAuthHashKey()).equals(smsAuthHash == null ? "" : smsAuthHash); if (noChange) { - fireUpdateSuccess(); + JSONObject result = new JSONObject(); + try { + result.put(IDENTIFIER, smsNumber); + result.put(getAuthHashKey(), smsAuthHash); + } catch (JSONException e) { + e.printStackTrace(); + } + fireUpdateSuccess(result); return; } - String existingsms = syncValues.optString("identifier", null); + String existingsms = syncValues.optString(IDENTIFIER, null); if (existingsms == null) setNewSession(); try { JSONObject smsJSON = new JSONObject(); - smsJSON.put("identifier", smsNumber); + smsJSON.put(IDENTIFIER, smsNumber); if (smsAuthHash != null) - smsJSON.put("sms_auth_hash", smsAuthHash); + smsJSON.put(getAuthHashKey(), smsAuthHash); if (smsAuthHash == null) { if (existingsms != null && !existingsms.equals(smsNumber)) { diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java index daf12fd9df..da3d3e2785 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java @@ -7,11 +7,10 @@ import org.json.JSONException; import org.json.JSONObject; -import java.util.ArrayList; -import java.util.List; - abstract class UserStateSecondaryChannelSynchronizer extends UserStateSynchronizer { + protected static final String IDENTIFIER = "identifier"; + UserStateSecondaryChannelSynchronizer(UserStateSynchronizerType channel) { super(channel); } @@ -28,9 +27,12 @@ abstract class UserStateSecondaryChannelSynchronizer extends UserStateSynchroniz @Override abstract void logoutSMS(); + abstract protected String getChannelKey(); + abstract protected String getAuthHashKey(); + abstract protected int getDeviceType(); - abstract void fireUpdateSuccess(); + abstract void fireUpdateSuccess(JSONObject result); abstract void fireUpdateFailure(); @@ -104,13 +106,22 @@ protected void addOnSessionOrCreateExtras(JSONObject jsonBody) { @Override protected void fireEventsForUpdateFailure(JSONObject jsonFields) { - if (jsonFields.has("identifier")) + if (jsonFields.has(IDENTIFIER)) fireUpdateFailure(); } @Override protected void onSuccessfulSync(JSONObject jsonFields) { - if (jsonFields.has("identifier")) - fireUpdateSuccess(); + if (jsonFields.has(IDENTIFIER)) { + JSONObject result = new JSONObject(); + try { + result.put(getChannelKey(), jsonFields.get(IDENTIFIER)); + if (jsonFields.has(getAuthHashKey())) + result.put(getAuthHashKey(), jsonFields.get(getAuthHashKey())); + } catch (JSONException e) { + e.printStackTrace(); + } + fireUpdateSuccess(result); + } } } diff --git a/OneSignalSDK/unittest/src/test/java/com/onesignal/StaticResetHelper.java b/OneSignalSDK/unittest/src/test/java/com/onesignal/StaticResetHelper.java index cb441438e1..34fb3387fd 100644 --- a/OneSignalSDK/unittest/src/test/java/com/onesignal/StaticResetHelper.java +++ b/OneSignalSDK/unittest/src/test/java/com/onesignal/StaticResetHelper.java @@ -2,8 +2,6 @@ package com.onesignal; -import android.util.Log; - import org.json.JSONArray; import java.lang.reflect.Field; @@ -13,7 +11,6 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; public class StaticResetHelper { diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java index 88c44a3ef5..25dfd57720 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java @@ -447,6 +447,41 @@ public void shouldSetEmailWithAuthHash() throws Exception { assertEquals(mockEmailHash, emailPost.getString("email_auth_hash")); } + @Test + public void shouldSetSMSWithAuthHash() throws Exception { + OneSignalInit(); + String mockSMSHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockSMSHash); + threadAndTaskWait(); + + JSONObject smsPost = ShadowOneSignalRestClient.requests.get(2).payload; + assertEquals(ONESIGNAL_SMS_NUMBER, smsPost.getString("identifier")); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_SMS, smsPost.getInt("device_type")); + assertEquals(mockSMSHash, smsPost.getString("sms_auth_hash")); + } + + @Test + public void shouldSetEmailAndSMSWithAuthHash() throws Exception { + OneSignalInit(); + String mockEmailHash = new String(new char[64]).replace('\0', '1'); + String mockSMSHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setEmail(ONESIGNAL_EMAIL_ADDRESS, mockEmailHash); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockSMSHash); + threadAndTaskWait(); + + JSONObject emailPost = ShadowOneSignalRestClient.requests.get(2).payload; + assertEquals(ONESIGNAL_EMAIL_ADDRESS, emailPost.getString("identifier")); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_EMAIL, emailPost.getInt("device_type")); + assertEquals(mockEmailHash, emailPost.getString("email_auth_hash")); + + JSONObject smsPost = ShadowOneSignalRestClient.requests.get(3).payload; + assertEquals(ONESIGNAL_SMS_NUMBER, smsPost.getString("identifier")); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_SMS, smsPost.getInt("device_type")); + assertEquals(mockSMSHash, smsPost.getString("sms_auth_hash")); + } + private class TestEmailUpdateHandler implements OneSignal.EmailUpdateHandler { boolean emailFiredSuccess = false; OneSignal.EmailUpdateError emailFiredFailure = null; @@ -462,6 +497,21 @@ public void onFailure(OneSignal.EmailUpdateError error) { } } + private class TestSMSUpdateHandler implements OneSignal.OSSMSUpdateHandler { + JSONObject smsResult = null; + OneSignal.OSSMSUpdateError smsFiredFailure = null; + + @Override + public void onSuccess(JSONObject result) { + smsResult = result; + } + + @Override + public void onFailure(OneSignal.OSSMSUpdateError error) { + smsFiredFailure = error; + } + } + @Test public void shouldFireOnSuccessOfEmailUpdate() throws Exception { OneSignalInit(); @@ -474,6 +524,20 @@ public void shouldFireOnSuccessOfEmailUpdate() throws Exception { assertNull(testEmailUpdateHandler.emailFiredFailure); } + @Test + public void shouldFireOnSuccessOfSMSUpdate() throws Exception { + OneSignalInit(); + TestSMSUpdateHandler testSMSUpdateHandler = new TestSMSUpdateHandler(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, testSMSUpdateHandler); + assertNull(testSMSUpdateHandler.smsResult); + threadAndTaskWait(); + + assertNotNull(testSMSUpdateHandler.smsResult); + assertEquals(1, testSMSUpdateHandler.smsResult.length()); + assertEquals(ONESIGNAL_SMS_NUMBER, testSMSUpdateHandler.smsResult.get("sms_number")); + assertNull(testSMSUpdateHandler.smsFiredFailure); + } + @Test public void shouldFireOnSuccessOfEmailEvenWhenNoChanges() throws Exception { OneSignalInit(); @@ -489,6 +553,22 @@ public void shouldFireOnSuccessOfEmailEvenWhenNoChanges() throws Exception { assertNull(testEmailUpdateHandler.emailFiredFailure); } + @Test + public void shouldFireOnSuccessOfSMSEvenWhenNoChanges() throws Exception { + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + TestSMSUpdateHandler testSMSUpdateHandler = new TestSMSUpdateHandler(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, testSMSUpdateHandler); + threadAndTaskWait(); + + assertNotNull(testSMSUpdateHandler.smsResult); + assertEquals(1, testSMSUpdateHandler.smsResult.length()); + assertEquals(ONESIGNAL_SMS_NUMBER, testSMSUpdateHandler.smsResult.get("identifier")); + assertNull(testSMSUpdateHandler.smsFiredFailure); + } + @Test public void shouldFireOnFailureOfEmailUpdateOnNetworkFailure() throws Exception { OneSignalInit(); @@ -501,6 +581,37 @@ public void shouldFireOnFailureOfEmailUpdateOnNetworkFailure() throws Exception assertEquals(OneSignal.EmailErrorType.NETWORK, testEmailUpdateHandler.emailFiredFailure.getType()); } + @Test + public void shouldFireOnFailureOfSMSlUpdateOnNetworkFailure() throws Exception { + OneSignalInit(); + TestSMSUpdateHandler testSMSUpdateHandler = new TestSMSUpdateHandler(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, testSMSUpdateHandler); + ShadowOneSignalRestClient.failAll = true; + threadAndTaskWait(); + + assertNull(testSMSUpdateHandler.smsResult); + assertNotNull(testSMSUpdateHandler.smsFiredFailure); + assertEquals(OneSignal.SMSErrorType.NETWORK, testSMSUpdateHandler.smsFiredFailure.getType()); + } + + @Test + public void shouldFireOnFailureOfEmailAndSMSlUpdateOnNetworkFailure() throws Exception { + OneSignalInit(); + TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); + OneSignal.setEmail(ONESIGNAL_EMAIL_ADDRESS, testEmailUpdateHandler); + TestSMSUpdateHandler testSMSUpdateHandler = new TestSMSUpdateHandler(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, testSMSUpdateHandler); + ShadowOneSignalRestClient.failAll = true; + threadAndTaskWait(); + + assertFalse(testEmailUpdateHandler.emailFiredSuccess); + assertEquals(OneSignal.EmailErrorType.NETWORK, testEmailUpdateHandler.emailFiredFailure.getType()); + + assertNull(testSMSUpdateHandler.smsResult); + assertNotNull(testSMSUpdateHandler.smsFiredFailure); + assertEquals(OneSignal.SMSErrorType.NETWORK, testSMSUpdateHandler.smsFiredFailure.getType()); + } + @Test public void shouldFireOnSuccessOnlyAfterNetworkCallAfterLogout() throws Exception { OneSignalInit(); From a5c6a9238ad40919f3bd15510218f170006c901a Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 1 Feb 2021 16:10:17 -0300 Subject: [PATCH 10/23] Add string constants * Share string constants between all UserStateSynchronizer --- .../onesignal/UserStateEmailSynchronizer.java | 7 +- .../onesignal/UserStatePushSynchronizer.java | 58 ++++++------ .../onesignal/UserStateSMSSynchronizer.java | 3 - ...UserStateSecondaryChannelSynchronizer.java | 6 +- .../com/onesignal/UserStateSynchronizer.java | 92 ++++++++++++------- 5 files changed, 91 insertions(+), 75 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java index 62db15a31f..43bdbee7af 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java @@ -10,9 +10,6 @@ class UserStateEmailSynchronizer extends UserStateSecondaryChannelSynchronizer { - private static final String EMAIL_KEY = "email"; - private static final String EMAIL_AUTH_HASH_KEY = "email_auth_hash"; - UserStateEmailSynchronizer() { super(UserStateSynchronizerType.EMAIL); } @@ -35,8 +32,8 @@ void logoutEmail() { getToSyncUserState().removeFromSyncValues(IDENTIFIER); List keysToRemove = new ArrayList<>(); keysToRemove.add(EMAIL_AUTH_HASH_KEY); - keysToRemove.add("device_player_id"); - keysToRemove.add("external_user_id"); + keysToRemove.add(DEVICE_PLAYER_ID); + keysToRemove.add(EXTERNAL_USER_ID); getToSyncUserState().removeFromSyncValues(keysToRemove); getToSyncUserState().persistState(); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java index 5fd8328bec..f05a93d355 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java @@ -49,13 +49,13 @@ void onSuccess(String responseStr) { try { JSONObject lastGetTagsResponse = new JSONObject(responseStr); - if (lastGetTagsResponse.has("tags")) { + if (lastGetTagsResponse.has(TAGS)) { synchronized(LOCK) { - JSONObject dependDiff = generateJsonDiff(getCurrentUserState().getSyncValues().optJSONObject("tags"), - getToSyncUserState().getSyncValues().optJSONObject("tags"), + JSONObject dependDiff = generateJsonDiff(getCurrentUserState().getSyncValues().optJSONObject(TAGS), + getToSyncUserState().getSyncValues().optJSONObject(TAGS), null, null); - getCurrentUserState().putOnSyncValues("tags", lastGetTagsResponse.optJSONObject("tags")); + getCurrentUserState().putOnSyncValues(TAGS, lastGetTagsResponse.optJSONObject(TAGS)); getCurrentUserState().persistState(); // Allow server side tags to overwrite local tags expect for any pending changes @@ -72,14 +72,14 @@ void onSuccess(String responseStr) { } synchronized(LOCK) { - return new GetTagsResult(serverSuccess, JSONUtils.getJSONObjectWithoutBlankValues(getToSyncUserState().getSyncValues(), "tags")); + return new GetTagsResult(serverSuccess, JSONUtils.getJSONObjectWithoutBlankValues(getToSyncUserState().getSyncValues(), TAGS)); } } @Override @Nullable String getExternalId(boolean fromServer) { synchronized(LOCK) { - return getToSyncUserState().getSyncValues().optString("external_user_id", null); + return getToSyncUserState().getSyncValues().optString(EXTERNAL_USER_ID, null); } } @@ -92,10 +92,10 @@ protected void scheduleSyncToServer() { void updateState(JSONObject pushState) { try { JSONObject syncUpdate = new JSONObject(); - syncUpdate.putOpt("identifier", pushState.optString("identifier", null)); - if (pushState.has("device_type")) - syncUpdate.put("device_type", pushState.optInt("device_type")); - syncUpdate.putOpt("parent_player_id", pushState.optString("parent_player_id", null)); + syncUpdate.putOpt(IDENTIFIER, pushState.optString(IDENTIFIER, null)); + if (pushState.has(DEVICE_TYPE)) + syncUpdate.put(DEVICE_TYPE, pushState.optInt(DEVICE_TYPE)); + syncUpdate.putOpt(PARENT_PLAYER_ID, pushState.optString(PARENT_PLAYER_ID, null)); UserState userState = getUserStateForModification(); userState.generateJsonDiffFromIntoSyncValued(syncUpdate, null); } catch(JSONException t) { @@ -104,10 +104,10 @@ void updateState(JSONObject pushState) { try { JSONObject dependUpdate = new JSONObject(); - if (pushState.has("subscribableStatus")) - dependUpdate.put("subscribableStatus", pushState.optInt("subscribableStatus")); - if (pushState.has("androidPermission")) - dependUpdate.put("androidPermission", pushState.optBoolean("androidPermission")); + if (pushState.has(SUBSCRIBABLE_STATUS)) + dependUpdate.put(SUBSCRIBABLE_STATUS, pushState.optInt(SUBSCRIBABLE_STATUS)); + if (pushState.has(ANDROID_PERMISSION)) + dependUpdate.put(ANDROID_PERMISSION, pushState.optBoolean(ANDROID_PERMISSION)); UserState userState = getUserStateForModification(); userState.generateJsonDiffFromIntoDependValues(dependUpdate, null); @@ -120,8 +120,8 @@ void setEmail(String email, String emailAuthHash) { try { UserState userState = getUserStateForModification(); - userState.putOnDependValues("email_auth_hash", emailAuthHash); - userState.generateJsonDiffFromIntoSyncValued(new JSONObject().put("email", email), null); + userState.putOnDependValues(EMAIL_AUTH_HASH_KEY, emailAuthHash); + userState.generateJsonDiffFromIntoSyncValued(new JSONObject().put(EMAIL_KEY, email), null); } catch (JSONException e) { e.printStackTrace(); @@ -132,8 +132,8 @@ void setSMSNumber(String smsNumber, String smsAuthHash) { try { UserState userState = getUserStateForModification(); - userState.putOnDependValues("sms_auth_hash", smsAuthHash); - userState.generateJsonDiffFromIntoSyncValued(new JSONObject().put("sms_number", smsNumber), null); + userState.putOnDependValues(SMS_AUTH_HASH_KEY, smsAuthHash); + userState.generateJsonDiffFromIntoSyncValued(new JSONObject().put(SMS_NUMBER_KEY, smsNumber), null); } catch (JSONException e) { e.printStackTrace(); } @@ -142,7 +142,7 @@ void setSMSNumber(String smsNumber, String smsAuthHash) { @Override void setSubscription(boolean enable) { try { - getUserStateForModification().putOnDependValues("userSubscribePref", enable); + getUserStateForModification().putOnDependValues(USER_SUBSCRIBE_PREF, enable); } catch (JSONException e) { e.printStackTrace(); } @@ -150,13 +150,13 @@ void setSubscription(boolean enable) { @Override public boolean getUserSubscribePreference() { - return getToSyncUserState().getDependValues().optBoolean("userSubscribePref", true); + return getToSyncUserState().getDependValues().optBoolean(USER_SUBSCRIBE_PREF, true); } @Override public void setPermission(boolean enable) { try { - getUserStateForModification().putOnDependValues("androidPermission", enable); + getUserStateForModification().putOnDependValues(ANDROID_PERMISSION, enable); } catch (JSONException e) { e.printStackTrace(); } @@ -178,7 +178,7 @@ protected void addOnSessionOrCreateExtras(JSONObject jsonBody) {} @Override void logoutEmail() { try { - getUserStateForModification().putOnDependValues("logoutEmail", true); + getUserStateForModification().putOnDependValues(LOGOUT_EMAIL, true); } catch (JSONException e) { e.printStackTrace(); } @@ -191,24 +191,24 @@ void logoutSMS() { @Override protected void fireEventsForUpdateFailure(JSONObject jsonFields) { - if (jsonFields.has("email")) + if (jsonFields.has(EMAIL_KEY)) OneSignal.fireEmailUpdateFailure(); - if (jsonFields.has("sms_number")) + if (jsonFields.has(SMS_NUMBER_KEY)) OneSignal.fireSMSUpdateFailure(); } @Override protected void onSuccessfulSync(JSONObject jsonFields) { - if (jsonFields.has("email")) + if (jsonFields.has(EMAIL_KEY)) OneSignal.fireEmailUpdateSuccess(); - if (jsonFields.has("sms_number")) { + if (jsonFields.has(SMS_NUMBER_KEY)) { JSONObject result = new JSONObject(); try { - result.put("sms_number", jsonFields.get("sms_number")); - if (jsonFields.has("sms_auth_hash")) - result.put("sms_auth_hash", jsonFields.get("sms_auth_hash")); + result.put(SMS_NUMBER_KEY, jsonFields.get(SMS_NUMBER_KEY)); + if (jsonFields.has(SMS_AUTH_HASH_KEY)) + result.put(SMS_AUTH_HASH_KEY, jsonFields.get(SMS_AUTH_HASH_KEY)); } catch (JSONException e) { e.printStackTrace(); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java index 91f894dc33..554a1a4d7d 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java @@ -8,9 +8,6 @@ public class UserStateSMSSynchronizer extends UserStateSecondaryChannelSynchronizer { - private static final String SMS_NUMBER_KEY = "sms_number"; - private static final String SMS_AUTH_HASH_KEY = "sms_auth_hash"; - UserStateSMSSynchronizer() { super(OneSignalStateSynchronizer.UserStateSynchronizerType.SMS); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java index da3d3e2785..7c9324e168 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java @@ -9,8 +9,6 @@ abstract class UserStateSecondaryChannelSynchronizer extends UserStateSynchronizer { - protected static final String IDENTIFIER = "identifier"; - UserStateSecondaryChannelSynchronizer(UserStateSynchronizerType channel) { super(channel); } @@ -97,8 +95,8 @@ protected void scheduleSyncToServer() { @Override protected void addOnSessionOrCreateExtras(JSONObject jsonBody) { try { - jsonBody.put("device_type", getDeviceType()); - jsonBody.putOpt("device_player_id", OneSignal.getUserId()); + jsonBody.put(DEVICE_TYPE, getDeviceType()); + jsonBody.putOpt(DEVICE_PLAYER_ID, OneSignal.getUserId()); } catch (JSONException e) { e.printStackTrace(); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java index 38bd7e4659..dfd7a973d7 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java @@ -22,6 +22,29 @@ abstract class UserStateSynchronizer { + private static final String CURRENT_STATE = "CURRENT_STATE"; + private static final String TOSYNC_STATE = "TOSYNC_STATE"; + private static final String SESSION = "session"; + private static final String APP_ID = "app_id"; + private static final String ID = "id"; + private static final String ERRORS = "errors"; + + protected static final String IDENTIFIER = "identifier"; + protected static final String DEVICE_TYPE = "device_type"; + protected static final String DEVICE_PLAYER_ID = "device_player_id"; + protected static final String PARENT_PLAYER_ID = "parent_player_id"; + protected static final String USER_SUBSCRIBE_PREF = "userSubscribePref"; + protected static final String ANDROID_PERMISSION = "androidPermission"; + protected static final String SUBSCRIBABLE_STATUS = "subscribableStatus"; + protected static final String TAGS = "tags"; + protected static final String EXTERNAL_USER_ID = "external_user_id"; + protected static final String EXTERNAL_USER_ID_AUTH_HASH = "external_user_id_auth_hash"; + protected static final String EMAIL_KEY = "email"; + protected static final String EMAIL_AUTH_HASH_KEY = "email_auth_hash"; + protected static final String LOGOUT_EMAIL = "logoutEmail"; + protected static final String SMS_NUMBER_KEY = "sms_number"; + protected static final String SMS_AUTH_HASH_KEY = "sms_auth_hash"; + // Object to synchronize on to prevent concurrent modifications on syncValues and dependValues protected final Object LOCK = new Object(); @@ -53,7 +76,7 @@ static class GetTagsResult { abstract boolean getSubscribed(); String getRegistrationId() { - return getToSyncUserState().getSyncValues().optString("identifier", null); + return getToSyncUserState().getSyncValues().optString(IDENTIFIER, null); } abstract GetTagsResult getTags(boolean fromServer); @@ -72,6 +95,7 @@ boolean hasQueuedHandlers() { } class NetworkHandlerThread extends HandlerThread { + private static final String THREAD_NAME_PREFIX = "OSH_NetworkHandlerThread_"; protected static final int NETWORK_HANDLER_USERSTATE = 0; int mType; @@ -82,7 +106,7 @@ class NetworkHandlerThread extends HandlerThread { int currentRetry; NetworkHandlerThread(int type) { - super("OSH_NetworkHandlerThread_" + UserStateSynchronizer.this.channel); + super(THREAD_NAME_PREFIX + UserStateSynchronizer.this.channel); mType = type; start(); mHandler = new Handler(getLooper()); @@ -156,7 +180,7 @@ protected UserState getCurrentUserState() { if (currentUserState == null) { synchronized (LOCK) { if (currentUserState == null) - currentUserState = newUserState("CURRENT_STATE", true); + currentUserState = newUserState(CURRENT_STATE, true); } } @@ -167,7 +191,7 @@ protected UserState getToSyncUserState() { if (toSyncUserState == null) { synchronized (LOCK) { if (toSyncUserState == null) - toSyncUserState = newUserState("TOSYNC_STATE", true); + toSyncUserState = newUserState(TOSYNC_STATE, true); } } @@ -178,7 +202,7 @@ void initUserState() { if (currentUserState == null) { synchronized (LOCK) { if (currentUserState == null) - currentUserState = newUserState("CURRENT_STATE", true); + currentUserState = newUserState(CURRENT_STATE, true); } } getToSyncUserState(); @@ -206,12 +230,12 @@ boolean persist() { protected abstract String getId(); private boolean isSessionCall() { - boolean toSyncSession = getToSyncUserState().getDependValues().optBoolean("session"); + boolean toSyncSession = getToSyncUserState().getDependValues().optBoolean(SESSION); return (toSyncSession || getId() == null) && !waitingForSessionResponse; } private boolean syncEmailLogout() { - return getToSyncUserState().getDependValues().optBoolean("logoutEmail", false); + return getToSyncUserState().getDependValues().optBoolean(LOGOUT_EMAIL, false); } void syncUserState(boolean fromSyncService) { @@ -259,14 +283,14 @@ private void doEmailLogout(String userId) { JSONObject jsonBody = new JSONObject(); try { ImmutableJSONObject dependValues = currentUserState.getDependValues(); - if (dependValues.has("email_auth_hash")) - jsonBody.put("email_auth_hash", dependValues.optString("email_auth_hash")); + if (dependValues.has(EMAIL_AUTH_HASH_KEY)) + jsonBody.put(EMAIL_AUTH_HASH_KEY, dependValues.optString(EMAIL_AUTH_HASH_KEY)); ImmutableJSONObject syncValues = currentUserState.getSyncValues(); - if (syncValues.has("parent_player_id")) - jsonBody.put("parent_player_id", syncValues.optString("parent_player_id")); + if (syncValues.has(PARENT_PLAYER_ID)) + jsonBody.put(PARENT_PLAYER_ID, syncValues.optString(PARENT_PLAYER_ID)); - jsonBody.put("app_id", syncValues.optString("app_id")); + jsonBody.put(APP_ID, syncValues.optString(APP_ID)); } catch (JSONException e) { e.printStackTrace(); } @@ -295,16 +319,16 @@ void onSuccess(String response) { } private void logoutEmailSyncSuccess() { - getToSyncUserState().removeFromDependValues("logoutEmail"); - toSyncUserState.removeFromDependValues("email_auth_hash"); - toSyncUserState.removeFromSyncValues("parent_player_id"); - toSyncUserState.removeFromSyncValues("email"); + getToSyncUserState().removeFromDependValues(LOGOUT_EMAIL); + toSyncUserState.removeFromDependValues(EMAIL_AUTH_HASH_KEY); + toSyncUserState.removeFromSyncValues(PARENT_PLAYER_ID); + toSyncUserState.removeFromSyncValues(EMAIL_KEY); toSyncUserState.persistState(); - currentUserState.removeFromDependValues("email_auth_hash"); - currentUserState.removeFromSyncValues("parent_player_id"); - String emailLoggedOut = currentUserState.getSyncValues().optString("email"); - currentUserState.removeFromSyncValues("email"); + currentUserState.removeFromDependValues(EMAIL_AUTH_HASH_KEY); + currentUserState.removeFromSyncValues(PARENT_PLAYER_ID); + String emailLoggedOut = currentUserState.getSyncValues().optString(EMAIL_KEY); + currentUserState.removeFromSyncValues(EMAIL_KEY); OneSignalStateSynchronizer.setNewSessionForEmail(); @@ -332,10 +356,10 @@ void onFailure(int statusCode, String response, Throwable throwable) { handleNetworkFailure(statusCode); } - if (jsonBody.has("tags")) + if (jsonBody.has(TAGS)) sendTagsHandlersPerformOnFailure(new SendTagsError(statusCode, response)); - if (jsonBody.has("external_user_id")) { + if (jsonBody.has(EXTERNAL_USER_ID)) { OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Error setting external user id for push with status code: " + statusCode + " and message: " + response); externalUserIdUpdateHandlersPerformOnFailure(); } @@ -348,10 +372,10 @@ void onSuccess(String response) { onSuccessfulSync(jsonBody); } - if (jsonBody.has("tags")) + if (jsonBody.has(TAGS)) sendTagsHandlersPerformOnSuccess(); - if (jsonBody.has("external_user_id")) + if (jsonBody.has(EXTERNAL_USER_ID)) externalUserIdUpdateHandlersPerformOnSuccess(); } }); @@ -390,15 +414,15 @@ void onSuccess(String response) { OneSignal.onesignalLog(OneSignal.LOG_LEVEL.DEBUG, "doCreateOrNewSession:response: " + response); JSONObject jsonResponse = new JSONObject(response); - if (jsonResponse.has("id")) { - String newUserId = jsonResponse.optString("id"); + if (jsonResponse.has(ID)) { + String newUserId = jsonResponse.optString(ID); updateIdDependents(newUserId); OneSignal.Log(OneSignal.LOG_LEVEL.INFO, "Device registered, UserId = " + newUserId); } else OneSignal.Log(OneSignal.LOG_LEVEL.INFO, "session sent, UserId = " + userId); - getUserStateForModification().putOnDependValues("session", false); + getUserStateForModification().putOnDependValues(SESSION, false); getUserStateForModification().persistState(); // List of in app messages to evaluate for the session @@ -434,7 +458,7 @@ private void fireNetworkFailureEvents() { if (jsonBody != null) fireEventsForUpdateFailure(jsonBody); - if (getToSyncUserState().getDependValues().optBoolean("logoutEmail", false)) + if (getToSyncUserState().getDependValues().optBoolean(LOGOUT_EMAIL, false)) OneSignal.handleFailedEmailLogout(); } @@ -446,7 +470,7 @@ private boolean response400WithErrorsContaining(int statusCode, String response, if (statusCode == 400 && response != null) { try { JSONObject responseJson = new JSONObject(response); - return responseJson.has("errors") && responseJson.optString("errors").contains(contains); + return responseJson.has(ERRORS) && responseJson.optString(ERRORS).contains(contains); } catch (JSONException e) { e.printStackTrace(); } @@ -468,7 +492,7 @@ protected NetworkHandlerThread getNetworkHandlerThread(Integer type) { // If there are differences a network call with the changes to made protected UserState getUserStateForModification() { if (toSyncUserState == null) - toSyncUserState = getCurrentUserState().deepClone("TOSYNC_STATE"); + toSyncUserState = getCurrentUserState().deepClone(TOSYNC_STATE); scheduleSyncToServer(); @@ -486,7 +510,7 @@ void updateDeviceInfo(JSONObject deviceInfo) { void setNewSession() { try { synchronized (LOCK) { - getUserStateForModification().putOnDependValues("session", true); + getUserStateForModification().putOnDependValues(SESSION, true); getUserStateForModification().persistState(); } } catch (JSONException e) { @@ -495,7 +519,7 @@ void setNewSession() { } boolean getSyncAsNewSession() { - return getUserStateForModification().getDependValues().optBoolean("session"); + return getUserStateForModification().getDependValues().optBoolean(SESSION); } void sendTags(JSONObject tags, @Nullable ChangeTagsUpdateHandler handler) { @@ -514,9 +538,9 @@ void setExternalUserId(final String externalId, final String externalIdAuthHash, this.externalUserIdUpdateHandlers.add(handler); UserState userState = getUserStateForModification(); - userState.putOnSyncValues("external_user_id", externalId); + userState.putOnSyncValues(EXTERNAL_USER_ID, externalId); if (externalIdAuthHash != null) - userState.putOnSyncValues("external_user_id_auth_hash", externalIdAuthHash); + userState.putOnSyncValues(EXTERNAL_USER_ID_AUTH_HASH, externalIdAuthHash); } abstract void setSubscription(boolean enable); From d657d6cb574a3f62ee6be4d3877f3a7967be03b4 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 1 Feb 2021 16:14:47 -0300 Subject: [PATCH 11/23] Generalize setEmail and setSMSNumber * Create setChannelId under UserStateSecondaryChannelSynchronizer --- .../onesignal/OneSignalStateSynchronizer.java | 4 +- .../onesignal/UserStateEmailSynchronizer.java | 40 ---------------- .../onesignal/UserStateSMSSynchronizer.java | 45 ------------------ ...UserStateSecondaryChannelSynchronizer.java | 46 +++++++++++++++++++ .../SynchronizerIntegrationTests.java | 2 +- 5 files changed, 49 insertions(+), 88 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java index 83bdf29377..5e94d2a322 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java @@ -136,13 +136,13 @@ static void sendTags(JSONObject newTags, @Nullable ChangeTagsUpdateHandler handl static void setSMSNumber(String smsNumber, String smsAuthHash) { getPushStateSynchronizer().setSMSNumber(smsNumber, smsAuthHash); - getSMSStateSynchronizer().setSMSNumber(smsNumber, smsAuthHash); + getSMSStateSynchronizer().setChannelId(smsNumber, smsAuthHash); // Should SMS be linked to email? } static void setEmail(String email, String emailAuthHash) { getPushStateSynchronizer().setEmail(email, emailAuthHash); - getEmailStateSynchronizer().setEmail(email, emailAuthHash); + getEmailStateSynchronizer().setChannelId(email, emailAuthHash); // Should email be linked to SMS? } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java index 43bdbee7af..9c015dab35 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java @@ -2,7 +2,6 @@ import com.onesignal.OneSignalStateSynchronizer.UserStateSynchronizerType; -import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; @@ -75,43 +74,4 @@ void updateIdDependents(String id) { OneSignal.updateEmailIdDependents(id); } - void setEmail(String email, String emailAuthHash) { - UserState userState = getUserStateForModification(); - ImmutableJSONObject syncValues = userState.getSyncValues(); - - boolean noChange = email.equals(syncValues.optString(IDENTIFIER)) && - syncValues.optString(getAuthHashKey()).equals(emailAuthHash == null ? "" : emailAuthHash); - if (noChange) { - fireUpdateSuccess(null); - return; - } - - String existingEmail = syncValues.optString(IDENTIFIER, null); - - if (existingEmail == null) - setNewSession(); - - try { - JSONObject emailJSON = new JSONObject(); - emailJSON.put(IDENTIFIER, email); - - if (emailAuthHash != null) - emailJSON.put(getAuthHashKey(), emailAuthHash); - - if (emailAuthHash == null) { - if (existingEmail != null && !existingEmail.equals(email)) { - OneSignal.saveEmailId(""); - resetCurrentState(); - setNewSession(); - } - } - - userState.generateJsonDiffFromIntoSyncValued(emailJSON, null); - scheduleSyncToServer(); - } - catch (JSONException e) { - e.printStackTrace(); - } - } - } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java index 554a1a4d7d..5f1c7632a9 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java @@ -1,6 +1,5 @@ package com.onesignal; -import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; @@ -72,48 +71,4 @@ void updateIdDependents(String id) { OneSignal.updateSMSIdDependents(id); } - void setSMSNumber(String smsNumber, String smsAuthHash) { - UserState userState = getUserStateForModification(); - ImmutableJSONObject syncValues = userState.getSyncValues(); - - boolean noChange = smsNumber.equals(syncValues.optString(IDENTIFIER)) && - syncValues.optString(getAuthHashKey()).equals(smsAuthHash == null ? "" : smsAuthHash); - if (noChange) { - JSONObject result = new JSONObject(); - try { - result.put(IDENTIFIER, smsNumber); - result.put(getAuthHashKey(), smsAuthHash); - } catch (JSONException e) { - e.printStackTrace(); - } - fireUpdateSuccess(result); - return; - } - - String existingsms = syncValues.optString(IDENTIFIER, null); - - if (existingsms == null) - setNewSession(); - - try { - JSONObject smsJSON = new JSONObject(); - smsJSON.put(IDENTIFIER, smsNumber); - - if (smsAuthHash != null) - smsJSON.put(getAuthHashKey(), smsAuthHash); - - if (smsAuthHash == null) { - if (existingsms != null && !existingsms.equals(smsNumber)) { - OneSignal.saveSMSId(""); - resetCurrentState(); - setNewSession(); - } - } - - userState.generateJsonDiffFromIntoSyncValued(smsJSON, null); - scheduleSyncToServer(); - } catch (JSONException e) { - e.printStackTrace(); - } - } } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java index 7c9324e168..3e401dacd4 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java @@ -122,4 +122,50 @@ protected void onSuccessfulSync(JSONObject jsonFields) { fireUpdateSuccess(result); } } + + void setChannelId(String id, String idAuthHash) { + UserState userState = getUserStateForModification(); + ImmutableJSONObject syncValues = userState.getSyncValues(); + + boolean noChange = id.equals(syncValues.optString(IDENTIFIER)) && + syncValues.optString(getAuthHashKey()).equals(idAuthHash == null ? "" : idAuthHash); + if (noChange) { + JSONObject result = new JSONObject(); + try { + result.put(getChannelKey(), id); + result.put(getAuthHashKey(), idAuthHash); + } catch (JSONException e) { + e.printStackTrace(); + } + fireUpdateSuccess(result); + return; + } + + String existingEmail = syncValues.optString(IDENTIFIER, null); + + if (existingEmail == null) + setNewSession(); + + try { + JSONObject emailJSON = new JSONObject(); + emailJSON.put(IDENTIFIER, id); + + if (idAuthHash != null) + emailJSON.put(getAuthHashKey(), idAuthHash); + + if (idAuthHash == null) { + if (existingEmail != null && !existingEmail.equals(id)) { + OneSignal.saveEmailId(""); + resetCurrentState(); + setNewSession(); + } + } + + userState.generateJsonDiffFromIntoSyncValued(emailJSON, null); + scheduleSyncToServer(); + } + catch (JSONException e) { + e.printStackTrace(); + } + } } diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java index 25dfd57720..e201c73f6b 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java @@ -565,7 +565,7 @@ public void shouldFireOnSuccessOfSMSEvenWhenNoChanges() throws Exception { assertNotNull(testSMSUpdateHandler.smsResult); assertEquals(1, testSMSUpdateHandler.smsResult.length()); - assertEquals(ONESIGNAL_SMS_NUMBER, testSMSUpdateHandler.smsResult.get("identifier")); + assertEquals(ONESIGNAL_SMS_NUMBER, testSMSUpdateHandler.smsResult.get("sms_number")); assertNull(testSMSUpdateHandler.smsFiredFailure); } From 00c3e6e5964226aa291c9f3da7c6ff48e0340f8d Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 1 Feb 2021 16:18:06 -0300 Subject: [PATCH 12/23] Add sendsOnFocusToEmailAndSMS test * Add test for onfocus with all channels available --- .../SynchronizerIntegrationTests.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java index e201c73f6b..a68ea94452 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java @@ -42,6 +42,8 @@ import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setSessionManager; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTime; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTrackerFactory; +import static com.onesignal.ShadowOneSignalRestClient.EMAIL_USER_ID; +import static com.onesignal.ShadowOneSignalRestClient.PUSH_USER_ID; import static com.onesignal.ShadowOneSignalRestClient.SMS_USER_ID; import static com.onesignal.ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse; import static com.test.onesignal.RestClientAsserts.assertOnFocusAtIndex; @@ -1410,6 +1412,38 @@ public void sendsOnFocusToSMS() throws Exception { assertEquals(60, postSMSl.payload.getInt("active_time")); } + @Test + public void sendsOnFocusToEmailAndSMS() throws Exception { + time.advanceSystemAndElapsedTimeBy(0); + OneSignalInit(); + OneSignal.setEmail(ONESIGNAL_EMAIL_ADDRESS); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + blankActivityController.resume(); + threadAndTaskWait(); + time.advanceSystemAndElapsedTimeBy(60); + pauseActivity(blankActivityController); + assertAndRunSyncService(); + + assertEquals(8, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request post = ShadowOneSignalRestClient.requests.get(4); + assertFalse(post.url.contains("on_focus")); + + ShadowOneSignalRestClient.Request postPush = ShadowOneSignalRestClient.requests.get(5); + assertEquals("players/" + PUSH_USER_ID + "/on_focus", postPush.url); + assertEquals(60, postPush.payload.getInt("active_time")); + + ShadowOneSignalRestClient.Request postEmail = ShadowOneSignalRestClient.requests.get(6); + assertEquals("players/" + EMAIL_USER_ID + "/on_focus", postEmail.url); + assertEquals(60, postEmail.payload.getInt("active_time")); + + ShadowOneSignalRestClient.Request postSMSl = ShadowOneSignalRestClient.requests.get(7); + assertEquals("players/" + SMS_USER_ID + "/on_focus", postSMSl.url); + assertEquals(60, postSMSl.payload.getInt("active_time")); + } + private void OneSignalInit() { OneSignal.setLogLevel(OneSignal.LOG_LEVEL.VERBOSE, OneSignal.LOG_LEVEL.NONE); ShadowOSUtils.subscribableStatus = 1; From fc7c090228d8b970f288e3f29ff99e6f8ce49d92 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 1 Feb 2021 16:38:09 -0300 Subject: [PATCH 13/23] Add shouldDoPostOnSMSChange test * Fix channel id save method --- .../onesignal/OneSignalStateSynchronizer.java | 8 +++---- .../onesignal/UserStateEmailSynchronizer.java | 5 +++++ .../onesignal/UserStatePushSynchronizer.java | 5 +++++ .../onesignal/UserStateSMSSynchronizer.java | 15 ++++++++----- ...UserStateSecondaryChannelSynchronizer.java | 2 +- .../com/onesignal/UserStateSynchronizer.java | 1 + .../SynchronizerIntegrationTests.java | 22 +++++++++++++++++++ 7 files changed, 47 insertions(+), 11 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java index 5e94d2a322..49627cee32 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java @@ -137,13 +137,11 @@ static void sendTags(JSONObject newTags, @Nullable ChangeTagsUpdateHandler handl static void setSMSNumber(String smsNumber, String smsAuthHash) { getPushStateSynchronizer().setSMSNumber(smsNumber, smsAuthHash); getSMSStateSynchronizer().setChannelId(smsNumber, smsAuthHash); - // Should SMS be linked to email? } static void setEmail(String email, String emailAuthHash) { getPushStateSynchronizer().setEmail(email, emailAuthHash); getEmailStateSynchronizer().setChannelId(email, emailAuthHash); - // Should email be linked to SMS? } static void setSubscription(boolean enable) { @@ -181,9 +179,9 @@ static void resetCurrentState() { getEmailStateSynchronizer().resetCurrentState(); getSMSStateSynchronizer().resetCurrentState(); - OneSignal.saveUserId(null); - OneSignal.saveEmailId(null); - OneSignal.saveSMSId(null); + getPushStateSynchronizer().saveChannelId(null); + getEmailStateSynchronizer().saveChannelId(null); + getSMSStateSynchronizer().saveChannelId(null); OneSignal.setLastSessionTime(-60 * 61); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java index 9c015dab35..cee7643678 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java @@ -23,6 +23,11 @@ protected String getId() { return OneSignal.getEmailId(); } + @Override + void saveChannelId(String id) { + OneSignal.saveEmailId(id); + } + @Override void logoutEmail() { OneSignal.saveEmailId(""); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java index f05a93d355..3de6131004 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java @@ -23,6 +23,11 @@ protected OneSignal.LOG_LEVEL getLogLevel() { return OneSignal.LOG_LEVEL.ERROR; } + @Override + void saveChannelId(String id) { + OneSignal.saveUserId(id); + } + @Override boolean getSubscribed() { return getToSyncUserState().isSubscribed(); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java index 5f1c7632a9..8c0b942ba6 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java @@ -21,20 +21,25 @@ protected String getId() { return OneSignal.getSMSId(); } + @Override + void saveChannelId(String id) { + OneSignal.saveSMSId(id); + } + @Override void logoutEmail() { } @Override void logoutSMS() { - OneSignal.saveSMSId(""); + saveChannelId(""); resetCurrentState(); - getToSyncUserState().removeFromSyncValues("identifier"); + getToSyncUserState().removeFromSyncValues(IDENTIFIER); List keysToRemove = new ArrayList<>(); - keysToRemove.add("sms_auth_hash"); - keysToRemove.add("device_player_id"); - keysToRemove.add("external_user_id"); + keysToRemove.add(SMS_AUTH_HASH_KEY); + keysToRemove.add(DEVICE_PLAYER_ID); + keysToRemove.add(EXTERNAL_USER_ID); getToSyncUserState().removeFromSyncValues(keysToRemove); getToSyncUserState().persistState(); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java index 3e401dacd4..855eb6cc07 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java @@ -155,7 +155,7 @@ void setChannelId(String id, String idAuthHash) { if (idAuthHash == null) { if (existingEmail != null && !existingEmail.equals(id)) { - OneSignal.saveEmailId(""); + saveChannelId(""); resetCurrentState(); setNewSession(); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java index dfd7a973d7..85d86ce765 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java @@ -73,6 +73,7 @@ static class GetTagsResult { } } + abstract void saveChannelId(String id); abstract boolean getSubscribed(); String getRegistrationId() { diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java index a68ea94452..9305b5c012 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java @@ -649,6 +649,28 @@ public void shouldDoPostOnEmailChange() throws Exception { assertEquals(newMockEmailPlayerId, playerPut.payload.get("parent_player_id")); } + // Should create a new sms instead of updating existing player record when no auth hash + @Test + public void shouldDoPostOnSMSChange() throws Exception { + OneSignalInit(); + + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + String newMockSMSPlayerId = "z007f967-98cc-11e4-bed1-118f05be4533"; + ShadowOneSignalRestClient.smsUserId = newMockSMSPlayerId; + String newSMS = "different_sms_number"; + OneSignal.setSMSNumber(newSMS); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(5); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); + assertEquals(newSMS, smsPost.payload.get("identifier")); + + ShadowOneSignalRestClient.Request playerPut = ShadowOneSignalRestClient.requests.get(6); + assertEquals(newMockSMSPlayerId, playerPut.payload.get("parent_player_id")); + } + // Should update player with new email instead of creating a new one when auth hash is provided @Test public void shouldUpdateEmailWhenAuthHashIsUsed() throws Exception { From 8b89d93d4c51cf57ef257d513275dc72523c57b9 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 1 Feb 2021 16:56:02 -0300 Subject: [PATCH 14/23] Add shouldUpdateSMSlWhenAuthHashIsUsed test --- .../SynchronizerIntegrationTests.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java index 9305b5c012..0890bf4686 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java @@ -694,6 +694,29 @@ public void shouldUpdateEmailWhenAuthHashIsUsed() throws Exception { assertEquals("different@email.com", emailPut.payload.get("identifier")); } + // Should update player with new sms instead of creating a new one when auth hash is provided + @Test + public void shouldUpdateSMSlWhenAuthHashIsUsed() throws Exception { + OneSignalInit(); + String mockEmailHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockEmailHash); + threadAndTaskWait(); + String newSMS = "different_sms_number"; + OneSignal.setSMSNumber(newSMS, mockEmailHash); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request pushPut = ShadowOneSignalRestClient.requests.get(4); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, pushPut.method); + assertEquals("players/" + PUSH_USER_ID, pushPut.url); + assertEquals(newSMS, pushPut.payload.get("sms_number")); + + ShadowOneSignalRestClient.Request smsPut = ShadowOneSignalRestClient.requests.get(5); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, smsPut.method); + assertEquals("players/" + SMS_USER_ID, smsPut.url); + assertEquals(newSMS, smsPut.payload.get("identifier")); + } + @Test public void shouldSendEmailAuthHashWithLogout() throws Exception { OneSignalInit(); From cdde9bf632b8868bd18738ab2515f6c6be634e64 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 1 Feb 2021 17:33:21 -0300 Subject: [PATCH 15/23] Add external id tests for SMS --- .../onesignal/OneSignalStateSynchronizer.java | 27 +- .../main/java/com/onesignal/UserState.java | 19 +- .../com/onesignal/UserStateSynchronizer.java | 9 +- .../SynchronizerIntegrationTests.java | 389 ++++++++++++++++-- 4 files changed, 399 insertions(+), 45 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java index 49627cee32..4a7bc09ba7 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java @@ -34,7 +34,9 @@ import org.json.JSONException; import org.json.JSONObject; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; class OneSignalStateSynchronizer { @@ -88,6 +90,22 @@ static UserStateSMSSynchronizer getSMSStateSynchronizer() { return (UserStateSMSSynchronizer) userStateSynchronizers.get(UserStateSynchronizerType.SMS); } + + static List getUserStateSynchronizers() { + List userStateSynchronizers = new ArrayList<>(); + + userStateSynchronizers.add(getPushStateSynchronizer()); + + // Make sure we are only setting external user id for email when an email is actually set + if (OneSignal.hasEmailId()) + userStateSynchronizers.add(getEmailStateSynchronizer()); + + // Make sure we are only setting external user id for sms when an sms is actually set + if (OneSignal.hasSMSlId()) + userStateSynchronizers.add(getSMSStateSynchronizer()); + + return userStateSynchronizers; + } static boolean persist() { boolean pushPersisted = getPushStateSynchronizer().persist(); @@ -257,11 +275,10 @@ public void run() { } }; - getPushStateSynchronizer().setExternalUserId(externalId, externalIdAuthHash, handler); - - // Make sure we are only setting external user id for email when an email is actually set - if (OneSignal.hasEmailId()) - getEmailStateSynchronizer().setExternalUserId(externalId, externalIdAuthHash, handler); + List userStateSynchronizers = getUserStateSynchronizers(); + for (UserStateSynchronizer userStateSynchronizer : userStateSynchronizers) { + userStateSynchronizer.setExternalUserId(externalId, externalIdAuthHash, handler); + } } // This is to indicate that StateSynchronizer can start making REST API calls diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserState.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserState.java index 4665004be4..efb2bae3f3 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserState.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserState.java @@ -11,6 +11,11 @@ import java.util.Map; import java.util.Set; +import static com.onesignal.UserStateSynchronizer.APP_ID; +import static com.onesignal.UserStateSynchronizer.EMAIL_AUTH_HASH_KEY; +import static com.onesignal.UserStateSynchronizer.EXTERNAL_USER_ID_AUTH_HASH; +import static com.onesignal.UserStateSynchronizer.SMS_AUTH_HASH_KEY; + abstract class UserState { // Object to synchronize on to prevent concurrent modifications on syncValues and dependValues @@ -238,12 +243,14 @@ JSONObject generateJsonDiff(UserState newState, boolean isSessionCall) { try { // app_id required for all REST API calls - if (!sendJson.has("app_id")) - sendJson.put("app_id", syncValues.optString("app_id")); - if (syncValues.has("email_auth_hash")) - sendJson.put("email_auth_hash", syncValues.optString("email_auth_hash")); - if (syncValues.has("external_user_id_auth_hash")) - sendJson.put("external_user_id_auth_hash", syncValues.optString("external_user_id_auth_hash")); + if (!sendJson.has(APP_ID)) + sendJson.put(APP_ID, syncValues.optString(APP_ID)); + if (syncValues.has(EMAIL_AUTH_HASH_KEY)) + sendJson.put(EMAIL_AUTH_HASH_KEY, syncValues.optString(EMAIL_AUTH_HASH_KEY)); + if (syncValues.has(SMS_AUTH_HASH_KEY)) + sendJson.put(SMS_AUTH_HASH_KEY, syncValues.optString(SMS_AUTH_HASH_KEY)); + if (syncValues.has(EXTERNAL_USER_ID_AUTH_HASH)) + sendJson.put(EXTERNAL_USER_ID_AUTH_HASH, syncValues.optString(EXTERNAL_USER_ID_AUTH_HASH)); } catch (JSONException e) { e.printStackTrace(); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java index 85d86ce765..64af6b5bca 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java @@ -25,7 +25,6 @@ abstract class UserStateSynchronizer { private static final String CURRENT_STATE = "CURRENT_STATE"; private static final String TOSYNC_STATE = "TOSYNC_STATE"; private static final String SESSION = "session"; - private static final String APP_ID = "app_id"; private static final String ID = "id"; private static final String ERRORS = "errors"; @@ -38,12 +37,14 @@ abstract class UserStateSynchronizer { protected static final String SUBSCRIBABLE_STATUS = "subscribableStatus"; protected static final String TAGS = "tags"; protected static final String EXTERNAL_USER_ID = "external_user_id"; - protected static final String EXTERNAL_USER_ID_AUTH_HASH = "external_user_id_auth_hash"; protected static final String EMAIL_KEY = "email"; - protected static final String EMAIL_AUTH_HASH_KEY = "email_auth_hash"; protected static final String LOGOUT_EMAIL = "logoutEmail"; protected static final String SMS_NUMBER_KEY = "sms_number"; - protected static final String SMS_AUTH_HASH_KEY = "sms_auth_hash"; + + static final String EXTERNAL_USER_ID_AUTH_HASH = "external_user_id_auth_hash"; + static final String EMAIL_AUTH_HASH_KEY = "email_auth_hash"; + static final String SMS_AUTH_HASH_KEY = "sms_auth_hash"; + static final String APP_ID = "app_id"; // Object to synchronize on to prevent concurrent modifications on syncValues and dependValues protected final Object LOCK = new Object(); diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java index 0890bf4686..4a73adf77f 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java @@ -117,6 +117,22 @@ public void onFailure(OneSignal.EmailUpdateError error) { }; } + private static JSONObject didSMSlUpdateSucceed; + private static OneSignal.OSSMSUpdateError lastSMSUpdateFailure; + private static OneSignal.OSSMSUpdateHandler getSMSUpdateHandler() { + return new OneSignal.OSSMSUpdateHandler() { + @Override + public void onSuccess(JSONObject result) { + didSMSlUpdateSucceed = result; + } + + @Override + public void onFailure(OneSignal.OSSMSUpdateError error) { + lastSMSUpdateFailure = error; + } + }; + } + private static void cleanUp() throws Exception { lastExternalUserIdResponse = null; lastExternalUserIdError = null; @@ -833,35 +849,7 @@ public void shouldCreateNewEmailAfterLogout() throws Exception { assertEquals(newMockEmailPlayerId, playerPut2.payload.get("parent_player_id")); } - @Test - public void shouldSendOnSessionToEmail() throws Exception { - OneSignalInit(); - OneSignal.setEmail("josh@onesignal.com"); - threadAndTaskWait(); - - restartAppAndElapseTimeToNextSession(time); - OneSignalInit(); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(6); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); - assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522/on_session", emailPost.url); - } - - @Test - public void shouldSendOnSessionToSMS() throws Exception { - OneSignalInit(); - OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); - threadAndTaskWait(); - - restartAppAndElapseTimeToNextSession(time); - OneSignalInit(); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(6); - assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); - assertEquals("players/" + ShadowOneSignalRestClient.SMS_USER_ID + "/on_session", smsPost.url); - } + // ####### external_id Tests ######## @Test public void shouldSendExternalUserIdAfterRegistration() throws Exception { @@ -951,7 +939,7 @@ public void shouldSetExternalIdWithAuthHashBeforeRegistration() throws Exception } @Test - public void shouldAlwaysSetExternalIdWithAuthHashAAfterRegistration() throws Exception { + public void shouldAlwaysSetExternalIdWithAuthHashAfterRegistration() throws Exception { OneSignalInit(); threadAndTaskWait(); @@ -1019,6 +1007,47 @@ public void shouldAlwaysSetExternalIdAndEmailWithAuthHashAfterRegistration() thr assertEquals(mockEmailHash, emailPostAfterColdStart.payload.getString("email_auth_hash")); } + @Test + public void shouldAlwaysSetExternalIdAndSMSWithAuthHashAfterRegistration() throws Exception { + OneSignalInit(); + threadAndTaskWait(); + + String testExternalId = "test_ext_id"; + String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); + + String mockSMSlHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setExternalUserId(testExternalId, mockExternalIdHash); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockSMSlHash); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request registrationRequest = ShadowOneSignalRestClient.requests.get(2); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, registrationRequest.method); + assertEquals(testExternalId, registrationRequest.payload.getString("external_user_id")); + assertEquals(mockExternalIdHash, registrationRequest.payload.getString("external_user_id_auth_hash")); + + ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(3); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); + assertEquals(ONESIGNAL_SMS_NUMBER, smsPost.payload.getString("identifier")); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_SMS, smsPost.payload.getInt("device_type")); + assertEquals(mockSMSlHash, smsPost.payload.getString("sms_auth_hash")); + + fastColdRestartApp(); + + time.advanceSystemTimeBy(60); + OneSignalInit(); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request registrationRequestAfterColdStart = ShadowOneSignalRestClient.requests.get(6); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, registrationRequestAfterColdStart.method); + assertEquals(mockExternalIdHash, registrationRequestAfterColdStart.payload.getString("external_user_id_auth_hash")); + + ShadowOneSignalRestClient.Request smsPostAfterColdStart = ShadowOneSignalRestClient.requests.get(7); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPostAfterColdStart.method); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_SMS, smsPostAfterColdStart.payload.getInt("device_type")); + assertEquals(mockSMSlHash, smsPostAfterColdStart.payload.getString("sms_auth_hash")); + } + @Test public void shouldRemoveExternalUserId() throws Exception { OneSignal.setExternalUserId("test_ext_id"); @@ -1104,6 +1133,44 @@ public void shouldRemoveExternalUserIdFromEmailWithAuthHash() throws Exception { assertEquals(mockEmailHash, removeIdEmailRequest.payload.getString("email_auth_hash")); } + @Test + public void shouldRemoveExternalUserIdFromSMSWithAuthHash() throws Exception { + String mockSMSHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockSMSHash, getSMSUpdateHandler()); + OneSignalInit(); + threadAndTaskWait(); + + OneSignal.removeExternalUserId(getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + JSONObject expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : true" + + " }" + ", " + + " \"sms\" : {" + + " \"success\" : true" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + assertNotNull(didSMSlUpdateSucceed); + assertNull(lastEmailUpdateFailure); + + assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(4); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method); + assertEquals(removeIdRequest.payload.getString("external_user_id"), ""); + assertFalse(removeIdRequest.payload.has("external_user_id_auth_hash")); + + ShadowOneSignalRestClient.Request removeIdSMSRequest = ShadowOneSignalRestClient.requests.get(5); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdSMSRequest.method); + assertEquals(removeIdSMSRequest.payload.getString("external_user_id"), ""); + assertEquals(mockSMSHash, removeIdSMSRequest.payload.getString("sms_auth_hash")); + } + @Test public void shouldRemoveExternalUserIdFromPushAndEmailWithAuthHash() throws Exception { String testExternalId = "test_ext_id"; @@ -1132,6 +1199,67 @@ public void shouldRemoveExternalUserIdFromPushAndEmailWithAuthHash() throws Exce assertEquals(mockEmailHash, removeIdEmailRequest.payload.getString("email_auth_hash")); } + @Test + public void shouldRemoveExternalUserIdFromPushAndSMSWithAuthHash() throws Exception { + String testExternalId = "test_ext_id"; + String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); + String mockSMSHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockSMSHash, null); + OneSignalInit(); + threadAndTaskWait(); + + OneSignal.removeExternalUserId(); + threadAndTaskWait(); + + assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(4); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method); + assertEquals(removeIdRequest.payload.getString("external_user_id"), ""); + assertEquals(mockExternalIdHash, removeIdRequest.payload.getString("external_user_id_auth_hash")); + + ShadowOneSignalRestClient.Request removeIdSMSRequest = ShadowOneSignalRestClient.requests.get(5); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdSMSRequest.method); + assertEquals(removeIdSMSRequest.payload.getString("external_user_id"), ""); + assertEquals(mockSMSHash, removeIdSMSRequest.payload.getString("sms_auth_hash")); + } + + @Test + public void shouldRemoveExternalUserIdFromPushAndEmailAndSMSWithAuthHash() throws Exception { + String testExternalId = "test_ext_id"; + String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); + String mockEmailHash = new String(new char[64]).replace('\0', '0'); + String mockSMSHash = new String(new char[64]).replace('\0', '1'); + + OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null); + OneSignal.setEmail(ONESIGNAL_EMAIL_ADDRESS, mockEmailHash, null); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockSMSHash, null); + OneSignalInit(); + threadAndTaskWait(); + + OneSignal.removeExternalUserId(); + threadAndTaskWait(); + + assertEquals(8, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(5); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method); + assertEquals(removeIdRequest.payload.getString("external_user_id"), ""); + assertEquals(mockExternalIdHash, removeIdRequest.payload.getString("external_user_id_auth_hash")); + + ShadowOneSignalRestClient.Request removeIdEmailRequest = ShadowOneSignalRestClient.requests.get(6); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdEmailRequest.method); + assertEquals(removeIdEmailRequest.payload.getString("external_user_id"), ""); + assertEquals(mockEmailHash, removeIdEmailRequest.payload.getString("email_auth_hash")); + + ShadowOneSignalRestClient.Request removeIdSMSRequest = ShadowOneSignalRestClient.requests.get(7); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdSMSRequest.method); + assertEquals(removeIdSMSRequest.payload.getString("external_user_id"), ""); + assertEquals(mockSMSHash, removeIdSMSRequest.payload.getString("sms_auth_hash")); + } + @Test public void doesNotSendSameExternalId() throws Exception { String testExternalId = "test_ext_id"; @@ -1182,6 +1310,72 @@ public void sendsExternalIdOnEmailPlayers() throws Exception { assertEquals(externalIdRequests, 2); } + @Test + public void sendsExternalIdOnSMSPlayers() throws Exception { + String testExternalId = "test_ext_id"; + + OneSignalInit(); + threadAndTaskWait(); + + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + int currentRequestCount = ShadowOneSignalRestClient.networkCallCount; + + OneSignal.setExternalUserId(testExternalId); + threadAndTaskWait(); + + // the SDK should have made two additional API calls + // One to set extID on the push player record, + // and another for the sms player record + assertEquals(ShadowOneSignalRestClient.networkCallCount, currentRequestCount + 2); + + int externalIdRequests = 0; + + for (ShadowOneSignalRestClient.Request request : ShadowOneSignalRestClient.requests) { + if (request.payload != null && request.payload.has("external_user_id")) { + externalIdRequests += 1; + assertEquals(request.payload.getString("external_user_id"), testExternalId); + } + } + + assertEquals(externalIdRequests, 2); + } + + @Test + public void sendsExternalIdOnEmailAndSMSPlayers() throws Exception { + String testExternalId = "test_ext_id"; + + OneSignalInit(); + threadAndTaskWait(); + + OneSignal.setEmail(ONESIGNAL_EMAIL_ADDRESS); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + int currentRequestCount = ShadowOneSignalRestClient.networkCallCount; + + OneSignal.setExternalUserId(testExternalId); + threadAndTaskWait(); + + // the SDK should have made two additional API calls + // One to set extID on the push player record, + // another for the sms player record + // and another for email sms player record + assertEquals(ShadowOneSignalRestClient.networkCallCount, currentRequestCount + 3); + + int externalIdRequests = 0; + + for (ShadowOneSignalRestClient.Request request : ShadowOneSignalRestClient.requests) { + if (request.payload != null && request.payload.has("external_user_id")) { + externalIdRequests += 1; + assertEquals(request.payload.getString("external_user_id"), testExternalId); + } + } + + assertEquals(externalIdRequests, 3); + } + @Test public void sendExternalUserId_withCompletionHandler() throws Exception { String testExternalId = "test_ext_id"; @@ -1358,6 +1552,109 @@ public void sendExternalUserId_forPushAndEmail_withFailure_withCompletionHandler assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); } + @Test + public void sendExternalUserId_forPushAndSMS_withFailure_withCompletionHandler() throws Exception { + String testExternalId = "test_ext_id"; + + // 1. Init OneSignal + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + // 2. Attempt to set external user id with callback and force failure on the network requests + ShadowOneSignalRestClient.failAll = true; + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 3. Make sure lastExternalUserIdResponse has push and sms with success : false + JSONObject expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : false" + + " }," + + " \"sms\" : {" + + " \"success\" : false" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + + // 4. Flip ShadowOneSignalRestClient.failAll flag back to false + ShadowOneSignalRestClient.failAll = false; + + // 5. Attempt a second set external user id with callback + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 6. Make sure lastExternalUserIdResponse has push and sms with success : true + expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : true" + + " }," + + " \"sms\" : {" + + " \"success\" : true" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + } + + @Test + public void sendExternalUserId_forPushAndEmailAndSMS_withFailure_withCompletionHandler() throws Exception { + String testExternalId = "test_ext_id"; + + // 1. Init OneSignal + OneSignalInit(); + OneSignal.setEmail(ONESIGNAL_EMAIL_ADDRESS); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + // 2. Attempt to set external user id with callback and force failure on the network requests + ShadowOneSignalRestClient.failAll = true; + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 3. Make sure lastExternalUserIdResponse has push and sms with success : false + JSONObject expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : false" + + " }," + + " \"email\" : {" + + " \"success\" : false" + + " }," + + " \"sms\" : {" + + " \"success\" : false" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + + // 4. Flip ShadowOneSignalRestClient.failAll flag back to false + ShadowOneSignalRestClient.failAll = false; + + // 5. Attempt a second set external user id with callback + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 6. Make sure lastExternalUserIdResponse has push and sms with success : true + expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : true" + + " }," + + " \"email\" : {" + + " \"success\" : true" + + " }," + + " \"sms\" : {" + + " \"success\" : true" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + } + @Test public void sendExternalUserId_forPush_afterLoggingOutEmail_withCompletion() throws Exception { String testEmail = "test@onesignal.com"; @@ -1387,6 +1684,38 @@ public void sendExternalUserId_forPush_afterLoggingOutEmail_withCompletion() thr assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); } + // ####### on_session Tests ######## + + @Test + public void shouldSendOnSessionToEmail() throws Exception { + OneSignalInit(); + OneSignal.setEmail("josh@onesignal.com"); + threadAndTaskWait(); + + restartAppAndElapseTimeToNextSession(time); + OneSignalInit(); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(6); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); + assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522/on_session", emailPost.url); + } + + @Test + public void shouldSendOnSessionToSMS() throws Exception { + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + restartAppAndElapseTimeToNextSession(time); + OneSignalInit(); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(6); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); + assertEquals("players/" + ShadowOneSignalRestClient.SMS_USER_ID + "/on_session", smsPost.url); + } + // ####### on_focus Tests ######## @Test From 414ab5e7cc0fb1a96556132cce0ef4e3b0668299 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Tue, 2 Feb 2021 11:06:27 -0300 Subject: [PATCH 16/23] Add SMS location tests --- .../onesignal/MainOneSignalClassRunner.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java index 6684507f8d..2fcc0f29e3 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java @@ -3136,6 +3136,23 @@ public void shouldSendLocationToEmailRecord() throws Exception { assertEquals(0.0, postEmailPayload.getDouble("loc_type")); } + @Test + @Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class}) + public void shouldSendLocationToSMSRecord() throws Exception { + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + + OneSignalInit(); + OneSignal.setSMSNumber("123456789"); + threadAndTaskWait(); + + JSONObject postSMSPayload = ShadowOneSignalRestClient.requests.get(2).payload; + assertEquals(UserState.DEVICE_TYPE_SMS, postSMSPayload.getInt("device_type")); + assertEquals(1.0, postSMSPayload.getDouble("lat")); + assertEquals(2.0, postSMSPayload.getDouble("long")); + assertEquals(3.0, postSMSPayload.getDouble("loc_acc")); + assertEquals(0.0, postSMSPayload.getDouble("loc_type")); + } + @Test @Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class}) public void shouldRegisterWhenPromptingAfterInit() throws Exception { @@ -3338,6 +3355,24 @@ public void shouldSendLocationToEmailRecord_Huawei() throws Exception { assertEquals(0.0, postEmailPayload.getDouble("loc_type")); } + @Test + @Config(shadows = {ShadowHMSFusedLocationProviderClient.class}) + public void shouldSendLocationToSMSRecord_Huawei() throws Exception { + ShadowOSUtils.supportsHMS(true); + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + + OneSignalInit(); + OneSignal.setSMSNumber("123456789"); + threadAndTaskWait(); + + JSONObject postEmailPayload = ShadowOneSignalRestClient.requests.get(2).payload; + assertEquals(UserState.DEVICE_TYPE_SMS, postEmailPayload.getInt("device_type")); + assertEquals(1.0, postEmailPayload.getDouble("lat")); + assertEquals(2.0, postEmailPayload.getDouble("long")); + assertEquals(3.0, postEmailPayload.getDouble("loc_acc")); + assertEquals(0.0, postEmailPayload.getDouble("loc_type")); + } + @Test @Config(shadows = {ShadowHMSFusedLocationProviderClient.class, ShadowHuaweiTask.class}) public void shouldRegisterWhenPromptingAfterInit_Huawei() throws Exception { From b1cdfbb3efab0013ee8c569565752fe9bcfddf6d Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Tue, 2 Feb 2021 12:07:48 -0300 Subject: [PATCH 17/23] Move location tests to LocationIntegrationTests * Move location tests from MainOneSignalClassRunner file to LocationIntegrationTests --- .../onesignal/LocationIntegrationTests.java | 614 ++++++++++++++++++ .../onesignal/MainOneSignalClassRunner.java | 466 ------------- 2 files changed, 614 insertions(+), 466 deletions(-) create mode 100644 OneSignalSDK/unittest/src/test/java/com/test/onesignal/LocationIntegrationTests.java diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/LocationIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/LocationIntegrationTests.java new file mode 100644 index 0000000000..fe44cc9153 --- /dev/null +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/LocationIntegrationTests.java @@ -0,0 +1,614 @@ +package com.test.onesignal; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.AlarmManager; +import android.content.Context; +import android.content.Intent; +import android.location.Location; + +import androidx.test.core.app.ApplicationProvider; + +import com.huawei.hms.location.HWLocation; +import com.onesignal.FocusDelaySyncService; +import com.onesignal.MockOSLog; +import com.onesignal.MockOSSharedPreferences; +import com.onesignal.MockOSTimeImpl; +import com.onesignal.MockOneSignalDBHelper; +import com.onesignal.MockSessionManager; +import com.onesignal.OneSignal; +import com.onesignal.OneSignalPackagePrivateHelper; +import com.onesignal.ShadowAdvertisingIdProviderGPS; +import com.onesignal.ShadowCustomTabsClient; +import com.onesignal.ShadowCustomTabsSession; +import com.onesignal.ShadowFusedLocationApiWrapper; +import com.onesignal.ShadowGMSLocationController; +import com.onesignal.ShadowGMSLocationUpdateListener; +import com.onesignal.ShadowGoogleApiClientBuilder; +import com.onesignal.ShadowGoogleApiClientCompatProxy; +import com.onesignal.ShadowHMSFusedLocationProviderClient; +import com.onesignal.ShadowHMSLocationUpdateListener; +import com.onesignal.ShadowHmsInstanceId; +import com.onesignal.ShadowHuaweiTask; +import com.onesignal.ShadowOSUtils; +import com.onesignal.ShadowOneSignal; +import com.onesignal.ShadowOneSignalRestClient; +import com.onesignal.ShadowPushRegistratorFCM; +import com.onesignal.StaticResetHelper; +import com.onesignal.SyncJobService; +import com.onesignal.SyncService; +import com.onesignal.example.BlankActivity; +import com.onesignal.influence.data.OSTrackerFactory; + +import org.json.JSONObject; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.android.controller.ActivityController; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; +import org.robolectric.shadows.ShadowLog; + +import java.lang.reflect.Method; + +import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_getSessionListener; +import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setSessionManager; +import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTime; +import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTrackerFactory; +import static com.onesignal.ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse; +import static com.test.onesignal.TestHelpers.afterTestCleanup; +import static com.test.onesignal.TestHelpers.fastColdRestartApp; +import static com.test.onesignal.TestHelpers.restartAppAndElapseTimeToNextSession; +import static com.test.onesignal.TestHelpers.threadAndTaskWait; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNull; +import static org.robolectric.Shadows.shadowOf; + +@Config(packageName = "com.onesignal.example", + shadows = { + ShadowOneSignalRestClient.class, + ShadowPushRegistratorFCM.class, + ShadowOSUtils.class, + ShadowAdvertisingIdProviderGPS.class, + ShadowCustomTabsClient.class, + ShadowCustomTabsSession.class, + ShadowHmsInstanceId.class, + }, + sdk = 21 +) +@RunWith(RobolectricTestRunner.class) +public class LocationIntegrationTests { + + private static final String ONESIGNAL_APP_ID = "b4f7f966-d8cc-11e4-bed1-df8f05be55ba"; + + @SuppressLint("StaticFieldLeak") + private static Activity blankActivity; + private static ActivityController blankActivityController; + + private MockOSTimeImpl time; + private OSTrackerFactory trackerFactory; + private MockSessionManager sessionManager; + private MockOneSignalDBHelper dbHelper; + + private static void cleanUp() throws Exception { + ShadowGMSLocationController.reset(); + + TestHelpers.beforeTestInitAndCleanup(); + + // Set remote_params GET response + setRemoteParamsGetHtmlResponse(); + } + + @BeforeClass // Runs only once, before any tests + public static void setUpClass() throws Exception { + ShadowLog.stream = System.out; + + TestHelpers.beforeTestSuite(); + +// Field OneSignal_CurrentSubscription = OneSignal.class.getDeclaredField("subscribableStatus"); +// OneSignal_CurrentSubscription.setAccessible(true); + + OneSignal.setLogLevel(OneSignal.LOG_LEVEL.VERBOSE, OneSignal.LOG_LEVEL.NONE); + StaticResetHelper.saveStaticValues(); + } + + @Before + public void beforeEachTest() throws Exception { + blankActivityController = Robolectric.buildActivity(BlankActivity.class).create(); + blankActivity = blankActivityController.get(); + + time = new MockOSTimeImpl(); + trackerFactory = new OSTrackerFactory(new MockOSSharedPreferences(), new MockOSLog(), time); + sessionManager = new MockSessionManager(OneSignal_getSessionListener(), trackerFactory, new MockOSLog()); + dbHelper = new MockOneSignalDBHelper(ApplicationProvider.getApplicationContext()); + + TestHelpers.setupTestWorkManager(blankActivity); + + cleanUp(); + + OneSignal_setTime(time); + } + + @After + public void afterEachTest() throws Exception { + afterTestCleanup(); + } + + @AfterClass + public static void afterEverything() throws Exception { + cleanUp(); + } + + @Test + @Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class}) + public void shouldUpdateAllLocationFieldsWhenTimeStampChanges() throws Exception { + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + OneSignalInit(); + threadAndTaskWait(); + assertEquals(1.0, ShadowOneSignalRestClient.lastPost.getDouble("lat")); + assertEquals(2.0, ShadowOneSignalRestClient.lastPost.getDouble("long")); + assertEquals(3.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_acc")); + assertEquals(0.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_type")); + + ShadowOneSignalRestClient.lastPost = null; + ShadowFusedLocationApiWrapper.lat = 30d; + ShadowFusedLocationApiWrapper.log = 2.0d; + ShadowFusedLocationApiWrapper.accuracy = 5.0f; + ShadowFusedLocationApiWrapper.time = 2L; + restartAppAndElapseTimeToNextSession(time); + OneSignalInit(); + threadAndTaskWait(); + + assertEquals(30.0, ShadowOneSignalRestClient.lastPost.getDouble("lat")); + assertEquals(2.0, ShadowOneSignalRestClient.lastPost.getDouble("long")); + assertEquals(5.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_acc")); + assertEquals(0.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_type")); + } + + @Test + @Config(shadows = {ShadowOneSignal.class}) + @SuppressWarnings("unchecked") // getDeclaredMethod + public void testLocationTimeout() throws Exception { + OneSignalInit(); + threadAndTaskWait(); + + Class klass = Class.forName("com.onesignal.GMSLocationController"); + Method method = klass.getDeclaredMethod("startFallBackThread"); + method.setAccessible(true); + method.invoke(null); + method = klass.getDeclaredMethod("fireFailedComplete"); + method.setAccessible(true); + method.invoke(null); + threadAndTaskWait(); + + assertFalse(ShadowOneSignal.messages.contains("GoogleApiClient timeout")); + } + + @Test + @Config(shadows = { + ShadowGoogleApiClientBuilder.class, + ShadowGoogleApiClientCompatProxy.class, + ShadowFusedLocationApiWrapper.class }, + sdk = 19) + public void testLocationSchedule() throws Exception { + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_FINE_LOCATION"); + ShadowFusedLocationApiWrapper.lat = 1.0d; + ShadowFusedLocationApiWrapper.log = 2.0d; + ShadowFusedLocationApiWrapper.accuracy = 3.0f; + ShadowFusedLocationApiWrapper.time = 12345L; + + // location if we have permission + OneSignalInit(); + threadAndTaskWait(); + assertEquals(1.0, ShadowOneSignalRestClient.lastPost.optDouble("lat")); + assertEquals(2.0, ShadowOneSignalRestClient.lastPost.optDouble("long")); + assertEquals(3.0, ShadowOneSignalRestClient.lastPost.optDouble("loc_acc")); + assertEquals(1, ShadowOneSignalRestClient.lastPost.optInt("loc_type")); + + // Checking make sure an update is scheduled. + AlarmManager alarmManager = (AlarmManager)ApplicationProvider.getApplicationContext().getSystemService(Context.ALARM_SERVICE); + assertEquals(1, shadowOf(alarmManager).getScheduledAlarms().size()); + Intent intent = shadowOf(shadowOf(alarmManager).getNextScheduledAlarm().operation).getSavedIntent(); + assertEquals(SyncService.class, shadowOf(intent).getIntentClass()); + + // Setting up a new point and testing it is sent + Location fakeLocation = new Location("UnitTest"); + fakeLocation.setLatitude(1.1d); + fakeLocation.setLongitude(2.2d); + fakeLocation.setAccuracy(3.3f); + fakeLocation.setTime(12346L); + ShadowGMSLocationUpdateListener.provideFakeLocation(fakeLocation); + + Robolectric.buildService(SyncService.class, intent).startCommand(0, 0); + threadAndTaskWait(); + assertEquals(1.1d, ShadowOneSignalRestClient.lastPost.optDouble("lat")); + assertEquals(2.2d, ShadowOneSignalRestClient.lastPost.optDouble("long")); + assertEquals(3.3f, ShadowOneSignalRestClient.lastPost.opt("loc_acc")); + + assertEquals(false, ShadowOneSignalRestClient.lastPost.opt("loc_bg")); + assertEquals("11111111-2222-3333-4444-555555555555", ShadowOneSignalRestClient.lastPost.opt("ad_id")); + + // Testing loc_bg + blankActivityController.pause(); + threadAndTaskWait(); + Robolectric.buildService(FocusDelaySyncService.class, intent).startCommand(0, 0); + threadAndTaskWait(); + fakeLocation.setTime(12347L); + ShadowGMSLocationUpdateListener.provideFakeLocation(fakeLocation); + Robolectric.buildService(SyncService.class, intent).startCommand(0, 0); + threadAndTaskWait(); + assertEquals(1.1d, ShadowOneSignalRestClient.lastPost.optDouble("lat")); + assertEquals(2.2d, ShadowOneSignalRestClient.lastPost.optDouble("long")); + assertEquals(3.3f, ShadowOneSignalRestClient.lastPost.opt("loc_acc")); + assertEquals(true, ShadowOneSignalRestClient.lastPost.opt("loc_bg")); + assertEquals(1, ShadowOneSignalRestClient.lastPost.optInt("loc_type")); + assertEquals("11111111-2222-3333-4444-555555555555", ShadowOneSignalRestClient.lastPost.opt("ad_id")); + } + + @Test + @Config(shadows = { + ShadowGoogleApiClientBuilder.class, + ShadowGoogleApiClientCompatProxy.class, + ShadowFusedLocationApiWrapper.class }, + sdk = 19) + public void testLocationFromSyncAlarm() throws Exception { + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + + ShadowFusedLocationApiWrapper.lat = 1.1d; + ShadowFusedLocationApiWrapper.log = 2.1d; + ShadowFusedLocationApiWrapper.accuracy = 3.1f; + ShadowFusedLocationApiWrapper.time = 12346L; + + OneSignalInit(); + threadAndTaskWait(); + + fastColdRestartApp(); + AlarmManager alarmManager = (AlarmManager)ApplicationProvider.getApplicationContext().getSystemService(Context.ALARM_SERVICE); + shadowOf(alarmManager).getScheduledAlarms().clear(); + ShadowOneSignalRestClient.lastPost = null; + + ShadowFusedLocationApiWrapper.lat = 1.0; + ShadowFusedLocationApiWrapper.log = 2.0d; + ShadowFusedLocationApiWrapper.accuracy = 3.0f; + ShadowFusedLocationApiWrapper.time = 12345L; + + blankActivityController.pause(); + threadAndTaskWait(); + Robolectric.buildService(FocusDelaySyncService.class, new Intent()).startCommand(0, 0); + threadAndTaskWait(); + Robolectric.buildService(SyncService.class, new Intent()).startCommand(0, 0); + threadAndTaskWait(); + + assertEquals(1.0, ShadowOneSignalRestClient.lastPost.optDouble("lat")); + assertEquals(2.0, ShadowOneSignalRestClient.lastPost.optDouble("long")); + assertEquals(3.0, ShadowOneSignalRestClient.lastPost.optDouble("loc_acc")); + assertEquals(0, ShadowOneSignalRestClient.lastPost.optInt("loc_type")); + assertEquals(12345L, ShadowOneSignalRestClient.lastPost.optInt("loc_time_stamp")); + assertEquals(true, ShadowOneSignalRestClient.lastPost.opt("loc_bg")); + + // Checking make sure an update is scheduled. + alarmManager = (AlarmManager)ApplicationProvider.getApplicationContext().getSystemService(Context.ALARM_SERVICE); + assertEquals(1, shadowOf(alarmManager).getScheduledAlarms().size()); + Intent intent = shadowOf(shadowOf(alarmManager).getNextScheduledAlarm().operation).getSavedIntent(); + assertEquals(SyncService.class, shadowOf(intent).getIntentClass()); + shadowOf(alarmManager).getScheduledAlarms().clear(); + } + + @Test + @Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class}) + public void shouldSendLocationToEmailRecord() throws Exception { + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + + OneSignalInit(); + OneSignal.setEmail("josh@onesignal.com"); + threadAndTaskWait(); + + JSONObject postEmailPayload = ShadowOneSignalRestClient.requests.get(2).payload; + assertEquals(11, postEmailPayload.getInt("device_type")); + assertEquals(1.0, postEmailPayload.getDouble("lat")); + assertEquals(2.0, postEmailPayload.getDouble("long")); + assertEquals(3.0, postEmailPayload.getDouble("loc_acc")); + assertEquals(0.0, postEmailPayload.getDouble("loc_type")); + } + + @Test + @Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class}) + public void shouldSendLocationToSMSRecord() throws Exception { + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + + OneSignalInit(); + OneSignal.setSMSNumber("123456789"); + threadAndTaskWait(); + + JSONObject postSMSPayload = ShadowOneSignalRestClient.requests.get(2).payload; + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_SMS, postSMSPayload.getInt("device_type")); + assertEquals(1.0, postSMSPayload.getDouble("lat")); + assertEquals(2.0, postSMSPayload.getDouble("long")); + assertEquals(3.0, postSMSPayload.getDouble("loc_acc")); + assertEquals(0.0, postSMSPayload.getDouble("loc_type")); + } + + @Test + @Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class}) + public void shouldRegisterWhenPromptingAfterInit() throws Exception { + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + ShadowGoogleApiClientCompatProxy.skipOnConnected = true; + + // Test promptLocation right after init race condition + OneSignalInit(); + OneSignal.promptLocation(); + + ShadowGoogleApiClientBuilder.connectionCallback.onConnected(null); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request request = ShadowOneSignalRestClient.requests.get(1); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, request.method); + assertEquals(1, request.payload.get("device_type")); + assertEquals(ShadowPushRegistratorFCM.regId, request.payload.get("identifier")); + } + + @Test + @Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class}) + public void shouldCallOnSessionEvenIfSyncJobStarted() throws Exception { + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + + OneSignalInit(); + threadAndTaskWait(); + + restartAppAndElapseTimeToNextSession(time); + ShadowGoogleApiClientCompatProxy.skipOnConnected = true; + OneSignalInit(); + + SyncJobService syncJobService = Robolectric.buildService(SyncJobService.class).create().get(); + syncJobService.onStartJob(null); + TestHelpers.getThreadByName("OS_SYNCSRV_BG_SYNC").join(); + OneSignalPackagePrivateHelper.runAllNetworkRunnables(); + ShadowGoogleApiClientBuilder.connectionCallback.onConnected(null); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request request = ShadowOneSignalRestClient.requests.get(3); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, request.method); + assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_session", request.url); + } + + // ####### Unit Test Huawei Location ######## + + @Test + @Config(shadows = {ShadowHMSFusedLocationProviderClient.class}) + public void shouldUpdateAllLocationFieldsWhenTimeStampChanges_Huawei() throws Exception { + ShadowOSUtils.supportsHMS(true); + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + OneSignalInit(); + threadAndTaskWait(); + assertEquals(1.0, ShadowOneSignalRestClient.lastPost.getDouble("lat")); + assertEquals(2.0, ShadowOneSignalRestClient.lastPost.getDouble("long")); + assertEquals(3.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_acc")); + assertEquals(0.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_type")); + + ShadowOneSignalRestClient.lastPost = null; + ShadowHMSFusedLocationProviderClient.resetStatics(); + ShadowHMSFusedLocationProviderClient.lat = 30d; + ShadowHMSFusedLocationProviderClient.log = 2.0d; + ShadowHMSFusedLocationProviderClient.accuracy = 5.0f; + ShadowHMSFusedLocationProviderClient.time = 2L; + restartAppAndElapseTimeToNextSession(time); + OneSignalInit(); + threadAndTaskWait(); + + assertEquals(30.0, ShadowOneSignalRestClient.lastPost.getDouble("lat")); + assertEquals(2.0, ShadowOneSignalRestClient.lastPost.getDouble("long")); + assertEquals(5.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_acc")); + assertEquals(0.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_type")); + } + + @Test + @Config(shadows = { + ShadowHMSFusedLocationProviderClient.class + }, sdk = 19) + public void testLocationSchedule_Huawei() throws Exception { + ShadowOSUtils.supportsHMS(true); + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_FINE_LOCATION"); + ShadowHMSFusedLocationProviderClient.lat = 1.0d; + ShadowHMSFusedLocationProviderClient.log = 2.0d; + ShadowHMSFusedLocationProviderClient.accuracy = 3.0f; + ShadowHMSFusedLocationProviderClient.time = 12345L; + + // location if we have permission + OneSignalInit(); + threadAndTaskWait(); + assertEquals(1.0, ShadowOneSignalRestClient.lastPost.optDouble("lat")); + assertEquals(2.0, ShadowOneSignalRestClient.lastPost.optDouble("long")); + assertEquals(3.0, ShadowOneSignalRestClient.lastPost.optDouble("loc_acc")); + assertEquals(1, ShadowOneSignalRestClient.lastPost.optInt("loc_type")); + + // Checking make sure an update is scheduled. + AlarmManager alarmManager = (AlarmManager)ApplicationProvider.getApplicationContext().getSystemService(Context.ALARM_SERVICE); + assertEquals(1, shadowOf(alarmManager).getScheduledAlarms().size()); + Intent intent = shadowOf(shadowOf(alarmManager).getNextScheduledAlarm().operation).getSavedIntent(); + assertEquals(SyncService.class, shadowOf(intent).getIntentClass()); + + // Setting up a new point and testing it is sent + HWLocation fakeLocation = new HWLocation(); + fakeLocation.setLatitude(1.1d); + fakeLocation.setLongitude(2.2d); + fakeLocation.setAccuracy(3.3f); + fakeLocation.setTime(12346L); + ShadowHMSLocationUpdateListener.provideFakeLocation_Huawei(fakeLocation); + + Robolectric.buildService(SyncService.class, intent).startCommand(0, 0); + threadAndTaskWait(); + assertEquals(1.1d, ShadowOneSignalRestClient.lastPost.optDouble("lat")); + assertEquals(2.2d, ShadowOneSignalRestClient.lastPost.optDouble("long")); + assertEquals(3.3f, ShadowOneSignalRestClient.lastPost.opt("loc_acc")); + + assertEquals(false, ShadowOneSignalRestClient.lastPost.opt("loc_bg")); + // Currently not getting ad_id for HMS devices + assertNull(ShadowOneSignalRestClient.lastPost.opt("ad_id")); + + // Testing loc_bg + blankActivityController.pause(); + threadAndTaskWait(); + Robolectric.buildService(FocusDelaySyncService.class, intent).startCommand(0, 0); + threadAndTaskWait(); + + fakeLocation.setTime(12347L); + ShadowHMSLocationUpdateListener.provideFakeLocation_Huawei(fakeLocation); + Robolectric.buildService(SyncService.class, intent).startCommand(0, 0); + threadAndTaskWait(); + assertEquals(1.1d, ShadowOneSignalRestClient.lastPost.optDouble("lat")); + assertEquals(2.2d, ShadowOneSignalRestClient.lastPost.optDouble("long")); + assertEquals(3.3f, ShadowOneSignalRestClient.lastPost.opt("loc_acc")); + assertEquals(true, ShadowOneSignalRestClient.lastPost.opt("loc_bg")); + assertEquals(1, ShadowOneSignalRestClient.lastPost.optInt("loc_type")); + // Currently not getting ad_id for HMS devices + assertNull(ShadowOneSignalRestClient.lastPost.opt("ad_id")); + } + + @Test + @Config(shadows = { + ShadowHMSFusedLocationProviderClient.class, + ShadowHuaweiTask.class + }, sdk = 19) + public void testLocationFromSyncAlarm_Huawei() throws Exception { + ShadowOSUtils.supportsHMS(true); + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + + ShadowHMSFusedLocationProviderClient.lat = 1.1d; + ShadowHMSFusedLocationProviderClient.log = 2.1d; + ShadowHMSFusedLocationProviderClient.accuracy = 3.1f; + ShadowHMSFusedLocationProviderClient.time = 12346L; + + OneSignalInit(); + threadAndTaskWait(); + + fastColdRestartApp(); + AlarmManager alarmManager = (AlarmManager)ApplicationProvider.getApplicationContext().getSystemService(Context.ALARM_SERVICE); + shadowOf(alarmManager).getScheduledAlarms().clear(); + + ShadowOneSignalRestClient.lastPost = null; + ShadowHMSFusedLocationProviderClient.resetStatics(); + ShadowHMSFusedLocationProviderClient.lat = 1.0; + ShadowHMSFusedLocationProviderClient.log = 2.0d; + ShadowHMSFusedLocationProviderClient.accuracy = 3.0f; + ShadowHMSFusedLocationProviderClient.time = 12345L; + ShadowHMSFusedLocationProviderClient.shadowTask = true; + ShadowHuaweiTask.result = ShadowHMSFusedLocationProviderClient.getLocation(); + + Robolectric.buildService(SyncService.class, new Intent()).startCommand(0, 0); + threadAndTaskWait(); + + assertEquals(1.0, ShadowOneSignalRestClient.lastPost.optDouble("lat")); + assertEquals(2.0, ShadowOneSignalRestClient.lastPost.optDouble("long")); + assertEquals(3.0, ShadowOneSignalRestClient.lastPost.optDouble("loc_acc")); + assertEquals(0, ShadowOneSignalRestClient.lastPost.optInt("loc_type")); + assertEquals(12345L, ShadowOneSignalRestClient.lastPost.optInt("loc_time_stamp")); + assertEquals(true, ShadowOneSignalRestClient.lastPost.opt("loc_bg")); + + // Checking make sure an update is scheduled. + alarmManager = (AlarmManager)ApplicationProvider.getApplicationContext().getSystemService(Context.ALARM_SERVICE); + assertEquals(1, shadowOf(alarmManager).getScheduledAlarms().size()); + Intent intent = shadowOf(shadowOf(alarmManager).getNextScheduledAlarm().operation).getSavedIntent(); + assertEquals(SyncService.class, shadowOf(intent).getIntentClass()); + shadowOf(alarmManager).getScheduledAlarms().clear(); + } + + @Test + @Config(shadows = {ShadowHMSFusedLocationProviderClient.class}) + public void shouldSendLocationToEmailRecord_Huawei() throws Exception { + ShadowOSUtils.supportsHMS(true); + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + + OneSignalInit(); + OneSignal.setEmail("josh@onesignal.com"); + threadAndTaskWait(); + + JSONObject postEmailPayload = ShadowOneSignalRestClient.requests.get(2).payload; + assertEquals(11, postEmailPayload.getInt("device_type")); + assertEquals(1.0, postEmailPayload.getDouble("lat")); + assertEquals(2.0, postEmailPayload.getDouble("long")); + assertEquals(3.0, postEmailPayload.getDouble("loc_acc")); + assertEquals(0.0, postEmailPayload.getDouble("loc_type")); + } + + @Test + @Config(shadows = {ShadowHMSFusedLocationProviderClient.class}) + public void shouldSendLocationToSMSRecord_Huawei() throws Exception { + ShadowOSUtils.supportsHMS(true); + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + + OneSignalInit(); + OneSignal.setSMSNumber("123456789"); + threadAndTaskWait(); + + JSONObject postEmailPayload = ShadowOneSignalRestClient.requests.get(2).payload; + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_SMS, postEmailPayload.getInt("device_type")); + assertEquals(1.0, postEmailPayload.getDouble("lat")); + assertEquals(2.0, postEmailPayload.getDouble("long")); + assertEquals(3.0, postEmailPayload.getDouble("loc_acc")); + assertEquals(0.0, postEmailPayload.getDouble("loc_type")); + } + + @Test + @Config(shadows = {ShadowHMSFusedLocationProviderClient.class, ShadowHuaweiTask.class}) + public void shouldRegisterWhenPromptingAfterInit_Huawei() throws Exception { + ShadowOSUtils.supportsHMS(true); + ShadowHMSFusedLocationProviderClient.skipOnGetLocation = true; + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + + // Test promptLocation right after init race condition + OneSignalInit(); + OneSignal.promptLocation(); + + ShadowHuaweiTask.callSuccessListener(ShadowHMSFusedLocationProviderClient.getLocation()); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request request = ShadowOneSignalRestClient.requests.get(1); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, request.method); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_HUAWEI, request.payload.get("device_type")); + assertEquals(ShadowHmsInstanceId.token, request.payload.get("identifier")); + } + + @Test + @Config(shadows = {ShadowHMSFusedLocationProviderClient.class, ShadowHuaweiTask.class}) + public void shouldCallOnSessionEvenIfSyncJobStarted_Huawei() throws Exception { + ShadowOSUtils.supportsHMS(true); + ShadowHMSFusedLocationProviderClient.shadowTask = true; + ShadowHuaweiTask.result = ShadowHMSFusedLocationProviderClient.getLocation(); + ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); + + OneSignalInit(); + threadAndTaskWait(); + + restartAppAndElapseTimeToNextSession(time); + ShadowHMSFusedLocationProviderClient.skipOnGetLocation = true; + OneSignalInit(); + + SyncJobService syncJobService = Robolectric.buildService(SyncJobService.class).create().get(); + syncJobService.onStartJob(null); + TestHelpers.getThreadByName("OS_SYNCSRV_BG_SYNC").join(); + OneSignalPackagePrivateHelper.runAllNetworkRunnables(); + ShadowHuaweiTask.callSuccessListener(ShadowHMSFusedLocationProviderClient.getLocation()); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request request = ShadowOneSignalRestClient.requests.get(3); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, request.method); + assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_session", request.url); + } + + private void OneSignalInit() { + OneSignal.setLogLevel(OneSignal.LOG_LEVEL.VERBOSE, OneSignal.LOG_LEVEL.NONE); + ShadowOSUtils.subscribableStatus = 1; + OneSignal_setTime(time); + OneSignal_setTrackerFactory(trackerFactory); + OneSignal_setSessionManager(sessionManager); + OneSignal.setAppId(ONESIGNAL_APP_ID); + OneSignal.initWithContext(blankActivity); + blankActivityController.resume(); + } +} diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java index 2fcc0f29e3..a27d8982bc 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java @@ -36,13 +36,11 @@ import android.content.pm.ActivityInfo; import android.content.pm.ResolveInfo; import android.database.Cursor; -import android.location.Location; import android.net.ConnectivityManager; import android.os.Bundle; import androidx.test.core.app.ApplicationProvider; -import com.huawei.hms.location.HWLocation; import com.onesignal.FocusDelaySyncJobService; import com.onesignal.FocusDelaySyncService; import com.onesignal.MockOSLog; @@ -64,7 +62,6 @@ import com.onesignal.OneSignal; import com.onesignal.OneSignal.ChangeTagsUpdateHandler; import com.onesignal.OneSignalPackagePrivateHelper; -import com.onesignal.OneSignalPackagePrivateHelper.UserState; import com.onesignal.OneSignalShadowPackageManager; import com.onesignal.PermissionsActivity; import com.onesignal.ShadowAdvertisingIdProviderGPS; @@ -74,14 +71,10 @@ import com.onesignal.ShadowFirebaseAnalytics; import com.onesignal.ShadowFusedLocationApiWrapper; import com.onesignal.ShadowGMSLocationController; -import com.onesignal.ShadowGMSLocationUpdateListener; import com.onesignal.ShadowGenerateNotification; import com.onesignal.ShadowGoogleApiClientBuilder; import com.onesignal.ShadowGoogleApiClientCompatProxy; -import com.onesignal.ShadowHMSFusedLocationProviderClient; -import com.onesignal.ShadowHMSLocationUpdateListener; import com.onesignal.ShadowHmsInstanceId; -import com.onesignal.ShadowHuaweiTask; import com.onesignal.ShadowJobService; import com.onesignal.ShadowNotificationManagerCompat; import com.onesignal.ShadowOSUtils; @@ -116,7 +109,6 @@ import org.robolectric.shadows.ShadowLog; import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; @@ -2962,464 +2954,6 @@ public void shouldNotDoubleCountFocusTime() throws Exception { } */ - // ####### Unit Test Location ######## - - @Test - @Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class}) - public void shouldUpdateAllLocationFieldsWhenTimeStampChanges() throws Exception { - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); - OneSignalInit(); - threadAndTaskWait(); - assertEquals(1.0, ShadowOneSignalRestClient.lastPost.getDouble("lat")); - assertEquals(2.0, ShadowOneSignalRestClient.lastPost.getDouble("long")); - assertEquals(3.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_acc")); - assertEquals(0.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_type")); - - ShadowOneSignalRestClient.lastPost = null; - ShadowFusedLocationApiWrapper.lat = 30d; - ShadowFusedLocationApiWrapper.log = 2.0d; - ShadowFusedLocationApiWrapper.accuracy = 5.0f; - ShadowFusedLocationApiWrapper.time = 2L; - restartAppAndElapseTimeToNextSession(time); - OneSignalInit(); - threadAndTaskWait(); - - assertEquals(30.0, ShadowOneSignalRestClient.lastPost.getDouble("lat")); - assertEquals(2.0, ShadowOneSignalRestClient.lastPost.getDouble("long")); - assertEquals(5.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_acc")); - assertEquals(0.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_type")); - } - - @Test - @Config(shadows = {ShadowOneSignal.class}) - @SuppressWarnings("unchecked") // getDeclaredMethod - public void testLocationTimeout() throws Exception { - OneSignalInit(); - threadAndTaskWait(); - - Class klass = Class.forName("com.onesignal.GMSLocationController"); - Method method = klass.getDeclaredMethod("startFallBackThread"); - method.setAccessible(true); - method.invoke(null); - method = klass.getDeclaredMethod("fireFailedComplete"); - method.setAccessible(true); - method.invoke(null); - threadAndTaskWait(); - - assertFalse(ShadowOneSignal.messages.contains("GoogleApiClient timeout")); - } - - @Test - @Config(shadows = { - ShadowGoogleApiClientBuilder.class, - ShadowGoogleApiClientCompatProxy.class, - ShadowFusedLocationApiWrapper.class }, - sdk = 19) - public void testLocationSchedule() throws Exception { - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_FINE_LOCATION"); - ShadowFusedLocationApiWrapper.lat = 1.0d; - ShadowFusedLocationApiWrapper.log = 2.0d; - ShadowFusedLocationApiWrapper.accuracy = 3.0f; - ShadowFusedLocationApiWrapper.time = 12345L; - - // location if we have permission - OneSignalInit(); - threadAndTaskWait(); - assertEquals(1.0, ShadowOneSignalRestClient.lastPost.optDouble("lat")); - assertEquals(2.0, ShadowOneSignalRestClient.lastPost.optDouble("long")); - assertEquals(3.0, ShadowOneSignalRestClient.lastPost.optDouble("loc_acc")); - assertEquals(1, ShadowOneSignalRestClient.lastPost.optInt("loc_type")); - - // Checking make sure an update is scheduled. - AlarmManager alarmManager = (AlarmManager)ApplicationProvider.getApplicationContext().getSystemService(Context.ALARM_SERVICE); - assertEquals(1, shadowOf(alarmManager).getScheduledAlarms().size()); - Intent intent = shadowOf(shadowOf(alarmManager).getNextScheduledAlarm().operation).getSavedIntent(); - assertEquals(SyncService.class, shadowOf(intent).getIntentClass()); - - // Setting up a new point and testing it is sent - Location fakeLocation = new Location("UnitTest"); - fakeLocation.setLatitude(1.1d); - fakeLocation.setLongitude(2.2d); - fakeLocation.setAccuracy(3.3f); - fakeLocation.setTime(12346L); - ShadowGMSLocationUpdateListener.provideFakeLocation(fakeLocation); - - Robolectric.buildService(SyncService.class, intent).startCommand(0, 0); - threadAndTaskWait(); - assertEquals(1.1d, ShadowOneSignalRestClient.lastPost.optDouble("lat")); - assertEquals(2.2d, ShadowOneSignalRestClient.lastPost.optDouble("long")); - assertEquals(3.3f, ShadowOneSignalRestClient.lastPost.opt("loc_acc")); - - assertEquals(false, ShadowOneSignalRestClient.lastPost.opt("loc_bg")); - assertEquals("11111111-2222-3333-4444-555555555555", ShadowOneSignalRestClient.lastPost.opt("ad_id")); - - // Testing loc_bg - blankActivityController.pause(); - threadAndTaskWait(); - Robolectric.buildService(FocusDelaySyncService.class, intent).startCommand(0, 0); - threadAndTaskWait(); - fakeLocation.setTime(12347L); - ShadowGMSLocationUpdateListener.provideFakeLocation(fakeLocation); - Robolectric.buildService(SyncService.class, intent).startCommand(0, 0); - threadAndTaskWait(); - assertEquals(1.1d, ShadowOneSignalRestClient.lastPost.optDouble("lat")); - assertEquals(2.2d, ShadowOneSignalRestClient.lastPost.optDouble("long")); - assertEquals(3.3f, ShadowOneSignalRestClient.lastPost.opt("loc_acc")); - assertEquals(true, ShadowOneSignalRestClient.lastPost.opt("loc_bg")); - assertEquals(1, ShadowOneSignalRestClient.lastPost.optInt("loc_type")); - assertEquals("11111111-2222-3333-4444-555555555555", ShadowOneSignalRestClient.lastPost.opt("ad_id")); - } - - @Test - @Config(shadows = { - ShadowGoogleApiClientBuilder.class, - ShadowGoogleApiClientCompatProxy.class, - ShadowFusedLocationApiWrapper.class }, - sdk = 19) - public void testLocationFromSyncAlarm() throws Exception { - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); - - ShadowFusedLocationApiWrapper.lat = 1.1d; - ShadowFusedLocationApiWrapper.log = 2.1d; - ShadowFusedLocationApiWrapper.accuracy = 3.1f; - ShadowFusedLocationApiWrapper.time = 12346L; - - OneSignalInit(); - threadAndTaskWait(); - - fastColdRestartApp(); - AlarmManager alarmManager = (AlarmManager)ApplicationProvider.getApplicationContext().getSystemService(Context.ALARM_SERVICE); - shadowOf(alarmManager).getScheduledAlarms().clear(); - ShadowOneSignalRestClient.lastPost = null; - - ShadowFusedLocationApiWrapper.lat = 1.0; - ShadowFusedLocationApiWrapper.log = 2.0d; - ShadowFusedLocationApiWrapper.accuracy = 3.0f; - ShadowFusedLocationApiWrapper.time = 12345L; - - blankActivityController.pause(); - threadAndTaskWait(); - Robolectric.buildService(FocusDelaySyncService.class, new Intent()).startCommand(0, 0); - threadAndTaskWait(); - Robolectric.buildService(SyncService.class, new Intent()).startCommand(0, 0); - threadAndTaskWait(); - - assertEquals(1.0, ShadowOneSignalRestClient.lastPost.optDouble("lat")); - assertEquals(2.0, ShadowOneSignalRestClient.lastPost.optDouble("long")); - assertEquals(3.0, ShadowOneSignalRestClient.lastPost.optDouble("loc_acc")); - assertEquals(0, ShadowOneSignalRestClient.lastPost.optInt("loc_type")); - assertEquals(12345L, ShadowOneSignalRestClient.lastPost.optInt("loc_time_stamp")); - assertEquals(true, ShadowOneSignalRestClient.lastPost.opt("loc_bg")); - - // Checking make sure an update is scheduled. - alarmManager = (AlarmManager)ApplicationProvider.getApplicationContext().getSystemService(Context.ALARM_SERVICE); - assertEquals(1, shadowOf(alarmManager).getScheduledAlarms().size()); - Intent intent = shadowOf(shadowOf(alarmManager).getNextScheduledAlarm().operation).getSavedIntent(); - assertEquals(SyncService.class, shadowOf(intent).getIntentClass()); - shadowOf(alarmManager).getScheduledAlarms().clear(); - } - - @Test - @Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class}) - public void shouldSendLocationToEmailRecord() throws Exception { - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); - - OneSignalInit(); - OneSignal.setEmail("josh@onesignal.com"); - threadAndTaskWait(); - - JSONObject postEmailPayload = ShadowOneSignalRestClient.requests.get(2).payload; - assertEquals(11, postEmailPayload.getInt("device_type")); - assertEquals(1.0, postEmailPayload.getDouble("lat")); - assertEquals(2.0, postEmailPayload.getDouble("long")); - assertEquals(3.0, postEmailPayload.getDouble("loc_acc")); - assertEquals(0.0, postEmailPayload.getDouble("loc_type")); - } - - @Test - @Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class}) - public void shouldSendLocationToSMSRecord() throws Exception { - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); - - OneSignalInit(); - OneSignal.setSMSNumber("123456789"); - threadAndTaskWait(); - - JSONObject postSMSPayload = ShadowOneSignalRestClient.requests.get(2).payload; - assertEquals(UserState.DEVICE_TYPE_SMS, postSMSPayload.getInt("device_type")); - assertEquals(1.0, postSMSPayload.getDouble("lat")); - assertEquals(2.0, postSMSPayload.getDouble("long")); - assertEquals(3.0, postSMSPayload.getDouble("loc_acc")); - assertEquals(0.0, postSMSPayload.getDouble("loc_type")); - } - - @Test - @Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class}) - public void shouldRegisterWhenPromptingAfterInit() throws Exception { - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); - ShadowGoogleApiClientCompatProxy.skipOnConnected = true; - - // Test promptLocation right after init race condition - OneSignalInit(); - OneSignal.promptLocation(); - - ShadowGoogleApiClientBuilder.connectionCallback.onConnected(null); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request request = ShadowOneSignalRestClient.requests.get(1); - assertEquals(REST_METHOD.POST, request.method); - assertEquals(1, request.payload.get("device_type")); - assertEquals(ShadowPushRegistratorFCM.regId, request.payload.get("identifier")); - } - - @Test - @Config(shadows = {ShadowGoogleApiClientBuilder.class, ShadowGoogleApiClientCompatProxy.class, ShadowFusedLocationApiWrapper.class}) - public void shouldCallOnSessionEvenIfSyncJobStarted() throws Exception { - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); - - OneSignalInit(); - threadAndTaskWait(); - - restartAppAndElapseTimeToNextSession(time); - ShadowGoogleApiClientCompatProxy.skipOnConnected = true; - OneSignalInit(); - - SyncJobService syncJobService = Robolectric.buildService(SyncJobService.class).create().get(); - syncJobService.onStartJob(null); - TestHelpers.getThreadByName("OS_SYNCSRV_BG_SYNC").join(); - OneSignalPackagePrivateHelper.runAllNetworkRunnables(); - ShadowGoogleApiClientBuilder.connectionCallback.onConnected(null); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request request = ShadowOneSignalRestClient.requests.get(3); - assertEquals(REST_METHOD.POST, request.method); - assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_session", request.url); - } - - // ####### Unit Test Huawei Location ######## - - @Test - @Config(shadows = {ShadowHMSFusedLocationProviderClient.class}) - public void shouldUpdateAllLocationFieldsWhenTimeStampChanges_Huawei() throws Exception { - ShadowOSUtils.supportsHMS(true); - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); - OneSignalInit(); - threadAndTaskWait(); - assertEquals(1.0, ShadowOneSignalRestClient.lastPost.getDouble("lat")); - assertEquals(2.0, ShadowOneSignalRestClient.lastPost.getDouble("long")); - assertEquals(3.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_acc")); - assertEquals(0.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_type")); - - ShadowOneSignalRestClient.lastPost = null; - ShadowHMSFusedLocationProviderClient.resetStatics(); - ShadowHMSFusedLocationProviderClient.lat = 30d; - ShadowHMSFusedLocationProviderClient.log = 2.0d; - ShadowHMSFusedLocationProviderClient.accuracy = 5.0f; - ShadowHMSFusedLocationProviderClient.time = 2L; - restartAppAndElapseTimeToNextSession(time); - OneSignalInit(); - threadAndTaskWait(); - - assertEquals(30.0, ShadowOneSignalRestClient.lastPost.getDouble("lat")); - assertEquals(2.0, ShadowOneSignalRestClient.lastPost.getDouble("long")); - assertEquals(5.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_acc")); - assertEquals(0.0, ShadowOneSignalRestClient.lastPost.getDouble("loc_type")); - } - - @Test - @Config(shadows = { - ShadowHMSFusedLocationProviderClient.class - }, sdk = 19) - public void testLocationSchedule_Huawei() throws Exception { - ShadowOSUtils.supportsHMS(true); - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_FINE_LOCATION"); - ShadowHMSFusedLocationProviderClient.lat = 1.0d; - ShadowHMSFusedLocationProviderClient.log = 2.0d; - ShadowHMSFusedLocationProviderClient.accuracy = 3.0f; - ShadowHMSFusedLocationProviderClient.time = 12345L; - - // location if we have permission - OneSignalInit(); - threadAndTaskWait(); - assertEquals(1.0, ShadowOneSignalRestClient.lastPost.optDouble("lat")); - assertEquals(2.0, ShadowOneSignalRestClient.lastPost.optDouble("long")); - assertEquals(3.0, ShadowOneSignalRestClient.lastPost.optDouble("loc_acc")); - assertEquals(1, ShadowOneSignalRestClient.lastPost.optInt("loc_type")); - - // Checking make sure an update is scheduled. - AlarmManager alarmManager = (AlarmManager)ApplicationProvider.getApplicationContext().getSystemService(Context.ALARM_SERVICE); - assertEquals(1, shadowOf(alarmManager).getScheduledAlarms().size()); - Intent intent = shadowOf(shadowOf(alarmManager).getNextScheduledAlarm().operation).getSavedIntent(); - assertEquals(SyncService.class, shadowOf(intent).getIntentClass()); - - // Setting up a new point and testing it is sent - HWLocation fakeLocation = new HWLocation(); - fakeLocation.setLatitude(1.1d); - fakeLocation.setLongitude(2.2d); - fakeLocation.setAccuracy(3.3f); - fakeLocation.setTime(12346L); - ShadowHMSLocationUpdateListener.provideFakeLocation_Huawei(fakeLocation); - - Robolectric.buildService(SyncService.class, intent).startCommand(0, 0); - threadAndTaskWait(); - assertEquals(1.1d, ShadowOneSignalRestClient.lastPost.optDouble("lat")); - assertEquals(2.2d, ShadowOneSignalRestClient.lastPost.optDouble("long")); - assertEquals(3.3f, ShadowOneSignalRestClient.lastPost.opt("loc_acc")); - - assertEquals(false, ShadowOneSignalRestClient.lastPost.opt("loc_bg")); - // Currently not getting ad_id for HMS devices - assertNull(ShadowOneSignalRestClient.lastPost.opt("ad_id")); - - // Testing loc_bg - blankActivityController.pause(); - threadAndTaskWait(); - Robolectric.buildService(FocusDelaySyncService.class, intent).startCommand(0, 0); - threadAndTaskWait(); - - fakeLocation.setTime(12347L); - ShadowHMSLocationUpdateListener.provideFakeLocation_Huawei(fakeLocation); - Robolectric.buildService(SyncService.class, intent).startCommand(0, 0); - threadAndTaskWait(); - assertEquals(1.1d, ShadowOneSignalRestClient.lastPost.optDouble("lat")); - assertEquals(2.2d, ShadowOneSignalRestClient.lastPost.optDouble("long")); - assertEquals(3.3f, ShadowOneSignalRestClient.lastPost.opt("loc_acc")); - assertEquals(true, ShadowOneSignalRestClient.lastPost.opt("loc_bg")); - assertEquals(1, ShadowOneSignalRestClient.lastPost.optInt("loc_type")); - // Currently not getting ad_id for HMS devices - assertNull(ShadowOneSignalRestClient.lastPost.opt("ad_id")); - } - - @Test - @Config(shadows = { - ShadowHMSFusedLocationProviderClient.class, - ShadowHuaweiTask.class - }, sdk = 19) - public void testLocationFromSyncAlarm_Huawei() throws Exception { - ShadowOSUtils.supportsHMS(true); - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); - - ShadowHMSFusedLocationProviderClient.lat = 1.1d; - ShadowHMSFusedLocationProviderClient.log = 2.1d; - ShadowHMSFusedLocationProviderClient.accuracy = 3.1f; - ShadowHMSFusedLocationProviderClient.time = 12346L; - - OneSignalInit(); - threadAndTaskWait(); - - fastColdRestartApp(); - AlarmManager alarmManager = (AlarmManager)ApplicationProvider.getApplicationContext().getSystemService(Context.ALARM_SERVICE); - shadowOf(alarmManager).getScheduledAlarms().clear(); - - ShadowOneSignalRestClient.lastPost = null; - ShadowHMSFusedLocationProviderClient.resetStatics(); - ShadowHMSFusedLocationProviderClient.lat = 1.0; - ShadowHMSFusedLocationProviderClient.log = 2.0d; - ShadowHMSFusedLocationProviderClient.accuracy = 3.0f; - ShadowHMSFusedLocationProviderClient.time = 12345L; - ShadowHMSFusedLocationProviderClient.shadowTask = true; - ShadowHuaweiTask.result = ShadowHMSFusedLocationProviderClient.getLocation(); - - Robolectric.buildService(SyncService.class, new Intent()).startCommand(0, 0); - threadAndTaskWait(); - - assertEquals(1.0, ShadowOneSignalRestClient.lastPost.optDouble("lat")); - assertEquals(2.0, ShadowOneSignalRestClient.lastPost.optDouble("long")); - assertEquals(3.0, ShadowOneSignalRestClient.lastPost.optDouble("loc_acc")); - assertEquals(0, ShadowOneSignalRestClient.lastPost.optInt("loc_type")); - assertEquals(12345L, ShadowOneSignalRestClient.lastPost.optInt("loc_time_stamp")); - assertEquals(true, ShadowOneSignalRestClient.lastPost.opt("loc_bg")); - - // Checking make sure an update is scheduled. - alarmManager = (AlarmManager)ApplicationProvider.getApplicationContext().getSystemService(Context.ALARM_SERVICE); - assertEquals(1, shadowOf(alarmManager).getScheduledAlarms().size()); - Intent intent = shadowOf(shadowOf(alarmManager).getNextScheduledAlarm().operation).getSavedIntent(); - assertEquals(SyncService.class, shadowOf(intent).getIntentClass()); - shadowOf(alarmManager).getScheduledAlarms().clear(); - } - - @Test - @Config(shadows = {ShadowHMSFusedLocationProviderClient.class}) - public void shouldSendLocationToEmailRecord_Huawei() throws Exception { - ShadowOSUtils.supportsHMS(true); - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); - - OneSignalInit(); - OneSignal.setEmail("josh@onesignal.com"); - threadAndTaskWait(); - - JSONObject postEmailPayload = ShadowOneSignalRestClient.requests.get(2).payload; - assertEquals(11, postEmailPayload.getInt("device_type")); - assertEquals(1.0, postEmailPayload.getDouble("lat")); - assertEquals(2.0, postEmailPayload.getDouble("long")); - assertEquals(3.0, postEmailPayload.getDouble("loc_acc")); - assertEquals(0.0, postEmailPayload.getDouble("loc_type")); - } - - @Test - @Config(shadows = {ShadowHMSFusedLocationProviderClient.class}) - public void shouldSendLocationToSMSRecord_Huawei() throws Exception { - ShadowOSUtils.supportsHMS(true); - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); - - OneSignalInit(); - OneSignal.setSMSNumber("123456789"); - threadAndTaskWait(); - - JSONObject postEmailPayload = ShadowOneSignalRestClient.requests.get(2).payload; - assertEquals(UserState.DEVICE_TYPE_SMS, postEmailPayload.getInt("device_type")); - assertEquals(1.0, postEmailPayload.getDouble("lat")); - assertEquals(2.0, postEmailPayload.getDouble("long")); - assertEquals(3.0, postEmailPayload.getDouble("loc_acc")); - assertEquals(0.0, postEmailPayload.getDouble("loc_type")); - } - - @Test - @Config(shadows = {ShadowHMSFusedLocationProviderClient.class, ShadowHuaweiTask.class}) - public void shouldRegisterWhenPromptingAfterInit_Huawei() throws Exception { - ShadowOSUtils.supportsHMS(true); - ShadowHMSFusedLocationProviderClient.skipOnGetLocation = true; - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); - - // Test promptLocation right after init race condition - OneSignalInit(); - OneSignal.promptLocation(); - - ShadowHuaweiTask.callSuccessListener(ShadowHMSFusedLocationProviderClient.getLocation()); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request request = ShadowOneSignalRestClient.requests.get(1); - assertEquals(REST_METHOD.POST, request.method); - assertEquals(UserState.DEVICE_TYPE_HUAWEI, request.payload.get("device_type")); - assertEquals(ShadowHmsInstanceId.token, request.payload.get("identifier")); - } - - @Test - @Config(shadows = {ShadowHMSFusedLocationProviderClient.class, ShadowHuaweiTask.class}) - public void shouldCallOnSessionEvenIfSyncJobStarted_Huawei() throws Exception { - ShadowOSUtils.supportsHMS(true); - ShadowHMSFusedLocationProviderClient.shadowTask = true; - ShadowHuaweiTask.result = ShadowHMSFusedLocationProviderClient.getLocation(); - ShadowApplication.getInstance().grantPermissions("android.permission.ACCESS_COARSE_LOCATION"); - - OneSignalInit(); - threadAndTaskWait(); - - restartAppAndElapseTimeToNextSession(time); - ShadowHMSFusedLocationProviderClient.skipOnGetLocation = true; - OneSignalInit(); - - SyncJobService syncJobService = Robolectric.buildService(SyncJobService.class).create().get(); - syncJobService.onStartJob(null); - TestHelpers.getThreadByName("OS_SYNCSRV_BG_SYNC").join(); - OneSignalPackagePrivateHelper.runAllNetworkRunnables(); - ShadowHuaweiTask.callSuccessListener(ShadowHMSFusedLocationProviderClient.getLocation()); - threadAndTaskWait(); - - ShadowOneSignalRestClient.Request request = ShadowOneSignalRestClient.requests.get(3); - assertEquals(REST_METHOD.POST, request.method); - assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_session", request.url); - } - // ####### Unit test postNotification ##### private static JSONObject postNotificationSuccess = null, postNotificationFailure = null; From 966005a6c349f2b1677cf3d5c94e49bd4aec5cc1 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Tue, 2 Feb 2021 13:13:32 -0300 Subject: [PATCH 18/23] Add OSSMSSubscriptionStateChanges Observer tests * Add SMS Observer tests * Add DeviceState SMS data * Add DeviceState SMS tests --- .../java/com/onesignal/OSDeviceState.java | 24 +- ...MSSubscriptionChangedInternalObserver.java | 12 +- .../main/java/com/onesignal/OneSignal.java | 6 +- .../onesignal/MainOneSignalClassRunner.java | 316 +++++++++++++----- 4 files changed, 261 insertions(+), 97 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSDeviceState.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSDeviceState.java index 83432171d8..d7089b35c1 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSDeviceState.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSDeviceState.java @@ -35,12 +35,16 @@ public class OSDeviceState { private final boolean pushDisabled; private final boolean subscribed; private final boolean emailSubscribed; + private final boolean smsSubscribed; private final String userId; private final String pushToken; private final String emailUserId; private final String emailAddress; + private final String smsUserId; + private final String smsNumber; - OSDeviceState(OSSubscriptionState subscriptionStatus, OSPermissionState permissionStatus, OSEmailSubscriptionState emailSubscriptionStatus) { + OSDeviceState(OSSubscriptionState subscriptionStatus, OSPermissionState permissionStatus, + OSEmailSubscriptionState emailSubscriptionStatus, OSSMSSubscriptionState smsSubscriptionState) { areNotificationsEnabled = permissionStatus.areNotificationsEnabled(); pushDisabled = subscriptionStatus.isPushDisabled(); subscribed = subscriptionStatus.isSubscribed(); @@ -49,6 +53,9 @@ public class OSDeviceState { emailUserId = emailSubscriptionStatus.getEmailUserId(); emailAddress = emailSubscriptionStatus.getEmailAddress(); emailSubscribed = emailSubscriptionStatus.isSubscribed(); + smsUserId = smsSubscriptionState.getSmsUserId(); + smsNumber = smsSubscriptionState.getSMSNumber(); + smsSubscribed = smsSubscriptionState.isSubscribed(); } /** @@ -123,6 +130,18 @@ public String getEmailAddress() { return emailAddress; } + public boolean isSMSSubscribed() { + return smsSubscribed; + } + + public String getSMSUserId() { + return smsUserId; + } + + public String getSMSNumber() { + return smsNumber; + } + public JSONObject toJSONObject() { JSONObject mainObj = new JSONObject(); @@ -135,6 +154,9 @@ public JSONObject toJSONObject() { mainObj.put("isEmailSubscribed", emailSubscribed); mainObj.put("emailUserId", emailUserId); mainObj.put("emailAddress", emailAddress); + mainObj.put("isSMSSubscribed", smsSubscribed); + mainObj.put("smsUserId", smsUserId); + mainObj.put("smsNumber", smsNumber); } catch (Throwable t) { t.printStackTrace(); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionChangedInternalObserver.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionChangedInternalObserver.java index d34ea487f0..4851a93bb8 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionChangedInternalObserver.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSSMSSubscriptionChangedInternalObserver.java @@ -1,21 +1,21 @@ /** * Modified MIT License - * + *

* Copyright 2021 OneSignal - * + *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + *

* 1. The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + *

* 2. All copies of substantial portions of the Software may only be used in connection * with services provided by OneSignal. - * + *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -43,7 +43,7 @@ static void fireChangesToPublicObserver(OSSMSSubscriptionState state) { boolean hasReceiver = OneSignal.getSMSSubscriptionStateChangesObserver().notifyChange(stateChanges); if (hasReceiver) { - OneSignal.lastSMSSubscriptionState = (OSSMSSubscriptionState)state.clone(); + OneSignal.lastSMSSubscriptionState = (OSSMSSubscriptionState) state.clone(); OneSignal.lastSMSSubscriptionState.persistAsFrom(); } } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java index 63e0ded346..3e2be9ca51 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java @@ -598,7 +598,7 @@ private static OSSMSSubscriptionState getLastSMSSubscriptionState(Context contex private static OSObservable smsSubscriptionStateChangesObserver; static OSObservable getSMSSubscriptionStateChangesObserver() { if (smsSubscriptionStateChangesObserver == null) - smsSubscriptionStateChangesObserver = new OSObservable<>("onOSSMSSubscriptionChanged", true); + smsSubscriptionStateChangesObserver = new OSObservable<>("onSMSSubscriptionChanged", true); return smsSubscriptionStateChangesObserver; } // End SMSSubscriptionState @@ -616,7 +616,8 @@ public static OSDeviceState getDeviceState() { OSSubscriptionState subscriptionStatus = getCurrentSubscriptionState(appContext); OSPermissionState permissionStatus = getCurrentPermissionState(appContext); OSEmailSubscriptionState emailSubscriptionStatus = getCurrentEmailSubscriptionState(appContext); - return new OSDeviceState(subscriptionStatus, permissionStatus, emailSubscriptionStatus); + OSSMSSubscriptionState smsSubscriptionStatus = getCurrentSMSSubscriptionState(appContext); + return new OSDeviceState(subscriptionStatus, permissionStatus, emailSubscriptionStatus, smsSubscriptionStatus); } private static class IAPUpdateJob { @@ -2970,7 +2971,6 @@ public static void removeEmailSubscriptionObserver(@NonNull OSEmailSubscriptionO * @param observer the instance of {@link OSSubscriptionObserver} that acts as the observer */ public static void addSMSSubscriptionObserver(@NonNull OSSMSSubscriptionObserver observer) { - if (appContext == null) { logger.error("OneSignal.initWithContext has not been called. Could not add sms subscription observer"); return; diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java index a27d8982bc..ea2a606403 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java @@ -57,6 +57,8 @@ import com.onesignal.OSNotificationReceivedEvent; import com.onesignal.OSPermissionObserver; import com.onesignal.OSPermissionStateChanges; +import com.onesignal.OSSMSSubscriptionObserver; +import com.onesignal.OSSMSSubscriptionStateChanges; import com.onesignal.OSSubscriptionObserver; import com.onesignal.OSSubscriptionStateChanges; import com.onesignal.OneSignal; @@ -131,6 +133,7 @@ import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTrackerFactory; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_taskQueueWaitingForInit; import static com.onesignal.ShadowOneSignalRestClient.REST_METHOD; +import static com.onesignal.ShadowOneSignalRestClient.SMS_USER_ID; import static com.onesignal.ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse; import static com.test.onesignal.GenerateNotificationRunner.getBaseNotifBundle; import static com.test.onesignal.RestClientAsserts.assertAmazonPlayerCreateAtIndex; @@ -186,6 +189,7 @@ public class MainOneSignalClassRunner { private static final String ONESIGNAL_APP_ID = "b4f7f966-d8cc-11e4-bed1-df8f05be55ba"; private static final String ONESIGNAL_NOTIFICATION_ID = "97d8e764-81c2-49b0-a644-713d052ae7d5"; + private static final String ONESIGNAL_SMS_NUMBER = "123456789"; private static final String TIMEZONE_ID = "Europe/London"; @SuppressLint("StaticFieldLeak") @@ -212,9 +216,14 @@ private static void getGetTagsHandler() { OneSignal.getTags(tags -> lastGetTags = tags); } + private static OSEmailSubscriptionStateChanges lastEmailSubscriptionStateChanges; + private static OSSMSSubscriptionStateChanges lastSMSSubscriptionStateChanges; + private static void cleanUp() throws Exception { lastNotificationOpenedBody = null; lastGetTags = null; + lastEmailSubscriptionStateChanges = null; + lastSMSSubscriptionStateChanges = null; ShadowGMSLocationController.reset(); @@ -2897,16 +2906,44 @@ public void onOSEmailSubscriptionChanged(OSEmailSubscriptionStateChanges stateCh OneSignal.provideUserConsent(true); threadAndTaskWait(); - OneSignal.setEmail("josh@onesignal.com"); + String email = "josh@onesignal.com"; + OneSignal.setEmail(email); threadAndTaskWait(); // make sure the email subscription observer was fired + assertEquals(email, lastEmailSubscriptionStateChanges.getFrom().getEmailAddress()); assertNull(lastEmailSubscriptionStateChanges.getFrom().getEmailUserId()); assertEquals("b007f967-98cc-11e4-bed1-118f05be4522", lastEmailSubscriptionStateChanges.getTo().getEmailUserId()); - assertEquals("josh@onesignal.com", lastEmailSubscriptionStateChanges.getTo().getEmailAddress()); + assertEquals(email, lastEmailSubscriptionStateChanges.getTo().getEmailAddress()); assertTrue(lastEmailSubscriptionStateChanges.getTo().isSubscribed()); } + @Test + public void shouldAddSMSSubscriptionObserverIfConsentNotGranted() throws Exception { + ShadowOneSignalRestClient.setRemoteParamsRequirePrivacyConsent(true); + OneSignalInit(); + threadAndTaskWait(); + + OSSMSSubscriptionObserver subscriptionObserver = stateChanges -> lastSMSSubscriptionStateChanges = stateChanges; + OneSignal.addSMSSubscriptionObserver(subscriptionObserver); + + OneSignal.provideUserConsent(true); + threadAndTaskWait(); + + assertNull(lastSMSSubscriptionStateChanges); + + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + // make sure the sms subscription observer was fired + assertEquals(ONESIGNAL_SMS_NUMBER, lastSMSSubscriptionStateChanges.getFrom().getSMSNumber()); + assertNull(lastSMSSubscriptionStateChanges.getFrom().getSmsUserId()); + assertFalse(lastSMSSubscriptionStateChanges.getFrom().isSubscribed()); + assertEquals(ShadowOneSignalRestClient.smsUserId, lastSMSSubscriptionStateChanges.getTo().getSmsUserId()); + assertEquals(ONESIGNAL_SMS_NUMBER, lastSMSSubscriptionStateChanges.getTo().getSMSNumber()); + assertTrue(lastSMSSubscriptionStateChanges.getTo().isSubscribed()); + } + /* // Can't get test to work from a app flow due to the main thread being locked one way or another in a robolectric env. // Running ActivityLifecycleListener.focusHandlerThread...advanceToNextPostedRunnable waits on the main thread. @@ -3267,8 +3304,6 @@ public void onOSSubscriptionChanged(OSSubscriptionStateChanges stateChanges) { assertNull(lastSubscriptionStateChanges); } - private OSEmailSubscriptionStateChanges lastEmailSubscriptionStateChanges; - @Test public void shouldFireEmailSubscriptionObserverOnSetEmail() throws Exception { OneSignalInit(); @@ -3288,6 +3323,20 @@ public void onOSEmailSubscriptionChanged(OSEmailSubscriptionStateChanges stateCh assertTrue(lastEmailSubscriptionStateChanges.getTo().isSubscribed()); } + @Test + public void shouldFireSMSSubscriptionObserverOnSetSMS() throws Exception { + OneSignalInit(); + OSSMSSubscriptionObserver subscriptionObserver = stateChanges -> lastSMSSubscriptionStateChanges = stateChanges; + OneSignal.addSMSSubscriptionObserver(subscriptionObserver); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + assertNull(lastSMSSubscriptionStateChanges.getFrom().getSmsUserId()); + assertEquals(SMS_USER_ID, lastSMSSubscriptionStateChanges.getTo().getSmsUserId()); + assertEquals(ONESIGNAL_SMS_NUMBER, lastSMSSubscriptionStateChanges.getTo().getSMSNumber()); + assertTrue(lastSMSSubscriptionStateChanges.getTo().isSubscribed()); + } + @Test public void shouldFireEmailSubscriptionObserverOnLogoutEmail() throws Exception { OneSignalInit(); @@ -3339,6 +3388,28 @@ public void onOSEmailSubscriptionChanged(OSEmailSubscriptionStateChanges stateCh assertNull(lastEmailSubscriptionStateChanges); } + @Test + public void shouldNotFireSMSSubscriptionObserverOnAppRestart() throws Exception { + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + OSSMSSubscriptionObserver subscriptionObserver = stateChanges -> lastSMSSubscriptionStateChanges = stateChanges; + OneSignal.addSMSSubscriptionObserver(subscriptionObserver); + threadAndTaskWait(); + assertNotNull(lastSMSSubscriptionStateChanges); + + restartAppAndElapseTimeToNextSession(time); + lastSMSSubscriptionStateChanges = null; + + OneSignalInit(); + threadAndTaskWait(); + OneSignal.addSMSSubscriptionObserver(subscriptionObserver); + threadAndTaskWait(); + + assertNull(lastSMSSubscriptionStateChanges); + } + @Test public void shouldGetCorrectCurrentEmailSubscriptionState() throws Exception { OneSignalInit(); @@ -3358,6 +3429,25 @@ public void shouldGetCorrectCurrentEmailSubscriptionState() throws Exception { assertTrue(deviceState.isEmailSubscribed()); } + @Test + public void shouldGetCorrectCurrentSMSSubscriptionState() throws Exception { + OneSignalInit(); + OSDeviceState deviceState = OneSignal.getDeviceState(); + + assertNotNull(deviceState); + assertNull(deviceState.getSMSUserId()); + assertNull(deviceState.getSMSNumber()); + assertFalse(deviceState.isSMSSubscribed()); + + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + deviceState = OneSignal.getDeviceState(); + + assertEquals(SMS_USER_ID, deviceState.getSMSUserId()); + assertEquals(ONESIGNAL_SMS_NUMBER, deviceState.getSMSNumber()); + assertTrue(deviceState.isSMSSubscribed()); + } + @Test public void shouldGetEmailUserIdAfterAppRestart() throws Exception { OneSignalInit(); @@ -3373,106 +3463,70 @@ public void shouldGetEmailUserIdAfterAppRestart() throws Exception { } @Test - public void shouldReturnCorrectGetPermissionSubscriptionState() throws Exception { + public void shouldGetSMSUserIdAfterAppRestart() throws Exception { OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); threadAndTaskWait(); + + restartAppAndElapseTimeToNextSession(time); + + OneSignalInit(); OSDeviceState deviceState = OneSignal.getDeviceState(); - assertTrue(deviceState.areNotificationsEnabled()); - assertTrue(deviceState.isSubscribed()); + assertEquals(ONESIGNAL_SMS_NUMBER, deviceState.getSMSNumber()); + assertNotNull(deviceState.getSMSUserId()); } @Test - public void shouldSendPurchases() throws Exception { + public void shouldReturnCorrectGetPermissionSubscriptionState() throws Exception { OneSignalInit(); - OneSignal.setEmail("josh@onesignal.com"); threadAndTaskWait(); - - JSONObject purchase = new JSONObject(); - purchase.put("sku", "com.test.sku"); - JSONArray purchases = new JSONArray(); - purchases.put(purchase); - - OneSignalPackagePrivateHelper.OneSignal_sendPurchases(purchases, false, null); - threadAndTaskWait(); - - String expectedPayload = "{\"app_id\":\"b4f7f966-d8cc-11e4-bed1-df8f05be55ba\",\"purchases\":[{\"sku\":\"com.test.sku\"}]}"; - ShadowOneSignalRestClient.Request pushPurchase = ShadowOneSignalRestClient.requests.get(4); - assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_purchase", pushPurchase.url); - assertEquals(expectedPayload, pushPurchase.payload.toString()); - - ShadowOneSignalRestClient.Request emailPurchase = ShadowOneSignalRestClient.requests.get(5); - assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522/on_purchase", emailPurchase.url); - assertEquals(expectedPayload, emailPurchase.payload.toString()); + OSDeviceState deviceState = OneSignal.getDeviceState(); + assertTrue(deviceState.areNotificationsEnabled()); + assertTrue(deviceState.isSubscribed()); } @Test - @Config(shadows = { ShadowFirebaseAnalytics.class }) - public void shouldSendFirebaseAnalyticsNotificationOpen() throws Exception { - ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse(new JSONObject().put("fba", true)); + public void testDeviceStateHasEmailAddress() throws Exception { + String testEmail = "test@onesignal.com"; + + assertNull(OneSignal.getDeviceState()); + OneSignalInit(); threadAndTaskWait(); - JSONObject openPayload = new JSONObject(); - openPayload.put("title", "Test title"); - openPayload.put("alert", "Test Msg"); - openPayload.put("custom", new JSONObject("{ \"i\": \"UUID\" }")); - OneSignal_handleNotificationOpen(blankActivity, new JSONArray().put(openPayload), false, ONESIGNAL_NOTIFICATION_ID); + OSDeviceState device = OneSignal.getDeviceState(); + assertNull(device.getEmailAddress()); - assertEquals("os_notification_opened", ShadowFirebaseAnalytics.lastEventString); - Bundle expectedBundle = new Bundle(); - expectedBundle.putString("notification_id", "UUID"); - expectedBundle.putString("medium", "notification"); - expectedBundle.putString("source", "OneSignal"); - expectedBundle.putString("campaign", "Test title"); - assertEquals(expectedBundle.toString(), ShadowFirebaseAnalytics.lastEventBundle.toString()); + OneSignal.setEmail(testEmail); + threadAndTaskWait(); - // Assert that another open isn't trigger later when the unprocessed opens are fired - ShadowFirebaseAnalytics.lastEventString = null; - OneSignal.setAppId(ONESIGNAL_APP_ID); - OneSignal.initWithContext(blankActivity); - OneSignal.setNotificationOpenedHandler(getNotificationOpenedHandler()); - assertNull(ShadowFirebaseAnalytics.lastEventString); + // Device is a snapshot, last value should not change + assertNull(device.getEmailAddress()); + // Retrieve new user device + assertEquals(testEmail, OneSignal.getDeviceState().getEmailAddress()); } @Test - @Config(shadows = { ShadowFirebaseAnalytics.class, ShadowGenerateNotification.class }) - public void shouldSendFirebaseAnalyticsNotificationReceived() throws Exception { - ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse(new JSONObject().put("fba", true)); + public void testDeviceStateHasSMSAddress() throws Exception { + assertNull(OneSignal.getDeviceState()); + OneSignalInit(); threadAndTaskWait(); - JSONObject openPayload = new JSONObject(); - openPayload.put("title", "Test title"); - openPayload.put("alert", "Test Msg"); - openPayload.put("custom", new JSONObject("{ \"i\": \"UUID\" }")); - NotificationBundleProcessor_Process(blankActivity, false, openPayload); - - assertEquals("os_notification_received", ShadowFirebaseAnalytics.lastEventString); - Bundle expectedBundle = new Bundle(); - expectedBundle.putString("notification_id", "UUID"); - expectedBundle.putString("medium", "notification"); - expectedBundle.putString("source", "OneSignal"); - expectedBundle.putString("campaign", "Test title"); - assertEquals(expectedBundle.toString(), ShadowFirebaseAnalytics.lastEventBundle.toString()); - - // Assert that another receive isn't trigger later when the unprocessed receives are fired - OneSignal.setAppId(ONESIGNAL_APP_ID); - OneSignal.initWithContext(blankActivity); - OneSignal.setNotificationWillShowInForegroundHandler(notificationReceivedEvent -> { + OSDeviceState device = OneSignal.getDeviceState(); + assertNull(device.getSMSNumber()); - }); - OneSignal.setNotificationOpenedHandler(getNotificationOpenedHandler()); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); threadAndTaskWait(); - ShadowFirebaseAnalytics.lastEventString = null; - OneSignal.setAppId(ONESIGNAL_APP_ID); - OneSignal.initWithContext(blankActivity); - OneSignal.setNotificationOpenedHandler(getNotificationOpenedHandler()); - assertNull(ShadowFirebaseAnalytics.lastEventString); + // Device is a snapshot, last value should not change + assertNull(device.getSMSNumber()); + // Retrieve new user device + assertEquals(ONESIGNAL_SMS_NUMBER, OneSignal.getDeviceState().getSMSNumber()); } @Test - public void testDeviceStateHasEmailAddress() throws Exception { + public void testDeviceStateHasEmailId() throws Exception { String testEmail = "test@onesignal.com"; assertNull(OneSignal.getDeviceState()); @@ -3481,36 +3535,34 @@ public void testDeviceStateHasEmailAddress() throws Exception { threadAndTaskWait(); OSDeviceState device = OneSignal.getDeviceState(); - assertNull(device.getEmailAddress()); + assertNull(device.getEmailUserId()); OneSignal.setEmail(testEmail); threadAndTaskWait(); // Device is a snapshot, last value should not change - assertNull(device.getEmailAddress()); + assertNull(device.getEmailUserId()); // Retrieve new user device - assertEquals(testEmail, OneSignal.getDeviceState().getEmailAddress()); + assertNotNull(OneSignal.getDeviceState().getEmailUserId()); } @Test - public void testDeviceStateHasEmailId() throws Exception { - String testEmail = "test@onesignal.com"; - + public void testDeviceStateHasSMSId() throws Exception { assertNull(OneSignal.getDeviceState()); OneSignalInit(); threadAndTaskWait(); OSDeviceState device = OneSignal.getDeviceState(); - assertNull(device.getEmailUserId()); + assertNull(device.getSMSUserId()); - OneSignal.setEmail(testEmail); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); threadAndTaskWait(); // Device is a snapshot, last value should not change - assertNull(device.getEmailUserId()); + assertNull(device.getSMSUserId()); // Retrieve new user device - assertNotNull(OneSignal.getDeviceState().getEmailUserId()); + assertEquals(SMS_USER_ID, OneSignal.getDeviceState().getSMSUserId()); } @Test @@ -3589,6 +3641,96 @@ public void testDeviceStateIsSubscribed() throws Exception { assertFalse(OneSignal.getDeviceState().isSubscribed()); } + @Test + public void shouldSendPurchases() throws Exception { + OneSignalInit(); + OneSignal.setEmail("josh@onesignal.com"); + threadAndTaskWait(); + + JSONObject purchase = new JSONObject(); + purchase.put("sku", "com.test.sku"); + JSONArray purchases = new JSONArray(); + purchases.put(purchase); + + OneSignalPackagePrivateHelper.OneSignal_sendPurchases(purchases, false, null); + threadAndTaskWait(); + + String expectedPayload = "{\"app_id\":\"b4f7f966-d8cc-11e4-bed1-df8f05be55ba\",\"purchases\":[{\"sku\":\"com.test.sku\"}]}"; + ShadowOneSignalRestClient.Request pushPurchase = ShadowOneSignalRestClient.requests.get(4); + assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_purchase", pushPurchase.url); + assertEquals(expectedPayload, pushPurchase.payload.toString()); + + ShadowOneSignalRestClient.Request emailPurchase = ShadowOneSignalRestClient.requests.get(5); + assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522/on_purchase", emailPurchase.url); + assertEquals(expectedPayload, emailPurchase.payload.toString()); + } + + @Test + @Config(shadows = { ShadowFirebaseAnalytics.class }) + public void shouldSendFirebaseAnalyticsNotificationOpen() throws Exception { + ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse(new JSONObject().put("fba", true)); + OneSignalInit(); + threadAndTaskWait(); + + JSONObject openPayload = new JSONObject(); + openPayload.put("title", "Test title"); + openPayload.put("alert", "Test Msg"); + openPayload.put("custom", new JSONObject("{ \"i\": \"UUID\" }")); + OneSignal_handleNotificationOpen(blankActivity, new JSONArray().put(openPayload), false, ONESIGNAL_NOTIFICATION_ID); + + assertEquals("os_notification_opened", ShadowFirebaseAnalytics.lastEventString); + Bundle expectedBundle = new Bundle(); + expectedBundle.putString("notification_id", "UUID"); + expectedBundle.putString("medium", "notification"); + expectedBundle.putString("source", "OneSignal"); + expectedBundle.putString("campaign", "Test title"); + assertEquals(expectedBundle.toString(), ShadowFirebaseAnalytics.lastEventBundle.toString()); + + // Assert that another open isn't trigger later when the unprocessed opens are fired + ShadowFirebaseAnalytics.lastEventString = null; + OneSignal.setAppId(ONESIGNAL_APP_ID); + OneSignal.initWithContext(blankActivity); + OneSignal.setNotificationOpenedHandler(getNotificationOpenedHandler()); + assertNull(ShadowFirebaseAnalytics.lastEventString); + } + + @Test + @Config(shadows = { ShadowFirebaseAnalytics.class, ShadowGenerateNotification.class }) + public void shouldSendFirebaseAnalyticsNotificationReceived() throws Exception { + ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse(new JSONObject().put("fba", true)); + OneSignalInit(); + threadAndTaskWait(); + + JSONObject openPayload = new JSONObject(); + openPayload.put("title", "Test title"); + openPayload.put("alert", "Test Msg"); + openPayload.put("custom", new JSONObject("{ \"i\": \"UUID\" }")); + NotificationBundleProcessor_Process(blankActivity, false, openPayload); + + assertEquals("os_notification_received", ShadowFirebaseAnalytics.lastEventString); + Bundle expectedBundle = new Bundle(); + expectedBundle.putString("notification_id", "UUID"); + expectedBundle.putString("medium", "notification"); + expectedBundle.putString("source", "OneSignal"); + expectedBundle.putString("campaign", "Test title"); + assertEquals(expectedBundle.toString(), ShadowFirebaseAnalytics.lastEventBundle.toString()); + + // Assert that another receive isn't trigger later when the unprocessed receives are fired + OneSignal.setAppId(ONESIGNAL_APP_ID); + OneSignal.initWithContext(blankActivity); + OneSignal.setNotificationWillShowInForegroundHandler(notificationReceivedEvent -> { + + }); + OneSignal.setNotificationOpenedHandler(getNotificationOpenedHandler()); + threadAndTaskWait(); + + ShadowFirebaseAnalytics.lastEventString = null; + OneSignal.setAppId(ONESIGNAL_APP_ID); + OneSignal.initWithContext(blankActivity); + OneSignal.setNotificationOpenedHandler(getNotificationOpenedHandler()); + assertNull(ShadowFirebaseAnalytics.lastEventString); + } + @Test public void testGetTagsQueuesCallbacks() throws Exception { final BlockingQueue queue = new ArrayBlockingQueue<>(2); @@ -3703,7 +3845,7 @@ private static OSNotification createTestOSNotification() throws Exception { } private void OneSignalInit() { - OneSignal.setLogLevel(OneSignal.LOG_LEVEL.VERBOSE, OneSignal.LOG_LEVEL.NONE); +// OneSignal.setLogLevel(OneSignal.LOG_LEVEL.VERBOSE, OneSignal.LOG_LEVEL.NONE); ShadowOSUtils.subscribableStatus = 1; OneSignal_setTime(time); OneSignal_setTrackerFactory(trackerFactory); From 383161e27e9bb9faca342b4f18b8cf3e2345a46a Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Tue, 2 Feb 2021 15:03:55 -0300 Subject: [PATCH 19/23] Add purchase request for SMS * Call on_purchase on all channels * Update on_purchase test to include SMS --- .../src/main/java/com/onesignal/OneSignal.java | 4 +--- .../onesignal/OneSignalStateSynchronizer.java | 7 +++++++ .../com/onesignal/UserStateSynchronizer.java | 5 +++++ .../onesignal/MainOneSignalClassRunner.java | 17 ++++++++++++----- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java index 3e2be9ca51..d15e9244d4 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java @@ -2100,9 +2100,7 @@ static void sendPurchases(JSONArray purchases, boolean newAsExisting, OneSignalR jsonBody.put("existing", true); jsonBody.put("purchases", purchases); - OneSignalRestClient.post("players/" + getUserId() + "/on_purchase", jsonBody, responseHandler); - if (getEmailId() != null) - OneSignalRestClient.post("players/" + getEmailId() + "/on_purchase", jsonBody, null); + OneSignalStateSynchronizer.sendPurchases(jsonBody, responseHandler); } catch (Throwable t) { Log(LOG_LEVEL.ERROR, "Failed to generate JSON for sendPurchases.", t); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java index 4a7bc09ba7..e19c6e4e52 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java @@ -281,6 +281,13 @@ public void run() { } } + static void sendPurchases(JSONObject jsonBody, OneSignalRestClient.ResponseHandler responseHandler) { + List userStateSynchronizers = getUserStateSynchronizers(); + for (UserStateSynchronizer userStateSynchronizer : userStateSynchronizers) { + userStateSynchronizer.sendPurchases(jsonBody, responseHandler); + } + } + // This is to indicate that StateSynchronizer can start making REST API calls // We do this to roll up as many field updates in a single create / on_session call to // optimize the number of api calls that are made diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java index 64af6b5bca..615a9447b3 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java @@ -8,6 +8,7 @@ import com.onesignal.OneSignal.SendTagsError; import com.onesignal.OneSignalStateSynchronizer.UserStateSynchronizerType; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -574,6 +575,10 @@ void updateLocation(LocationController.LocationPoint point) { abstract void logoutSMS(); + void sendPurchases(JSONObject jsonBody, OneSignalRestClient.ResponseHandler responseHandler) { + OneSignalRestClient.post("players/" + getId() + "/on_purchase", jsonBody, responseHandler); + } + void readyToUpdate(boolean canMakeUpdates) { boolean changed = this.canMakeUpdates != canMakeUpdates; this.canMakeUpdates = canMakeUpdates; diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java index ea2a606403..15abd1b943 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java @@ -132,6 +132,8 @@ import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTime; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTrackerFactory; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_taskQueueWaitingForInit; +import static com.onesignal.ShadowOneSignalRestClient.EMAIL_USER_ID; +import static com.onesignal.ShadowOneSignalRestClient.PUSH_USER_ID; import static com.onesignal.ShadowOneSignalRestClient.REST_METHOD; import static com.onesignal.ShadowOneSignalRestClient.SMS_USER_ID; import static com.onesignal.ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse; @@ -3645,6 +3647,7 @@ public void testDeviceStateIsSubscribed() throws Exception { public void shouldSendPurchases() throws Exception { OneSignalInit(); OneSignal.setEmail("josh@onesignal.com"); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); threadAndTaskWait(); JSONObject purchase = new JSONObject(); @@ -3656,13 +3659,17 @@ public void shouldSendPurchases() throws Exception { threadAndTaskWait(); String expectedPayload = "{\"app_id\":\"b4f7f966-d8cc-11e4-bed1-df8f05be55ba\",\"purchases\":[{\"sku\":\"com.test.sku\"}]}"; - ShadowOneSignalRestClient.Request pushPurchase = ShadowOneSignalRestClient.requests.get(4); - assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_purchase", pushPurchase.url); + ShadowOneSignalRestClient.Request pushPurchase = ShadowOneSignalRestClient.requests.get(5); + assertEquals("players/" + PUSH_USER_ID + "/on_purchase", pushPurchase.url); assertEquals(expectedPayload, pushPurchase.payload.toString()); - ShadowOneSignalRestClient.Request emailPurchase = ShadowOneSignalRestClient.requests.get(5); - assertEquals("players/b007f967-98cc-11e4-bed1-118f05be4522/on_purchase", emailPurchase.url); + ShadowOneSignalRestClient.Request emailPurchase = ShadowOneSignalRestClient.requests.get(6); + assertEquals("players/" + EMAIL_USER_ID + "/on_purchase", emailPurchase.url); assertEquals(expectedPayload, emailPurchase.payload.toString()); + + ShadowOneSignalRestClient.Request smsPurchase = ShadowOneSignalRestClient.requests.get(7); + assertEquals("players/" + SMS_USER_ID + "/on_purchase", smsPurchase.url); + assertEquals(expectedPayload, smsPurchase.payload.toString()); } @Test @@ -3845,7 +3852,7 @@ private static OSNotification createTestOSNotification() throws Exception { } private void OneSignalInit() { -// OneSignal.setLogLevel(OneSignal.LOG_LEVEL.VERBOSE, OneSignal.LOG_LEVEL.NONE); + OneSignal.setLogLevel(OneSignal.LOG_LEVEL.VERBOSE, OneSignal.LOG_LEVEL.NONE); ShadowOSUtils.subscribableStatus = 1; OneSignal_setTime(time); OneSignal_setTrackerFactory(trackerFactory); From 015632e49af6b986d24f42d7fa0f4162c93cd970 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Thu, 11 Feb 2021 13:14:01 -0300 Subject: [PATCH 20/23] Add external id and SMS auth hash tests * Add tests for combination cases between external id and SMS auth hash --- .../SynchronizerIntegrationTests.java | 129 +++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java index 4a73adf77f..db6d160652 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java @@ -426,6 +426,28 @@ public void shouldSendTagsToSMSAfterCreate() throws Exception { assertEquals(tagsJson.toString(), smsPut.payload.getJSONObject("tags").toString()); } + @Test + public void shouldSendTagsToSMSWithHashToken() throws Exception { + String mockSMSHash = new String(new char[64]).replace('\0', '0'); + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockSMSHash); + threadAndTaskWait(); + + JSONObject tagsJson = new JSONObject("{\"test1\": \"value1\", \"test2\": \"value2\"}"); + OneSignal.sendTags(tagsJson); + threadAndTaskWait(); + + assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request smsPut = ShadowOneSignalRestClient.requests.get(5); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, smsPut.method); + assertEquals("players/" + ShadowOneSignalRestClient.smsUserId, smsPut.url); + assertEquals(3, smsPut.payload.length()); + assertTrue(smsPut.payload.has("app_id")); + assertEquals(tagsJson.toString(), smsPut.payload.getJSONObject("tags").toString()); + assertEquals(mockSMSHash, smsPut.payload.getString("sms_auth_hash")); + } + @Test public void shouldSendTagsToEmailAndSMSAfterCreate() throws Exception { OneSignalInit(); @@ -1015,7 +1037,7 @@ public void shouldAlwaysSetExternalIdAndSMSWithAuthHashAfterRegistration() throw String testExternalId = "test_ext_id"; String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); - String mockSMSlHash = new String(new char[64]).replace('\0', '0'); + String mockSMSlHash = new String(new char[64]).replace('\0', '1'); OneSignal.setExternalUserId(testExternalId, mockExternalIdHash); OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockSMSlHash); @@ -1048,6 +1070,83 @@ public void shouldAlwaysSetExternalIdAndSMSWithAuthHashAfterRegistration() throw assertEquals(mockSMSlHash, smsPostAfterColdStart.payload.getString("sms_auth_hash")); } + @Test + public void shouldSetExternalIdOnSMS() throws Exception { + OneSignalInit(); + threadAndTaskWait(); + + String testExternalId = "test_ext_id"; + String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); + + String mockSMSlHash = new String(new char[64]).replace('\0', '1'); + + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockSMSlHash); + threadAndTaskWait(); + + OneSignal.setExternalUserId(testExternalId, mockExternalIdHash); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request setExternalIdOnSMSChannel = ShadowOneSignalRestClient.requests.get(6); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, setExternalIdOnSMSChannel.method); + assertEquals("players/" + ShadowOneSignalRestClient.smsUserId, setExternalIdOnSMSChannel.url); + assertEquals(testExternalId, setExternalIdOnSMSChannel.payload.get("external_user_id")); + assertEquals(mockExternalIdHash, setExternalIdOnSMSChannel.payload.get("external_user_id_auth_hash")); + assertEquals(mockSMSlHash, setExternalIdOnSMSChannel.payload.get("sms_auth_hash")); + } + + @Test + public void shouldSetExternalIdWithAuthHashOnSendTagsSMS() throws Exception { + String testExternalId = "test_ext_id"; + String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); + + String mockSMSHash = new String(new char[64]).replace('\0', '0'); + + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockSMSHash); + threadAndTaskWait(); + + OneSignal.setExternalUserId(testExternalId, mockExternalIdHash); + threadAndTaskWait(); + + JSONObject tagsJson = new JSONObject("{\"test1\": \"value1\", \"test2\": \"value2\"}"); + OneSignal.sendTags(tagsJson); + threadAndTaskWait(); + + assertEquals(8, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request smsPut = ShadowOneSignalRestClient.requests.get(7); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, smsPut.method); + assertEquals("players/" + ShadowOneSignalRestClient.smsUserId, smsPut.url); + assertEquals(4, smsPut.payload.length()); + assertTrue(smsPut.payload.has("app_id")); + assertEquals(tagsJson.toString(), smsPut.payload.getJSONObject("tags").toString()); + assertEquals(mockSMSHash, smsPut.payload.getString("sms_auth_hash")); + assertEquals(mockExternalIdHash, smsPut.payload.getString("external_user_id_auth_hash")); + } + + @Test + public void shouldNotSetExternalIdOnSMShWithLaterSMSNumber() throws Exception { + OneSignalInit(); + threadAndTaskWait(); + + String testExternalId = "test_ext_id"; + String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); + + String mockSMSlHash = new String(new char[64]).replace('\0', '1'); + + OneSignal.setExternalUserId(testExternalId, mockExternalIdHash); + threadAndTaskWait(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockSMSlHash); + threadAndTaskWait(); + + ShadowOneSignalRestClient.Request setSMSNumberSMSChannelRequest = ShadowOneSignalRestClient.requests.get(4); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, setSMSNumberSMSChannelRequest.method); + assertTrue(setSMSNumberSMSChannelRequest.payload.has("identifier")); + assertTrue(setSMSNumberSMSChannelRequest.payload.has("sms_auth_hash")); + assertFalse(setSMSNumberSMSChannelRequest.payload.has("external_user_id")); + assertFalse(setSMSNumberSMSChannelRequest.payload.has("external_user_id_auth_hash")); + } + @Test public void shouldRemoveExternalUserId() throws Exception { OneSignal.setExternalUserId("test_ext_id"); @@ -1714,6 +1813,12 @@ public void shouldSendOnSessionToSMS() throws Exception { ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(6); assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); assertEquals("players/" + ShadowOneSignalRestClient.SMS_USER_ID + "/on_session", smsPost.url); + + // Check that the request only has app_id, device_type and device_player_id + assertEquals(3, smsPost.payload.length()); + assertEquals(PUSH_USER_ID, smsPost.payload.get("device_player_id")); + assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_SMS, smsPost.payload.get("device_type")); + assertTrue(smsPost.payload.has("app_id")); } // ####### on_focus Tests ######## @@ -1732,6 +1837,28 @@ public void sendsOnFocus() throws Exception { assertRestCalls(3); } + @Test + public void sendsOnFocusWithExternalId() throws Exception { + time.advanceSystemAndElapsedTimeBy(0); + OneSignalInit(); + threadAndTaskWait(); + + String testExternalId = "test_ext_id"; + String mockExternalIdHash = new String(new char[64]).replace('\0', '0'); + + OneSignal.setExternalUserId(testExternalId, mockExternalIdHash); + threadAndTaskWait(); + + time.advanceSystemAndElapsedTimeBy(60); + pauseActivity(blankActivityController); + assertAndRunSyncService(); + + assertOnFocusAtIndex(3, 60); + assertRestCalls(4); + assertFalse(ShadowOneSignalRestClient.lastPost.has("external_user_id")); + assertFalse(ShadowOneSignalRestClient.lastPost.has("external_user_id_auth_hash")); + } + @Test public void sendsOnFocusToEmail() throws Exception { time.advanceSystemAndElapsedTimeBy(0); From f2776ba7c68b98cde9b9756fbee6f6025b9ecc37 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Tue, 2 Feb 2021 16:26:59 -0300 Subject: [PATCH 21/23] Add logout SMS method * Add logoutSMS() public method * Add logout SMS functionality to UserStateSynchronizers * Remove parent player update from SMS * Update tests after removal of parent player id link from SMS * Add logoutSMS tests --- .../java/com/onesignal/OSTaskController.java | 2 + .../main/java/com/onesignal/OneSignal.java | 59 ++++- .../onesignal/OneSignalStateSynchronizer.java | 7 +- .../main/java/com/onesignal/UserState.java | 2 +- .../onesignal/UserStateEmailSynchronizer.java | 7 +- .../onesignal/UserStatePushSynchronizer.java | 22 +- .../onesignal/UserStateSMSSynchronizer.java | 6 +- ...UserStateSecondaryChannelSynchronizer.java | 5 +- .../com/onesignal/UserStateSynchronizer.java | 6 +- .../SynchronizerIntegrationTests.java | 213 ++++++++++++++---- 10 files changed, 263 insertions(+), 66 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSTaskController.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSTaskController.java index d083d20a5b..2dd6a5ceb3 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSTaskController.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSTaskController.java @@ -19,6 +19,7 @@ class OSTaskController { static final String GET_TAGS = "getTags()"; static final String SET_SMS_NUMBER = "setSMSNumber()"; static final String SET_EMAIL = "setEmail()"; + static final String LOGOUT_SMS_NUMBER = "logoutSMSNumber()"; static final String LOGOUT_EMAIL = "logoutEmail()"; static final String SYNC_HASHED_EMAIL = "syncHashedEmail()"; static final String SET_EXTERNAL_USER_ID = "setExternalUserId()"; @@ -41,6 +42,7 @@ class OSTaskController { GET_TAGS, SET_SMS_NUMBER, SET_EMAIL, + LOGOUT_SMS_NUMBER, LOGOUT_EMAIL, SYNC_HASHED_EMAIL, SET_EXTERNAL_USER_ID, diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java index d15e9244d4..6bc1d49799 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java @@ -1521,6 +1521,45 @@ public void run() { OneSignalStateSynchronizer.setSMSNumber(smsNumber, smsAuthHash); } + /** + * Call when user logs out of their account. + * This dissociates the device from the email address. + * This does not effect the subscription status of the email address itself. + */ + public static void logoutSMSNumber() { + logoutSMSNumber(null); + } + + public static void logoutSMSNumber(@Nullable final OSSMSUpdateHandler callback) { + if (taskController.shouldQueueTaskForInit(OSTaskController.LOGOUT_SMS_NUMBER)) { + logger.error("Waiting for remote params. " + + "Moving " + OSTaskController.LOGOUT_SMS_NUMBER + " operation to a pending task queue."); + taskController.addTaskToQueue(new Runnable() { + @Override + public void run() { + logger.debug("Running " + OSTaskController.LOGOUT_SMS_NUMBER + " operation from pending task queue."); + logoutSMSNumber(callback); + } + }); + return; + } + + // If applicable, check if the user provided privacy consent + if (shouldLogUserPrivacyConsentErrorMessageForMethodName(OSTaskController.LOGOUT_SMS_NUMBER)) + return; + + if (getSMSId() == null) { + final String message = OSTaskController.LOGOUT_SMS_NUMBER + " not valid as sms number was not set or already logged out!"; + if (callback != null) + callback.onFailure(new OSSMSUpdateError(SMSErrorType.INVALID_OPERATION, message)); + logger.error(message); + return; + } + + smsLogoutHandler = callback; + OneSignalStateSynchronizer.logoutSMS(); + } + public static void setEmail(@NonNull final String email, EmailUpdateHandler callback) { setEmail(email, null, callback); } @@ -2526,12 +2565,6 @@ static void updateEmailIdDependents(String emailId) { static void updateSMSIdDependents(String smsId) { saveSMSId(smsId); getCurrentSMSSubscriptionState(appContext).setSMSUserId(smsId); - try { - JSONObject updateJson = new JSONObject().put("parent_player_id", smsId); - OneSignalStateSynchronizer.updatePushState(updateJson); - } catch (JSONException e) { - e.printStackTrace(); - } } // Start Remote params getters @@ -3223,6 +3256,20 @@ static void fireEmailUpdateFailure() { } } + static void handleSuccessfulSMSlLogout(JSONObject result) { + if (smsLogoutHandler != null) { + smsLogoutHandler.onSuccess(result); + smsLogoutHandler = null; + } + } + + static void handleFailedSMSLogout() { + if (smsLogoutHandler != null) { + smsLogoutHandler.onFailure(new OSSMSUpdateError(SMSErrorType.NETWORK, "Failed due to network failure. Will retry on next sync.")); + smsLogoutHandler = null; + } + } + static void fireSMSUpdateSuccess(JSONObject result) { if (smsUpdateHandler != null) { smsUpdateHandler.onSuccess(result); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java index e19c6e4e52..9ddf31052f 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalStateSynchronizer.java @@ -237,7 +237,12 @@ static void setNewSessionForEmail() { static void logoutEmail() { getPushStateSynchronizer().logoutEmail(); - getEmailStateSynchronizer().logoutEmail(); + getEmailStateSynchronizer().logoutChannel(); + } + + static void logoutSMS() { + getPushStateSynchronizer().logoutSMS(); + getSMSStateSynchronizer().logoutChannel(); } static void setExternalUserId(String externalId, String externalIdAuthHash, final OneSignal.OSExternalUserIdUpdateCompletionHandler completionHandler) throws JSONException { diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserState.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserState.java index efb2bae3f3..3622c51ff7 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserState.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserState.java @@ -65,7 +65,7 @@ public ImmutableJSONObject getDependValues() { return new ImmutableJSONObject(); } - void setDependValues(JSONObject dependValues) { + public void setDependValues(JSONObject dependValues) { synchronized (LOCK) { this.dependValues = dependValues; } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java index cee7643678..f6c8ff7a35 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateEmailSynchronizer.java @@ -29,7 +29,7 @@ void saveChannelId(String id) { } @Override - void logoutEmail() { + void logoutChannel() { OneSignal.saveEmailId(""); resetCurrentState(); @@ -44,11 +44,6 @@ void logoutEmail() { OneSignal.getEmailSubscriptionState().clearEmailAndId(); } - @Override - void logoutSMS() { - - } - @Override protected String getChannelKey() { return EMAIL_KEY; diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java index 3de6131004..903c1087e8 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStatePushSynchronizer.java @@ -181,6 +181,9 @@ void updateIdDependents(String id) { protected void addOnSessionOrCreateExtras(JSONObject jsonBody) {} @Override + void logoutChannel() { + } + void logoutEmail() { try { getUserStateForModification().putOnDependValues(LOGOUT_EMAIL, true); @@ -189,9 +192,26 @@ void logoutEmail() { } } - @Override void logoutSMS() { + UserState toSyncUserState = getToSyncUserState(); + toSyncUserState.removeFromDependValues(SMS_AUTH_HASH_KEY); + toSyncUserState.removeFromSyncValues(SMS_NUMBER_KEY); + toSyncUserState.persistState(); + + UserState currentUserState = getCurrentUserState(); + currentUserState.removeFromDependValues(SMS_AUTH_HASH_KEY); + String smsNumberLoggedOut = currentUserState.getSyncValues().optString(SMS_NUMBER_KEY); + currentUserState.removeFromSyncValues(SMS_NUMBER_KEY); + + JSONObject result = new JSONObject(); + try { + result.put(SMS_NUMBER_KEY, smsNumberLoggedOut); + } catch (JSONException e) { + e.printStackTrace(); + } + OneSignal.Log(OneSignal.LOG_LEVEL.INFO, "Device successfully logged out of SMS number: " + result); + OneSignal.handleSuccessfulSMSlLogout(result); } @Override diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java index 8c0b942ba6..524f7ec880 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSMSSynchronizer.java @@ -27,11 +27,7 @@ void saveChannelId(String id) { } @Override - void logoutEmail() { - } - - @Override - void logoutSMS() { + void logoutChannel() { saveChannelId(""); resetCurrentState(); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java index 855eb6cc07..eeadbd6ec1 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSecondaryChannelSynchronizer.java @@ -20,10 +20,7 @@ abstract class UserStateSecondaryChannelSynchronizer extends UserStateSynchroniz abstract protected String getId(); @Override - abstract void logoutEmail(); - - @Override - abstract void logoutSMS(); + abstract void logoutChannel(); abstract protected String getChannelKey(); abstract protected String getAuthHashKey(); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java index 615a9447b3..d882f97569 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/UserStateSynchronizer.java @@ -2,13 +2,13 @@ import android.os.Handler; import android.os.HandlerThread; + import androidx.annotation.Nullable; import com.onesignal.OneSignal.ChangeTagsUpdateHandler; import com.onesignal.OneSignal.SendTagsError; import com.onesignal.OneSignalStateSynchronizer.UserStateSynchronizerType; -import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -571,9 +571,7 @@ void updateLocation(LocationController.LocationPoint point) { abstract void updateIdDependents(String id); - abstract void logoutEmail(); - - abstract void logoutSMS(); + abstract void logoutChannel(); void sendPurchases(JSONObject jsonBody, OneSignalRestClient.ResponseHandler responseHandler) { OneSignalRestClient.post("players/" + getId() + "/on_purchase", jsonBody, responseHandler); diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java index db6d160652..d610c9fc16 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java @@ -10,6 +10,7 @@ import com.onesignal.MockOSTimeImpl; import com.onesignal.MockOneSignalDBHelper; import com.onesignal.MockSessionManager; +import com.onesignal.OSDeviceState; import com.onesignal.OneSignal; import com.onesignal.OneSignalPackagePrivateHelper; import com.onesignal.ShadowCustomTabsClient; @@ -87,6 +88,7 @@ public class SynchronizerIntegrationTests { private static JSONObject lastExternalUserIdResponse; private static OneSignal.ExternalIdError lastExternalUserIdError; + private static OneSignal.OSExternalUserIdUpdateCompletionHandler getExternalUserIdUpdateCompletionHandler() { return new OneSignal.OSExternalUserIdUpdateCompletionHandler() { @Override @@ -103,6 +105,7 @@ public void onFailure(OneSignal.ExternalIdError error) { private static boolean didEmailUpdateSucceed; private static OneSignal.EmailUpdateError lastEmailUpdateFailure; + private static OneSignal.EmailUpdateHandler getEmailUpdateHandler() { return new OneSignal.EmailUpdateHandler() { @Override @@ -119,6 +122,7 @@ public void onFailure(OneSignal.EmailUpdateError error) { private static JSONObject didSMSlUpdateSucceed; private static OneSignal.OSSMSUpdateError lastSMSUpdateFailure; + private static OneSignal.OSSMSUpdateHandler getSMSUpdateHandler() { return new OneSignal.OSSMSUpdateHandler() { @Override @@ -217,7 +221,8 @@ public void shouldSetSMS() throws Exception { OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); threadAndTaskWait(); - assertEquals(4, ShadowOneSignalRestClient.networkCallCount); + // NO parent player id call + assertEquals(3, ShadowOneSignalRestClient.networkCallCount); JSONObject pushPost = ShadowOneSignalRestClient.requests.get(1).payload; assertEquals(ONESIGNAL_SMS_NUMBER, pushPost.getString("sms_number")); @@ -227,10 +232,6 @@ public void shouldSetSMS() throws Exception { assertEquals(ONESIGNAL_SMS_NUMBER, smsPost.getString("identifier")); assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_SMS, smsPost.getInt("device_type")); assertEquals(ShadowOneSignalRestClient.pushUserId, smsPost.getString("device_player_id")); - - JSONObject pushPut = ShadowOneSignalRestClient.requests.get(3).payload; - assertEquals(ShadowOneSignalRestClient.smsUserId, pushPut.getString("parent_player_id")); - assertFalse(pushPut.has("identifier")); } @Test @@ -259,7 +260,7 @@ public void shouldSetSMSAndEmail() throws Exception { assertEquals(ShadowOneSignalRestClient.pushUserId, smsPost.getString("device_player_id")); JSONObject pushPut = ShadowOneSignalRestClient.requests.get(4).payload; - assertEquals(ShadowOneSignalRestClient.smsUserId, pushPut.getString("parent_player_id")); + assertEquals(ShadowOneSignalRestClient.emailUserId, pushPut.getString("parent_player_id")); // add email parent id call // TODO: check if this behaviour is not dropped assertFalse(pushPut.has("identifier")); @@ -289,7 +290,7 @@ public void shouldSendTagsToSMSBeforeCreate() throws Exception { OneSignal.sendTags(tagsJson); threadAndTaskWait(); - assertEquals(4, ShadowOneSignalRestClient.networkCallCount); + assertEquals(3, ShadowOneSignalRestClient.networkCallCount); ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(2); assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); @@ -307,7 +308,7 @@ public void shouldWaitBeforeCreateEmailIfPushCreateFails() throws Exception { // Assert we are sending / retry for the push player first. assertEquals(5, ShadowOneSignalRestClient.networkCallCount); - for(int i = 1; i < ShadowOneSignalRestClient.networkCallCount; i++) { + for (int i = 1; i < ShadowOneSignalRestClient.networkCallCount; i++) { ShadowOneSignalRestClient.Request emailPost = ShadowOneSignalRestClient.requests.get(i); assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, emailPost.method); assertEquals(1, emailPost.payload.getInt("device_type")); @@ -334,7 +335,7 @@ public void shouldWaitBeforeCreateSMSIfPushCreateFails() throws Exception { // Assert we are sending / retry for the push player first. assertEquals(5, ShadowOneSignalRestClient.networkCallCount); - for(int i = 1; i < ShadowOneSignalRestClient.networkCallCount; i++) { + for (int i = 1; i < ShadowOneSignalRestClient.networkCallCount; i++) { ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(i); assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); assertEquals(ONESIGNAL_SMS_NUMBER, smsPost.payload.getString("sms_number")); @@ -364,7 +365,7 @@ public void shouldWaitBeforeCreateEmailAndSMSIfPushCreateFails() throws Exceptio // Assert we are sending / retry for the push player first. assertEquals(5, ShadowOneSignalRestClient.networkCallCount); - for(int i = 1; i < ShadowOneSignalRestClient.networkCallCount; i++) { + for (int i = 1; i < ShadowOneSignalRestClient.networkCallCount; i++) { ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(i); assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); assertEquals(ONESIGNAL_EMAIL_ADDRESS, smsPost.payload.getString("email")); @@ -418,9 +419,9 @@ public void shouldSendTagsToSMSAfterCreate() throws Exception { OneSignal.sendTags(tagsJson); threadAndTaskWait(); - assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + assertEquals(5, ShadowOneSignalRestClient.networkCallCount); - ShadowOneSignalRestClient.Request smsPut = ShadowOneSignalRestClient.requests.get(5); + ShadowOneSignalRestClient.Request smsPut = ShadowOneSignalRestClient.requests.get(4); assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, smsPut.method); assertEquals("players/" + ShadowOneSignalRestClient.smsUserId, smsPut.url); assertEquals(tagsJson.toString(), smsPut.payload.getJSONObject("tags").toString()); @@ -437,9 +438,9 @@ public void shouldSendTagsToSMSWithHashToken() throws Exception { OneSignal.sendTags(tagsJson); threadAndTaskWait(); - assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + assertEquals(5, ShadowOneSignalRestClient.networkCallCount); - ShadowOneSignalRestClient.Request smsPut = ShadowOneSignalRestClient.requests.get(5); + ShadowOneSignalRestClient.Request smsPut = ShadowOneSignalRestClient.requests.get(4); assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, smsPut.method); assertEquals("players/" + ShadowOneSignalRestClient.smsUserId, smsPut.url); assertEquals(3, smsPut.payload.length()); @@ -653,7 +654,7 @@ public void shouldFireOnFailureOfEmailAndSMSlUpdateOnNetworkFailure() throws Exc } @Test - public void shouldFireOnSuccessOnlyAfterNetworkCallAfterLogout() throws Exception { + public void shouldFireOnSuccessOnlyAfterNetworkCallAfterEmailLogout() throws Exception { OneSignalInit(); emailSetThenLogout(); TestEmailUpdateHandler testEmailUpdateHandler = new TestEmailUpdateHandler(); @@ -665,6 +666,20 @@ public void shouldFireOnSuccessOnlyAfterNetworkCallAfterLogout() throws Exceptio assertNull(testEmailUpdateHandler.emailFiredFailure); } + @Test + public void shouldFireOnSuccessOnlyAfterNetworkCallAfterSMSLogout() throws Exception { + OneSignalInit(); + smsSetThenLogout(); + TestSMSUpdateHandler testSMSUpdateHandler = new TestSMSUpdateHandler(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, testSMSUpdateHandler); + assertNull(testSMSUpdateHandler.smsResult); + threadAndTaskWait(); + + assertNotNull(testSMSUpdateHandler.smsResult); + assertEquals(ONESIGNAL_SMS_NUMBER, testSMSUpdateHandler.smsResult.getString("sms_number")); + assertNull(testSMSUpdateHandler.smsFiredFailure); + } + // Should create a new email instead of updating existing player record when no auth hash @Test public void shouldDoPostOnEmailChange() throws Exception { @@ -701,12 +716,9 @@ public void shouldDoPostOnSMSChange() throws Exception { OneSignal.setSMSNumber(newSMS); threadAndTaskWait(); - ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(5); + ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(4); assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); assertEquals(newSMS, smsPost.payload.get("identifier")); - - ShadowOneSignalRestClient.Request playerPut = ShadowOneSignalRestClient.requests.get(6); - assertEquals(newMockSMSPlayerId, playerPut.payload.get("parent_player_id")); } // Should update player with new email instead of creating a new one when auth hash is provided @@ -744,12 +756,12 @@ public void shouldUpdateSMSlWhenAuthHashIsUsed() throws Exception { OneSignal.setSMSNumber(newSMS, mockEmailHash); threadAndTaskWait(); - ShadowOneSignalRestClient.Request pushPut = ShadowOneSignalRestClient.requests.get(4); + ShadowOneSignalRestClient.Request pushPut = ShadowOneSignalRestClient.requests.get(3); assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, pushPut.method); assertEquals("players/" + PUSH_USER_ID, pushPut.url); assertEquals(newSMS, pushPut.payload.get("sms_number")); - ShadowOneSignalRestClient.Request smsPut = ShadowOneSignalRestClient.requests.get(5); + ShadowOneSignalRestClient.Request smsPut = ShadowOneSignalRestClient.requests.get(4); assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, smsPut.method); assertEquals("players/" + SMS_USER_ID, smsPut.url); assertEquals(newSMS, smsPut.payload.get("identifier")); @@ -779,6 +791,14 @@ private void emailSetThenLogout() throws Exception { threadAndTaskWait(); } + private void smsSetThenLogout() throws Exception { + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + OneSignal.logoutSMSNumber(); + threadAndTaskWait(); + } + @Test public void shouldLogoutOfEmail() throws Exception { OneSignalInit(); @@ -791,6 +811,17 @@ public void shouldLogoutOfEmail() throws Exception { assertEquals("b4f7f966-d8cc-11e4-bed1-df8f05be55ba", logoutEmailPost.payload.get("app_id")); } + @Test + public void shouldLogoutOfSMS() throws Exception { + OneSignalInit(); + + smsSetThenLogout(); + + OSDeviceState deviceState = OneSignal.getDeviceState(); + assertNull(deviceState.getSMSUserId()); + assertNull(deviceState.getSMSNumber()); + } + @Test public void logoutEmailShouldNotSendEmailPlayersRequest() throws Exception { // 1. Init OneSignal and set email @@ -802,7 +833,6 @@ public void logoutEmailShouldNotSendEmailPlayersRequest() throws Exception { pauseActivity(blankActivityController); assertAndRunSyncService(); - List requests = ShadowOneSignalRestClient.requests; assertEquals(6, ShadowOneSignalRestClient.networkCallCount); ShadowOneSignalRestClient.Request postPush = ShadowOneSignalRestClient.requests.get(4); @@ -812,6 +842,23 @@ public void logoutEmailShouldNotSendEmailPlayersRequest() throws Exception { assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_focus", postEmail.url); } + @Test + public void logoutSMSShouldNotSendSMSPlayersRequest() throws Exception { + // 1. Init OneSignal and set email + OneSignalInit(); + + smsSetThenLogout(); + + time.advanceSystemAndElapsedTimeBy(60); + pauseActivity(blankActivityController); + assertAndRunSyncService(); + + assertEquals(4, ShadowOneSignalRestClient.networkCallCount); + + ShadowOneSignalRestClient.Request postPush = ShadowOneSignalRestClient.requests.get(3); + assertNotEquals("players/" + PUSH_USER_ID, postPush.url); + } + @Test public void shouldFireOnSuccessOfLogoutEmail() throws Exception { OneSignalInit(); @@ -827,6 +874,22 @@ public void shouldFireOnSuccessOfLogoutEmail() throws Exception { assertNull(testEmailUpdateHandler.emailFiredFailure); } + @Test + public void shouldFireOnSuccessOfLogoutSMS() throws Exception { + OneSignalInit(); + TestSMSUpdateHandler testSMSUpdateHandler = new TestSMSUpdateHandler(); + + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + OneSignal.logoutSMSNumber(testSMSUpdateHandler); + threadAndTaskWait(); + + assertNotNull(testSMSUpdateHandler.smsResult); + assertEquals(ONESIGNAL_SMS_NUMBER, testSMSUpdateHandler.smsResult.getString("sms_number")); + assertNull(testSMSUpdateHandler.smsFiredFailure); + } + @Test public void shouldFireOnFailureOfLogoutEmailOnNetworkFailure() throws Exception { OneSignalInit(); @@ -844,6 +907,24 @@ public void shouldFireOnFailureOfLogoutEmailOnNetworkFailure() throws Exception assertEquals(OneSignal.EmailErrorType.NETWORK, testEmailUpdateHandler.emailFiredFailure.getType()); } + @Test + public void shouldNotFireOnFailureOfLogoutSMSOnNetworkFailure() throws Exception { + OneSignalInit(); + TestSMSUpdateHandler testSMSUpdateHandler = new TestSMSUpdateHandler(); + + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + ShadowOneSignalRestClient.failAll = true; + OneSignal.logoutSMSNumber(testSMSUpdateHandler); + threadAndTaskWait(); + + assertNotNull(testSMSUpdateHandler.smsResult); + assertEquals(ONESIGNAL_SMS_NUMBER, testSMSUpdateHandler.smsResult.getString("sms_number")); + assertNull(testSMSUpdateHandler.smsFiredFailure); + } + @Test public void shouldCreateNewEmailAfterLogout() throws Exception { OneSignalInit(); @@ -871,6 +952,34 @@ public void shouldCreateNewEmailAfterLogout() throws Exception { assertEquals(newMockEmailPlayerId, playerPut2.payload.get("parent_player_id")); } + @Test + public void shouldCreateNewSMSAfterLogout() throws Exception { + OneSignalInit(); + + smsSetThenLogout(); + + String newMockSMSPlayerId = "c007f967-98cc-11e4-bed1-118f05be4533"; + String newSMSNumber = "new_sms_number"; + + ShadowOneSignalRestClient.smsUserId = newMockSMSPlayerId; + OneSignal.setSMSNumber(newSMSNumber); + threadAndTaskWait(); + + // There should not be a parent_player_id update, last SMS POST should be the last one + assertEquals(5, ShadowOneSignalRestClient.requests.size()); + + // Update Push record's sms field. + ShadowOneSignalRestClient.Request putPushSMS = ShadowOneSignalRestClient.requests.get(3); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, putPushSMS.method); + assertEquals("players/" + PUSH_USER_ID, putPushSMS.url); + assertEquals(newSMSNumber, putPushSMS.payload.get("sms_number")); + + // Create new SMS record + ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(4); + assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); + assertEquals(newSMSNumber, smsPost.payload.get("identifier")); + } + // ####### external_id Tests ######## @Test @@ -1060,11 +1169,11 @@ public void shouldAlwaysSetExternalIdAndSMSWithAuthHashAfterRegistration() throw OneSignalInit(); threadAndTaskWait(); - ShadowOneSignalRestClient.Request registrationRequestAfterColdStart = ShadowOneSignalRestClient.requests.get(6); + ShadowOneSignalRestClient.Request registrationRequestAfterColdStart = ShadowOneSignalRestClient.requests.get(5); assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, registrationRequestAfterColdStart.method); assertEquals(mockExternalIdHash, registrationRequestAfterColdStart.payload.getString("external_user_id_auth_hash")); - ShadowOneSignalRestClient.Request smsPostAfterColdStart = ShadowOneSignalRestClient.requests.get(7); + ShadowOneSignalRestClient.Request smsPostAfterColdStart = ShadowOneSignalRestClient.requests.get(6); assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPostAfterColdStart.method); assertEquals(OneSignalPackagePrivateHelper.UserState.DEVICE_TYPE_SMS, smsPostAfterColdStart.payload.getInt("device_type")); assertEquals(mockSMSlHash, smsPostAfterColdStart.payload.getString("sms_auth_hash")); @@ -1086,7 +1195,7 @@ public void shouldSetExternalIdOnSMS() throws Exception { OneSignal.setExternalUserId(testExternalId, mockExternalIdHash); threadAndTaskWait(); - ShadowOneSignalRestClient.Request setExternalIdOnSMSChannel = ShadowOneSignalRestClient.requests.get(6); + ShadowOneSignalRestClient.Request setExternalIdOnSMSChannel = ShadowOneSignalRestClient.requests.get(5); assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, setExternalIdOnSMSChannel.method); assertEquals("players/" + ShadowOneSignalRestClient.smsUserId, setExternalIdOnSMSChannel.url); assertEquals(testExternalId, setExternalIdOnSMSChannel.payload.get("external_user_id")); @@ -1112,9 +1221,9 @@ public void shouldSetExternalIdWithAuthHashOnSendTagsSMS() throws Exception { OneSignal.sendTags(tagsJson); threadAndTaskWait(); - assertEquals(8, ShadowOneSignalRestClient.networkCallCount); + assertEquals(7, ShadowOneSignalRestClient.networkCallCount); - ShadowOneSignalRestClient.Request smsPut = ShadowOneSignalRestClient.requests.get(7); + ShadowOneSignalRestClient.Request smsPut = ShadowOneSignalRestClient.requests.get(6); assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, smsPut.method); assertEquals("players/" + ShadowOneSignalRestClient.smsUserId, smsPut.url); assertEquals(4, smsPut.payload.length()); @@ -1257,14 +1366,14 @@ public void shouldRemoveExternalUserIdFromSMSWithAuthHash() throws Exception { assertNotNull(didSMSlUpdateSucceed); assertNull(lastEmailUpdateFailure); - assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + assertEquals(5, ShadowOneSignalRestClient.networkCallCount); - ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(4); + ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(3); assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method); assertEquals(removeIdRequest.payload.getString("external_user_id"), ""); assertFalse(removeIdRequest.payload.has("external_user_id_auth_hash")); - ShadowOneSignalRestClient.Request removeIdSMSRequest = ShadowOneSignalRestClient.requests.get(5); + ShadowOneSignalRestClient.Request removeIdSMSRequest = ShadowOneSignalRestClient.requests.get(4); assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdSMSRequest.method); assertEquals(removeIdSMSRequest.payload.getString("external_user_id"), ""); assertEquals(mockSMSHash, removeIdSMSRequest.payload.getString("sms_auth_hash")); @@ -1312,14 +1421,14 @@ public void shouldRemoveExternalUserIdFromPushAndSMSWithAuthHash() throws Except OneSignal.removeExternalUserId(); threadAndTaskWait(); - assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + assertEquals(5, ShadowOneSignalRestClient.networkCallCount); - ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(4); + ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(3); assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method); assertEquals(removeIdRequest.payload.getString("external_user_id"), ""); assertEquals(mockExternalIdHash, removeIdRequest.payload.getString("external_user_id_auth_hash")); - ShadowOneSignalRestClient.Request removeIdSMSRequest = ShadowOneSignalRestClient.requests.get(5); + ShadowOneSignalRestClient.Request removeIdSMSRequest = ShadowOneSignalRestClient.requests.get(4); assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdSMSRequest.method); assertEquals(removeIdSMSRequest.payload.getString("external_user_id"), ""); assertEquals(mockSMSHash, removeIdSMSRequest.payload.getString("sms_auth_hash")); @@ -1768,6 +1877,34 @@ public void sendExternalUserId_forPush_afterLoggingOutEmail_withCompletion() thr OneSignal.logoutEmail(); threadAndTaskWait(); + // 3. Attempt a set external user id with callback + OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); + threadAndTaskWait(); + + // 4. Make sure lastExternalUserIdResponse has push with success : true + JSONObject expectedExternalUserIdResponse = new JSONObject( + "{" + + " \"push\" : {" + + " \"success\" : true" + + " }" + + "}" + ); + assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString()); + } + + @Test + public void sendExternalUserId_forPush_afterLoggingOutSMS_withCompletion() throws Exception { + String testExternalId = "test_ext_id"; + + // 1. Init OneSignal and set email + OneSignalInit(); + OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER); + threadAndTaskWait(); + + // 2. Logout SMS + OneSignal.logoutSMSNumber(); + threadAndTaskWait(); + // 4. Attempt a set external user id with callback OneSignal.setExternalUserId(testExternalId, getExternalUserIdUpdateCompletionHandler()); threadAndTaskWait(); @@ -1810,7 +1947,7 @@ public void shouldSendOnSessionToSMS() throws Exception { OneSignalInit(); threadAndTaskWait(); - ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(6); + ShadowOneSignalRestClient.Request smsPost = ShadowOneSignalRestClient.requests.get(5); assertEquals(ShadowOneSignalRestClient.REST_METHOD.POST, smsPost.method); assertEquals("players/" + ShadowOneSignalRestClient.SMS_USER_ID + "/on_session", smsPost.url); @@ -1899,16 +2036,16 @@ public void sendsOnFocusToSMS() throws Exception { pauseActivity(blankActivityController); assertAndRunSyncService(); - assertEquals(6, ShadowOneSignalRestClient.networkCallCount); + assertEquals(5, ShadowOneSignalRestClient.networkCallCount); - ShadowOneSignalRestClient.Request post = ShadowOneSignalRestClient.requests.get(3); + ShadowOneSignalRestClient.Request post = ShadowOneSignalRestClient.requests.get(2); assertFalse(post.url.contains("on_focus")); - ShadowOneSignalRestClient.Request postPush = ShadowOneSignalRestClient.requests.get(4); + ShadowOneSignalRestClient.Request postPush = ShadowOneSignalRestClient.requests.get(3); assertEquals("players/a2f7f967-e8cc-11e4-bed1-118f05be4511/on_focus", postPush.url); assertEquals(60, postPush.payload.getInt("active_time")); - ShadowOneSignalRestClient.Request postSMSl = ShadowOneSignalRestClient.requests.get(5); + ShadowOneSignalRestClient.Request postSMSl = ShadowOneSignalRestClient.requests.get(4); assertEquals("players/" + SMS_USER_ID + "/on_focus", postSMSl.url); assertEquals(60, postSMSl.payload.getInt("active_time")); } From c1d538a429eb28e7d402890c3e6ac7682281e81e Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 8 Mar 2021 18:47:11 -0300 Subject: [PATCH 22/23] Add SMS to demo app --- .../sdktest/callback/SMSUpdateCallback.java | 8 + .../com/onesignal/sdktest/constant/Text.java | 6 + .../sdktest/model/MainActivityViewModel.java | 58 +++ .../onesignal/sdktest/user/CurrentUser.java | 32 ++ .../com/onesignal/sdktest/util/Dialog.java | 43 +++ .../onesignal/sdktest/util/ProfileUtil.java | 19 +- .../sdktest/util/SharedPreferenceUtil.java | 5 + .../main/res/layout/main_activity_layout.xml | 348 ++++++++++++------ .../app/src/main/res/values/strings.xml | 5 + 9 files changed, 409 insertions(+), 115 deletions(-) create mode 100644 Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/callback/SMSUpdateCallback.java diff --git a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/callback/SMSUpdateCallback.java b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/callback/SMSUpdateCallback.java new file mode 100644 index 0000000000..f99dfe6333 --- /dev/null +++ b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/callback/SMSUpdateCallback.java @@ -0,0 +1,8 @@ +package com.onesignal.sdktest.callback; + +public interface SMSUpdateCallback { + + void onSuccess(); + void onFailure(); + +} diff --git a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/constant/Text.java b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/constant/Text.java index 79f4475c03..50e04d0b3a 100644 --- a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/constant/Text.java +++ b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/constant/Text.java @@ -11,13 +11,19 @@ public class Text { public static final String EMAIL_SET_SUCCESSFULLY = "Email set successfully"; public static final String EMAIL_SET_FAILURE = "Email set failure"; + public static final String SMS_SET_SUCCESSFULLY = "SMS Number set successfully"; + public static final String SMS_SET_FAILURE = "SMS Number set failure"; + public static final String APP_ID_IS_REQUIRED = "App id is required"; public static final String INVALID_APP_ID = "Invalid app id"; public static final String EMAIL_IS_REQUIRED = "Email is required"; public static final String INVALID_EMAIL = "Invalid email"; + public static final String SMS_IS_REQUIRED = "SMS number is required"; + public static final String EMAIL_NOT_SET = "Email not set"; + public static final String SMS_NOT_SET = "SMS Number not set"; public static final String EXTERNAL_USER_ID_NOT_SET = "External user id not set"; public static final String EXTERNAL_USER_ID_IS_REQUIRED = "External user id is required"; diff --git a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java index 88639b21f8..198a8dbbcd 100644 --- a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java +++ b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java @@ -46,6 +46,7 @@ import com.onesignal.sdktest.util.Toaster; import org.json.JSONException; +import org.json.JSONObject; import java.util.ArrayList; import java.util.HashMap; @@ -79,9 +80,18 @@ public class MainActivityViewModel implements ActivityViewModel { private TextView appIdTextView; // Email private RelativeLayout emailRelativeLayout; + private TextView emailHeaderTextView; private TextView emailTitleTextView; private TextView userEmailTextView; private Button logoutEmailButton; + + // SMS + private RelativeLayout smsRelativeLayout; + private TextView smsHeaderTextView; + private TextView smsTitleTextView; + private TextView userSMSTextView; + private Button logoutSMSButton; + // External User Id private RelativeLayout externalUserIdRelativeLayout; private TextView externalUserIdTitleTextView; @@ -181,11 +191,18 @@ public ActivityViewModel onActivityCreated(Context context) { appIdTitleTextView = getActivity().findViewById(R.id.main_activity_account_details_app_id_title_text_view); appIdTextView = getActivity().findViewById(R.id.main_activity_account_details_app_id_text_view); + emailHeaderTextView = getActivity().findViewById(R.id.main_activity_email_title_text_view); emailRelativeLayout = getActivity().findViewById(R.id.main_activity_account_details_email_relative_layout); emailTitleTextView = getActivity().findViewById(R.id.main_activity_account_details_email_text_view); userEmailTextView = getActivity().findViewById(R.id.main_activity_account_details_user_email_text_view); logoutEmailButton = getActivity().findViewById(R.id.main_activity_email_logout_email_button); + smsHeaderTextView = getActivity().findViewById(R.id.main_activity_sms_title_text_view); + smsRelativeLayout = getActivity().findViewById(R.id.main_activity_account_details_sms_relative_layout); + smsTitleTextView = getActivity().findViewById(R.id.main_activity_account_details_sms_text_view); + userSMSTextView = getActivity().findViewById(R.id.main_activity_account_details_user_sms_text_view); + logoutSMSButton = getActivity().findViewById(R.id.main_activity_sms_logout_sms_button); + externalUserIdRelativeLayout = getActivity().findViewById(R.id.main_activity_account_details_external_user_id_relative_layout); externalUserIdTitleTextView = getActivity().findViewById(R.id.main_activity_account_details_external_user_id_text_view); userExternalUserIdTextView = getActivity().findViewById(R.id.main_activity_account_details_user_external_user_id_text_view); @@ -244,8 +261,12 @@ public ActivityViewModel setupInterfaceElements() { font.applyFont(privacyConsentAllowButton, font.saralaBold); font.applyFont(appIdTitleTextView, font.saralaBold); font.applyFont(appIdTextView, font.saralaRegular); + font.applyFont(emailHeaderTextView, font.saralaBold); font.applyFont(emailTitleTextView, font.saralaBold); font.applyFont(userEmailTextView, font.saralaRegular); + font.applyFont(smsHeaderTextView, font.saralaBold); + font.applyFont(smsTitleTextView, font.saralaBold); + font.applyFont(userSMSTextView, font.saralaRegular); font.applyFont(externalUserIdTitleTextView, font.saralaBold); font.applyFont(userExternalUserIdTextView, font.saralaRegular); font.applyFont(tagsTitleTextView, font.saralaBold); @@ -373,6 +394,7 @@ public void onFailure() { }); setupEmailButton(); + setupSMSButton(); setupExternalUserIdButton(); } @@ -417,6 +439,42 @@ public void onFailure(OneSignal.EmailUpdateError error) { }); } + private void setupSMSButton() { + boolean isSMSNumberSet = currentUser.isSMSNumberSet(); + String smsNumber = isSMSNumberSet ? currentUser.getSMSNumber() : Text.SMS_NOT_SET; + userSMSTextView.setText(smsNumber); + + smsRelativeLayout.setOnClickListener(v -> dialog.createUpdateAlertDialog(currentUser.getSMSNumber(), ProfileUtil.FieldType.SMS, new UpdateAlertDialogCallback() { + @Override + public void onSuccess(String update) { + userSMSTextView.setText(update); + } + + @Override + public void onFailure() { + + } + })); + + logoutSMSButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + OneSignal.logoutSMSNumber(new OneSignal.OSSMSUpdateHandler() { + @Override + public void onSuccess(JSONObject result) { + OneSignal.onesignalLog(OneSignal.LOG_LEVEL.DEBUG, "Logout SMS ended successfully, result: " + result); + MainActivityViewModel.this.getActivity().runOnUiThread(() -> userSMSTextView.setText("")); + } + + @Override + public void onFailure(OneSignal.OSSMSUpdateError error) { + OneSignal.onesignalLog(OneSignal.LOG_LEVEL.DEBUG, "Logout SMS failed with error: " + error); + } + }); + } + }); + } + private void setupExternalUserIdButton() { boolean isExternalUserSet = currentUser.isExternalUserIdSet(context); String externalUserId = isExternalUserSet ? currentUser.getExternalUserId(context) : Text.EXTERNAL_USER_ID_NOT_SET; diff --git a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/user/CurrentUser.java b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/user/CurrentUser.java index fc1089fd7e..769fb141d0 100644 --- a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/user/CurrentUser.java +++ b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/user/CurrentUser.java @@ -6,9 +6,12 @@ import com.onesignal.OSDeviceState; import com.onesignal.OneSignal; import com.onesignal.sdktest.callback.EmailUpdateCallback; +import com.onesignal.sdktest.callback.SMSUpdateCallback; import com.onesignal.sdktest.constant.Tag; import com.onesignal.sdktest.util.SharedPreferenceUtil; +import org.json.JSONObject; + public class CurrentUser { private static CurrentUser currentUser; @@ -20,6 +23,13 @@ public String getEmail() { return null; } + public String getSMSNumber() { + OSDeviceState deviceState = OneSignal.getDeviceState(); + if (deviceState != null) + return deviceState.getSMSNumber(); + return null; + } + public String getExternalUserId(Context context) { return SharedPreferenceUtil.getCachedUserExternalUserId(context); } @@ -46,6 +56,23 @@ public void onFailure(OneSignal.EmailUpdateError error) { }); } + public void setSMSNumber(String smsNumber, final SMSUpdateCallback callback) { + OneSignal.setSMSNumber(smsNumber, new OneSignal.OSSMSUpdateHandler() { + @Override + public void onSuccess(JSONObject result) { + callback.onSuccess(); + } + + @Override + public void onFailure(OneSignal.OSSMSUpdateError error) { + String errorMsg = error.getType() + ": " + error.getMessage(); + Log.e(Tag.ERROR, errorMsg); + + callback.onFailure(); + } + }); + } + public void removeEmail(final EmailUpdateCallback callback) { OneSignal.logoutEmail(); callback.onSuccess(); @@ -56,6 +83,11 @@ public boolean isEmailSet() { return deviceState != null && deviceState.getEmailAddress() != null; } + public boolean isSMSNumberSet() { + OSDeviceState deviceState = OneSignal.getDeviceState(); + return deviceState != null && deviceState.getSMSNumber() != null; + } + public boolean isExternalUserIdSet(Context context) { String userId = getExternalUserId(context); return userId != null && !userId.isEmpty(); diff --git a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/Dialog.java b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/Dialog.java index 507635f802..579a9212a0 100644 --- a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/Dialog.java +++ b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/Dialog.java @@ -27,6 +27,7 @@ import com.onesignal.sdktest.callback.AddPairAlertDialogCallback; import com.onesignal.sdktest.callback.EmailUpdateCallback; import com.onesignal.sdktest.callback.EnumSelectionCallback; +import com.onesignal.sdktest.callback.SMSUpdateCallback; import com.onesignal.sdktest.callback.UpdateAlertDialogCallback; import com.onesignal.sdktest.constant.Tag; import com.onesignal.sdktest.constant.Text; @@ -105,6 +106,10 @@ public void onClick(final DialogInterface dialog, int which) { updateEmail(dialog, newContent); break; + case SMS: + updateSMsNumber(dialog, newContent); + break; + case EXTERNAL_USER_ID: updateExternalUserId(dialog, newContent); break; @@ -177,6 +182,44 @@ public void run() { }); } + /** + * Updates the SMS number attached to the device and caches + */ + private void updateSMsNumber(final DialogInterface dialog, final String smsNumber) { + currentUser.setSMSNumber(smsNumber, new SMSUpdateCallback() { + @Override + public void onSuccess() { + SharedPreferenceUtil.cacheUserSMSNumber(context, smsNumber); + Log.d(Tag.DEBUG, Text.SMS_SET_SUCCESSFULLY); + + ((Activity) context).runOnUiThread(new Runnable() { + @Override + public void run() { + toggleUpdateAlertDialogAttributes(false); + + dialog.dismiss(); + callback.onSuccess(smsNumber); + } + }); + } + + @Override + public void onFailure() { + Log.d(Tag.ERROR, Text.SMS_SET_FAILURE); + + ((Activity) context).runOnUiThread(new Runnable() { + @Override + public void run() { + toggleUpdateAlertDialogAttributes(false); + + dialog.dismiss(); + callback.onFailure(); + } + }); + } + }); + } + /** * Set external id attached to the user/email of the device */ diff --git a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/ProfileUtil.java b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/ProfileUtil.java index 85a2eb31ec..de433876eb 100644 --- a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/ProfileUtil.java +++ b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/ProfileUtil.java @@ -11,6 +11,7 @@ public enum FieldType { APP_ID("App Id"), EMAIL("Email"), + SMS("SMS"), EXTERNAL_USER_ID("External User Id"), TAG("Tags"), @@ -65,6 +66,21 @@ public static boolean isEmailValid(TextInputLayout emailTextInputLayout) { return true; } + public static boolean isSMSValid(TextInputLayout smsTextInputLayout) { + smsTextInputLayout.setErrorEnabled(false); + if (smsTextInputLayout.getEditText() != null) { + String smsNumber = smsTextInputLayout.getEditText().getText().toString().trim(); + if (smsNumber.isEmpty()) { + smsTextInputLayout.setError(Text.SMS_IS_REQUIRED); + return false; + } + } else { + smsTextInputLayout.setError(Text.ERROR); + return false; + } + return true; + } + private static boolean isExternalUserIdValid(TextInputLayout externalUserIdTextInputLayout) { externalUserIdTextInputLayout.setErrorEnabled(false); if (externalUserIdTextInputLayout.getEditText() != null) { @@ -101,9 +117,10 @@ static boolean isContentValid(FieldType field, TextInputLayout alertDialogTextIn return isAppIdValid(alertDialogTextInputLayout); case EMAIL: return isEmailValid(alertDialogTextInputLayout); + case SMS: + return isSMSValid(alertDialogTextInputLayout); case EXTERNAL_USER_ID: return isExternalUserIdValid(alertDialogTextInputLayout); - case TAG: case TRIGGER: return isKeyValid(alertDialogTextInputLayout); diff --git a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/SharedPreferenceUtil.java b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/SharedPreferenceUtil.java index d2b6c8f5b7..e8ef9ab751 100644 --- a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/SharedPreferenceUtil.java +++ b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/SharedPreferenceUtil.java @@ -12,6 +12,7 @@ public class SharedPreferenceUtil { public static final String OS_APP_ID_SHARED_PREF = "OS_APP_ID_SHARED_PREF"; private static final String PRIVACY_CONSENT_SHARED_PREF = "PRIVACY_CONSENT_SHARED_PREF"; public static final String USER_EMAIL_SHARED_PREF = "USER_EMAIL_SHARED_PREF"; + public static final String USER_SMS_NUMBER_SHARED_PREF = "USER_SMS_NUMBER_SHARED_PREF"; public static final String USER_EXTERNAL_USER_ID_SHARED_PREF = "USER_EXTERNAL_USER_ID_SHARED_PREF"; private static final String LOCATION_SHARED_PREF = "LOCATION_SHARED_PREF"; private static final String IN_APP_MESSAGING_PAUSED_PREF = "IN_APP_MESSAGING_PAUSED_PREF"; @@ -60,6 +61,10 @@ public static void cacheUserEmail(Context context, String email) { getSharedPreference(context).edit().putString(USER_EMAIL_SHARED_PREF, email).apply(); } + public static void cacheUserSMSNumber(Context context, String smsNumber) { + getSharedPreference(context).edit().putString(USER_SMS_NUMBER_SHARED_PREF, smsNumber).apply(); + } + public static void cacheUserExternalUserId(Context context, String userId) { getSharedPreference(context).edit().putString(USER_EXTERNAL_USER_ID_SHARED_PREF, userId).apply(); } diff --git a/Examples/OneSignalDemo/app/src/main/res/layout/main_activity_layout.xml b/Examples/OneSignalDemo/app/src/main/res/layout/main_activity_layout.xml index df9402da24..32a6287a11 100644 --- a/Examples/OneSignalDemo/app/src/main/res/layout/main_activity_layout.xml +++ b/Examples/OneSignalDemo/app/src/main/res/layout/main_activity_layout.xml @@ -169,167 +169,287 @@ app:cardElevation="4dp"> - + + - - + android:background="@drawable/ripple_selector_red_white" + android:paddingStart="20dp" + android:paddingTop="12dp" + android:paddingEnd="20dp" + android:paddingBottom="16dp"> - - - - - + android:layout_alignParentStart="true" + android:layout_alignParentLeft="true" + android:layout_centerVertical="true" + android:layout_toStartOf="@id/main_activity_account_details_app_id_text_view" + android:layout_toLeftOf="@id/main_activity_account_details_app_id_text_view" + android:text="@string/id_colon" + android:textColor="@color/colorDarkText" + android:textSize="16sp" /> + + - + - + + - + - + android:layout_alignParentStart="true" + android:layout_alignParentLeft="true" + android:layout_centerVertical="true" + android:layout_toStartOf="@id/main_activity_account_details_user_external_user_id_text_view" + android:layout_toLeftOf="@id/main_activity_account_details_user_external_user_id_text_view" + android:text="@string/external_user_id_colon" + android:textColor="@color/colorDarkText" + android:textSize="16sp" /> + + - + - + - + - + + - + - + - + - + - + + + + - + + android:orientation="vertical">