Skip to content

Commit 89504ee

Browse files
authored
Feature/add set external id callback (#969)
* Adding a callback to the set external id method * This now handles the email and normal id case for changing external id with a callback * WIP - Add unit testing * Finishing with external id updating callback * Callback will handle 3 cases * Normal external id update (Success w/ newExternalUserId) * Duplicate external id update (Success w/ externalUserId) * Failed external id update (Failure w/ ExternalUserId error) * Wrote 3 unit tests for testing external user id * Normal setExternalUserId success * Duplicate setExternalUserId success * setExternalUserId failure * Updated the UserStateSynchronizer class and those that inherit from it * UserStateEmailSynchronizer needed to implement the new getExternalUserId method * UserStatePushSynchronizer needed to implement the new getExternalUserId method * Fixed 1 unit test for externalUserId callback and added 1 unit test * Fixed sendSameExternalUserId_withCallbackSuccess, which was not trying to send same external user id back to back * Added sendDifferentExternalUserId_withCallbackSuccess, which tests successful callback for changed changing the external user id * Update to the a unit test for set external user id callback tests * sendExternalUserId_withCallbackFailure_andWithCallbackSuccess now makes a second request for success after a failure, making sure that on failure a successful request should be made in expected conditions * Example... Network failure flag is true and an attempt to set external id fails and a error is passed back. Network failure flag is false now and an additional attempt succeeds and returns the correct successfully set external user id. * Added email and ext user id UI elements for testing in old demo app * Email has set and logout buttons along with text field * External user id has set and remove buttons along with text field Modified the payload of the completion handlers now to give back specific success results for push and email channels since both are sep player records inside of the DB * Format is as follows: { "channel" : { "success" : boolean } } Several scenarios needed to be handled for setting external along side email... 7 main scenarios: * Set external user id before init/register (no email set and email set) * Set external user id before setting email * Set external user id after setting email * Set external user id after logging out email * Changing external user id (no email set and email set) * PR review comment fixing * Small nits nothing major * Added more TODO comments, remove unused code, added some annotations for nullable returns on abstraction for getExternalId * Remove sendTag comment from demo app
1 parent 8a7ed4a commit 89504ee

File tree

10 files changed

+521
-68
lines changed

10 files changed

+521
-68
lines changed

OneSignalSDK/app/src/main/java/com/onesignal/MainActivity.java

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import android.widget.Button;
4343
import android.widget.CheckBox;
4444
import android.widget.CompoundButton;
45+
import android.widget.EditText;
4546
import android.widget.TextView;
4647
import android.widget.Toast;
4748

@@ -62,6 +63,12 @@ public class MainActivity extends Activity implements OSEmailSubscriptionObserve
6263
private int[] interactiveViewIds = new int[]{
6364
R.id.subscribe,
6465
R.id.unsubscribe,
66+
R.id.emailEditText,
67+
R.id.setEmailButton,
68+
R.id.logoutEmailButton,
69+
R.id.externalUserIdEditText,
70+
R.id.setExternalUserId,
71+
R.id.removeExternalUserId,
6572
R.id.sendTags,
6673
R.id.getTags,
6774
R.id.sendEvent,
@@ -70,16 +77,17 @@ public class MainActivity extends Activity implements OSEmailSubscriptionObserve
7077
R.id.postNotificationGroupCheckBox,
7178
R.id.postNotificationAsyncGroupCheckBox};
7279

80+
private EditText externalUserIdEditText;
7381
private TextView debugTextView;
74-
private TextView emailTextView;
7582
private TextView outcomeName;
7683
private TextView outcomeValueName;
7784
private TextView outcomeValue;
7885
private TextView outcomeUnique;
7986
private Button consentButton;
87+
private EditText emailEditText;
8088
private Button setEmailButton;
81-
private Button sendEvent;
8289
private Button logoutEmailButton;
90+
private Button sendEvent;
8391
private Button postNotifButton;
8492
private Button postNotifAsyncButton;
8593
private CheckBox postNotifGroupCheckBox;
@@ -118,14 +126,16 @@ protected void onCreate(Bundle savedInstanceState) {
118126
setContentView(com.onesignal.example.R.layout.activity_main);
119127

120128
this.consentButton = this.findViewById(R.id.consentButton);
121-
this.logoutEmailButton = this.findViewById(R.id.logoutEmail);
129+
this.emailEditText = findViewById(R.id.emailEditText);
130+
this.setEmailButton = findViewById(R.id.setEmailButton);
131+
this.logoutEmailButton = this.findViewById(R.id.logoutEmailButton);
132+
this.externalUserIdEditText = this.findViewById(R.id.externalUserIdEditText);
122133
this.postNotifButton = this.findViewById(R.id.postNotification);
123134
this.postNotifAsyncButton = this.findViewById(R.id.postNotificationAsync);
124135
this.postNotifGroupCheckBox = this.findViewById(R.id.postNotificationGroupCheckBox);
125136
this.postNotifAsyncGroupCheckBox = this.findViewById(R.id.postNotificationAsyncGroupCheckBox);
126-
this.consentButton = (Button)this.findViewById(com.onesignal.example.R.id.consentButton);
127-
this.logoutEmailButton = (Button)this.findViewById(com.onesignal.example.R.id.logoutEmail);
128-
this.sendEvent = (Button)this.findViewById(R.id.sendEvent);
137+
this.consentButton = this.findViewById(R.id.consentButton);
138+
this.sendEvent = this.findViewById(R.id.sendEvent);
129139
this.iamHost = this.findViewById(R.id.iamHost);
130140
this.triggerKeyTextView = this.findViewById(R.id.triggerKey);
131141
this.triggerValueTextView = this.findViewById(R.id.triggerValue);
@@ -246,6 +256,26 @@ public void onSubscribeClicked(View v) {
246256
nMgr.cancelAll();
247257
}
248258

259+
public void onSetExternalUserId(View view) {
260+
String externalUserId = this.externalUserIdEditText.getText().toString().trim();
261+
262+
OneSignal.setExternalUserId(externalUserId, new OneSignal.OSExternalUserIdUpdateCompletionHandler() {
263+
@Override
264+
public void onComplete(JSONObject results) {
265+
OneSignal.onesignalLog(OneSignal.LOG_LEVEL.VERBOSE, "Set external user id done with results: " + results.toString());
266+
}
267+
});
268+
}
269+
270+
public void onRemoveExternalUserId(View view) {
271+
OneSignal.removeExternalUserId(new OneSignal.OSExternalUserIdUpdateCompletionHandler() {
272+
@Override
273+
public void onComplete(JSONObject results) {
274+
OneSignal.onesignalLog(OneSignal.LOG_LEVEL.VERBOSE, "Remove external user id done with results: " + results.toString());
275+
}
276+
});
277+
}
278+
249279
public void onSetTrigger(View v) {
250280
String triggerKey = this.triggerKeyTextView.getText().toString();
251281
String triggerValue = this.triggerValueTextView.getText().toString();
@@ -308,12 +338,7 @@ public void tagsAvailable(final JSONObject tags) {
308338
}
309339

310340
public void onSetEmailClicked(View v) {
311-
String email = emailTextView.getText().toString();
312-
313-
if (email.length() == 0) {
314-
Log.d("onesingal", "email not set");
315-
return;
316-
}
341+
String email = emailEditText.getText().toString();
317342

318343
OneSignal.setEmail(email, new OneSignal.EmailUpdateHandler() {
319344
@Override
@@ -328,6 +353,20 @@ public void onFailure(OneSignal.EmailUpdateError error) {
328353
});
329354
}
330355

356+
public void onLogoutEmailClicked(View v) {
357+
OneSignal.logoutEmail(new OneSignal.EmailUpdateHandler() {
358+
@Override
359+
public void onSuccess() {
360+
updateTextView("Successfully logged out of email");
361+
}
362+
363+
@Override
364+
public void onFailure(OneSignal.EmailUpdateError error) {
365+
updateTextView("Failed to logout of email with error: " + error.getMessage());
366+
}
367+
});
368+
}
369+
331370
public void onSendOutcomeClicked(View view) {
332371
OneSignal.sendOutcome(outcomeName.getText().toString(), new OneSignal.OutcomeCallback() {
333372
@Override
@@ -361,20 +400,6 @@ public void onSuccess(@Nullable OutcomeEvent outcomeEvent) {
361400
});
362401
}
363402

364-
public void onLogoutEmailClicked(View v) {
365-
OneSignal.logoutEmail(new OneSignal.EmailUpdateHandler() {
366-
@Override
367-
public void onSuccess() {
368-
updateTextView("Successfully logged out of email");
369-
}
370-
371-
@Override
372-
public void onFailure(OneSignal.EmailUpdateError error) {
373-
updateTextView("Failed to logout of email with error: " + error.getMessage());
374-
}
375-
});
376-
}
377-
378403
public void onFullScreenClicked(View v) {
379404
updateIamhost();
380405
}

OneSignalSDK/app/src/main/java/com/onesignal/example/OneSignalExampleApp.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ public void onCreate() {
7070
new ExampleNotificationOpenedHandler(),
7171
new ExampleNotificationReceivedHandler()
7272
);
73-
74-
OneSignal.sendTag("test1", "test1");
7573
}
7674

7775
public static void setOneSignalAppId(@NonNull Context context, @NonNull String id) {

OneSignalSDK/app/src/main/res/layout/activity_main.xml

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,46 @@
3636
android:onClick="onConsentButtonClicked"
3737
android:text="Provide Consent" />
3838

39+
<EditText
40+
android:id="@+id/emailEditText"
41+
android:layout_width="match_parent"
42+
android:layout_height="wrap_content"
43+
android:hint="Email" />
44+
45+
<Button
46+
android:id="@+id/setEmailButton"
47+
android:layout_width="match_parent"
48+
android:layout_height="wrap_content"
49+
android:onClick="onSetEmailClicked"
50+
android:text="Set Email" />
51+
52+
<Button
53+
android:id="@+id/logoutEmailButton"
54+
android:layout_width="match_parent"
55+
android:layout_height="wrap_content"
56+
android:onClick="onLogoutEmailClicked"
57+
android:text="Logout Email" />
58+
59+
<EditText
60+
android:id="@+id/externalUserIdEditText"
61+
android:layout_width="match_parent"
62+
android:layout_height="wrap_content"
63+
android:hint="External user id" />
64+
65+
<Button
66+
android:id="@+id/setExternalUserId"
67+
android:layout_width="match_parent"
68+
android:layout_height="wrap_content"
69+
android:onClick="onSetExternalUserId"
70+
android:text="Set External User Id" />
71+
72+
<Button
73+
android:id="@+id/removeExternalUserId"
74+
android:layout_width="match_parent"
75+
android:layout_height="wrap_content"
76+
android:onClick="onRemoveExternalUserId"
77+
android:text="Remove External User Id" />
78+
3979
<LinearLayout
4080
android:layout_width="match_parent"
4181
android:layout_height="match_parent"
@@ -129,13 +169,6 @@
129169
android:onClick="onSendOutcomeWithValueClicked"
130170
android:text="Send Outcome With Value" />
131171

132-
<Button
133-
android:id="@+id/logoutEmail"
134-
android:layout_width="match_parent"
135-
android:layout_height="wrap_content"
136-
android:onClick="onLogoutEmailClicked"
137-
android:text="Logout Email" />
138-
139172
<LinearLayout
140173
android:layout_width="0px"
141174
android:layout_height="0px"

OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import android.support.annotation.NonNull;
4444
import android.support.annotation.Nullable;
4545
import android.support.annotation.WorkerThread;
46+
import android.text.TextUtils;
4647
import android.util.Log;
4748

4849
import com.onesignal.OneSignalDbContract.NotificationTable;
@@ -138,7 +139,6 @@ public interface InAppMessageClickHandler {
138139
void inAppMessageClicked(OSInAppMessageAction result);
139140
}
140141

141-
142142
/**
143143
* An interface used to handle notifications that are received.
144144
* <br/>
@@ -194,6 +194,13 @@ public static class SendTagsError {
194194
public String getMessage() { return message; }
195195
}
196196

197+
public interface OSExternalUserIdUpdateCompletionHandler {
198+
void onComplete(JSONObject results);
199+
}
200+
201+
interface OSInternalExternalUserIdUpdateCompletionHandler {
202+
void onComplete(String channel, boolean success);
203+
}
197204

198205
public enum EmailErrorType {
199206
VALIDATION, REQUIRES_EMAIL_AUTH, INVALID_OPERATION, NETWORK
@@ -1492,18 +1499,27 @@ public void run() {
14921499
emailLogout.run();
14931500
}
14941501

1495-
public static void setExternalUserId(final String externalId) {
1502+
public static void setExternalUserId(@NonNull final String externalId) {
1503+
setExternalUserId(externalId, null);
1504+
}
1505+
1506+
public static void setExternalUserId(@NonNull final String externalId, @Nullable final OSExternalUserIdUpdateCompletionHandler completionCallback) {
14961507

1497-
if (shouldLogUserPrivacyConsentErrorMessageForMethodName("setExternalId()"))
1508+
if (shouldLogUserPrivacyConsentErrorMessageForMethodName("setExternalUserId()"))
14981509
return;
14991510

15001511
Runnable runSetExternalUserId = new Runnable() {
15011512
@Override
15021513
public void run() {
1514+
if (externalId == null) {
1515+
OneSignal.Log(LOG_LEVEL.WARN, "External id can't be null, set an empty string to remove an external id");
1516+
return;
1517+
}
1518+
15031519
try {
1504-
OneSignalStateSynchronizer.setExternalUserId(externalId);
1520+
OneSignalStateSynchronizer.setExternalUserId(externalId, completionCallback);
15051521
} catch (JSONException exception) {
1506-
String operation = externalId == "" ? "remove" : "set";
1522+
String operation = externalId.equals("") ? "remove" : "set";
15071523
onesignalLog(LOG_LEVEL.ERROR, "Attempted to " + operation + " external ID but encountered a JSON exception");
15081524
exception.printStackTrace();
15091525
}
@@ -1523,8 +1539,15 @@ public static void removeExternalUserId() {
15231539
if (shouldLogUserPrivacyConsentErrorMessageForMethodName("removeExternalUserId()"))
15241540
return;
15251541

1542+
removeExternalUserId(null);
1543+
}
1544+
1545+
public static void removeExternalUserId(final OSExternalUserIdUpdateCompletionHandler completionHandler) {
1546+
if (shouldLogUserPrivacyConsentErrorMessageForMethodName("removeExternalUserId()"))
1547+
return;
1548+
15261549
// to remove the external user ID, the API requires an empty string
1527-
setExternalUserId("");
1550+
setExternalUserId("", completionHandler);
15281551
}
15291552

15301553
/**
@@ -2226,14 +2249,11 @@ static void saveUserId(String id) {
22262249
}
22272250

22282251
static boolean hasEmailId() {
2229-
return getEmailId() != null;
2252+
return !TextUtils.isEmpty(emailId);
22302253
}
22312254

22322255
static String getEmailId() {
2233-
if ("".equals(emailId))
2234-
return null;
2235-
2236-
if (emailId == null && appContext != null) {
2256+
if (TextUtils.isEmpty(emailId) && appContext != null) {
22372257
emailId = OneSignalPrefs.getString(OneSignalPrefs.PREFS_ONESIGNAL,
22382258
OneSignalPrefs.PREFS_OS_EMAIL_ID,null);
22392259
}

OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalPrefs.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,28 @@
4040

4141
class OneSignalPrefs {
4242

43+
// TODO: Remove this once the tasks below have been finished...
44+
// 1. Fix all of the SharedPreference Keys so they are organized by usage with comments
45+
// ex.
46+
// // In-App Messaging
47+
// public static final String PREFS_OS_CACHED_IAMS = "PREFS_OS_CACHED_IAMS";
48+
// public static final String PREFS_OS_DISMISSED_IAMS = "PREFS_OS_DISPLAYED_IAMS";
49+
// public static final String PREFS_OS_IMPRESSIONED_IAMS = "PREFS_OS_IMPRESSIONED_IAMS";
50+
// public static final String PREFS_OS_CLICKED_CLICK_IDS_IAMS = "PREFS_OS_CLICKED_CLICK_IDS_IAMS";
51+
// 2. Match keys with value names
52+
// ex.
53+
// public static final String PREFS_OS_LAST_LOCATION_TIME = "OS_LAST_LOCATION_TIME";
54+
// 3. Follow syntax and make new names relevant (specific and as short as possible)
55+
// ex.
56+
// Start with prefix "PREFS_OS_" + "LAST_LOCATION_TIME"
57+
4358
// SharedPreferences Instances
4459
public static final String PREFS_ONESIGNAL = OneSignal.class.getSimpleName();
4560
public static final String PREFS_PLAYER_PURCHASES = "GTPlayerPurchases";
4661
public static final String PREFS_TRIGGERS = "OneSignalTriggers";
4762

48-
// PREFERENCES KEYS
63+
// SharedPreference Keys
64+
// Unorganized Keys
4965
public static final String PREFS_OS_LAST_LOCATION_TIME = "OS_LAST_LOCATION_TIME";
5066
public static final String PREFS_GT_SOUND_ENABLED = "GT_SOUND_ENABLED";
5167
public static final String PREFS_OS_LAST_SESSION_TIME = "OS_LAST_SESSION_TIME";
@@ -56,7 +72,6 @@ class OneSignalPrefs {
5672
public static final String PREFS_OS_FILTER_OTHER_GCM_RECEIVERS = "OS_FILTER_OTHER_GCM_RECEIVERS";
5773
public static final String PREFS_GT_APP_ID = "GT_APP_ID";
5874
public static final String PREFS_GT_PLAYER_ID = "GT_PLAYER_ID";
59-
public static final String PREFS_OS_EMAIL_ID = "OS_EMAIL_ID";
6075
public static final String PREFS_GT_UNSENT_ACTIVE_TIME = "GT_UNSENT_ACTIVE_TIME";
6176
public static final String PREFS_OS_UNSENT_ATTRIBUTED_ACTIVE_TIME = "OS_UNSENT_ATTRIBUTED_ACTIVE_TIME";
6277
public static final String PREFS_ONESIGNAL_USERSTATE_DEPENDVALYES_ = "ONESIGNAL_USERSTATE_DEPENDVALYES_";
@@ -66,20 +81,25 @@ class OneSignalPrefs {
6681
public static final String PREFS_ONESIGNAL_PLAYER_ID_LAST = "ONESIGNAL_PLAYER_ID_LAST";
6782
public static final String PREFS_ONESIGNAL_PUSH_TOKEN_LAST = "ONESIGNAL_PUSH_TOKEN_LAST";
6883
public static final String PREFS_ONESIGNAL_PERMISSION_ACCEPTED_LAST = "ONESIGNAL_PERMISSION_ACCEPTED_LAST";
69-
public static final String PREFS_ONESIGNAL_EMAIL_ID_LAST = "PREFS_ONESIGNAL_EMAIL_ID_LAST";
70-
public static final String PREFS_ONESIGNAL_EMAIL_ADDRESS_LAST = "PREFS_ONESIGNAL_EMAIL_ADDRESS_LAST";
7184
public static final String PREFS_GT_DO_NOT_SHOW_MISSING_GPS = "GT_DO_NOT_SHOW_MISSING_GPS";
7285
public static final String PREFS_ONESIGNAL_SUBSCRIPTION = "ONESIGNAL_SUBSCRIPTION";
7386
public static final String PREFS_ONESIGNAL_SYNCED_SUBSCRIPTION = "ONESIGNAL_SYNCED_SUBSCRIPTION";
7487
public static final String PREFS_GT_REGISTRATION_ID = "GT_REGISTRATION_ID";
7588
public static final String PREFS_ONESIGNAL_USER_PROVIDED_CONSENT = "ONESIGNAL_USER_PROVIDED_CONSENT";
7689
public static final String PREFS_OS_ETAG_PREFIX = "PREFS_OS_ETAG_PREFIX_";
7790
public static final String PREFS_OS_HTTP_CACHE_PREFIX = "PREFS_OS_HTTP_CACHE_PREFIX_";
91+
// Email
92+
public static final String PREFS_OS_EMAIL_ID = "OS_EMAIL_ID";
93+
public static final String PREFS_ONESIGNAL_EMAIL_ID_LAST = "PREFS_ONESIGNAL_EMAIL_ID_LAST";
94+
public static final String PREFS_ONESIGNAL_EMAIL_ADDRESS_LAST = "PREFS_ONESIGNAL_EMAIL_ADDRESS_LAST";
95+
// In-App Messaging
7896
public static final String PREFS_OS_CACHED_IAMS = "PREFS_OS_CACHED_IAMS";
7997
public static final String PREFS_OS_DISMISSED_IAMS = "PREFS_OS_DISPLAYED_IAMS";
8098
public static final String PREFS_OS_IMPRESSIONED_IAMS = "PREFS_OS_IMPRESSIONED_IAMS";
8199
public static final String PREFS_OS_CLICKED_CLICK_IDS_IAMS = "PREFS_OS_CLICKED_CLICK_IDS_IAMS";
100+
// Receive Receipts (aka Confirmed Deliveries)
82101
public static final String PREFS_OS_RECEIVE_RECEIPTS_ENABLED = "PREFS_OS_RECEIVE_RECEIPTS_ENABLED";
102+
// Outcomes
83103
public static final String PREFS_OS_LAST_ATTRIBUTED_NOTIFICATION_OPEN = "PREFS_OS_LAST_ATTRIBUTED_NOTIFICATION_OPEN";
84104
public static final String PREFS_OS_LAST_NOTIFICATIONS_RECEIVED = "PREFS_OS_LAST_NOTIFICATIONS_RECEIVED";
85105
public static final String PREFS_OS_NOTIFICATION_LIMIT = "PREFS_OS_NOTIFICATION_LIMIT";
@@ -90,7 +110,7 @@ class OneSignalPrefs {
90110
public static final String PREFS_OS_OUTCOMES_CURRENT_SESSION = "PREFS_OS_OUTCOMES_CURRENT_SESSION";
91111
public static final String PREFS_OS_UNATTRIBUTED_UNIQUE_OUTCOME_EVENTS_SENT = "PREFS_OS_UNATTRIBUTED_UNIQUE_OUTCOME_EVENTS_SENT";
92112

93-
// PLAYER PURCHASE KEYS
113+
// Player Purchase Keys
94114
static final String PREFS_PURCHASE_TOKENS = "purchaseTokens";
95115
static final String PREFS_EXISTING_PURCHASES = "ExistingPurchases";
96116

0 commit comments

Comments
 (0)