Skip to content

Commit 16ac66b

Browse files
authored
Merge pull request #1712 from OneSignal/user-model/alpha-bug-fixes
[User Model] alpha bug fixes
2 parents b7d2022 + 64cdfad commit 16ac66b

File tree

15 files changed

+247
-126
lines changed

15 files changed

+247
-126
lines changed

Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ public class MainActivityViewModel implements ActivityViewModel, ISubscriptionCh
8686
private RelativeLayout appIdRelativeLayout;
8787
private TextView appIdTitleTextView;
8888
private TextView appIdTextView;
89-
private Button switchUserButton;
89+
private Button loginUserButton;
90+
private Button logoutUserButton;
9091

9192
// Alias
9293
private TextView aliasTitleTextView;
@@ -213,7 +214,8 @@ public ActivityViewModel onActivityCreated(Context context) {
213214
appIdTitleTextView = getActivity().findViewById(R.id.main_activity_account_details_app_id_title_text_view);
214215
appIdTextView = getActivity().findViewById(R.id.main_activity_account_details_app_id_text_view);
215216
revokeConsentButton = getActivity().findViewById(R.id.main_activity_app_revoke_consent_button);
216-
switchUserButton = getActivity().findViewById(R.id.main_activity_switch_user_button);
217+
loginUserButton = getActivity().findViewById(R.id.main_activity_login_user_button);
218+
logoutUserButton = getActivity().findViewById(R.id.main_activity_logout_user_button);
217219

218220
aliasTitleTextView = getActivity().findViewById(R.id.main_activity_aliases_title_text_view);
219221
noAliasesTextView = getActivity().findViewById(R.id.main_activity_aliases_no_aliases_text_view);
@@ -301,7 +303,8 @@ public ActivityViewModel setupInterfaceElements() {
301303
font.applyFont(privacyConsentAllowButton, font.saralaBold);
302304
font.applyFont(appIdTitleTextView, font.saralaBold);
303305
font.applyFont(appIdTextView, font.saralaRegular);
304-
font.applyFont(switchUserButton, font.saralaBold);
306+
font.applyFont(loginUserButton, font.saralaBold);
307+
font.applyFont(logoutUserButton, font.saralaBold);
305308
font.applyFont(aliasTitleTextView, font.saralaBold);
306309
font.applyFont(noAliasesTextView, font.saralaBold);
307310
font.applyFont(emailHeaderTextView, font.saralaBold);
@@ -401,37 +404,28 @@ public void onScrollChanged() {
401404
private void setupAppLayout() {
402405
revokeConsentButton.setOnClickListener(v -> togglePrivacyConsent(false));
403406

404-
if(SharedPreferenceUtil.getCachedIsLoggedIn(context)) {
405-
switchUserButton.setText(R.string.logout_user);
406-
}
407-
408-
switchUserButton.setOnClickListener(v -> {
409-
if(SharedPreferenceUtil.getCachedIsLoggedIn(context)) {
410-
OneSignal.logout(Continue.with(r -> {
411-
SharedPreferenceUtil.cacheIsLoggedIn(context, false);
412-
switchUserButton.setText(R.string.login_user);
413-
refreshState();
414-
}));
415-
}
416-
else {
417-
dialog.createUpdateAlertDialog("", Dialog.DialogAction.LOGIN, ProfileUtil.FieldType.EXTERNAL_USER_ID, new UpdateAlertDialogCallback() {
418-
@Override
419-
public void onSuccess(String update) {
420-
if (update != null && !update.isEmpty()) {
421-
OneSignal.login(update, Continue.with(r -> {
422-
SharedPreferenceUtil.cacheIsLoggedIn(context, true);
423-
switchUserButton.setText(R.string.logout_user);
424-
refreshState();
425-
}));
426-
}
407+
loginUserButton.setOnClickListener(v -> {
408+
dialog.createUpdateAlertDialog("", Dialog.DialogAction.LOGIN, ProfileUtil.FieldType.EXTERNAL_USER_ID, new UpdateAlertDialogCallback() {
409+
@Override
410+
public void onSuccess(String update) {
411+
if (update != null && !update.isEmpty()) {
412+
OneSignal.login(update, Continue.with(r -> {
413+
refreshState();
414+
}));
427415
}
416+
}
428417

429-
@Override
430-
public void onFailure() {
418+
@Override
419+
public void onFailure() {
431420

432-
}
433-
});
434-
}
421+
}
422+
});
423+
});
424+
425+
logoutUserButton.setOnClickListener(v -> {
426+
OneSignal.logout(Continue.with(r -> {
427+
refreshState();
428+
}));
435429
});
436430
}
437431

Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/SharedPreferenceUtil.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ public class SharedPreferenceUtil {
1414
public static final String USER_EXTERNAL_USER_ID_SHARED_PREF = "USER_EXTERNAL_USER_ID_SHARED_PREF";
1515
private static final String LOCATION_SHARED_PREF = "LOCATION_SHARED_PREF";
1616
private static final String IN_APP_MESSAGING_PAUSED_PREF = "IN_APP_MESSAGING_PAUSED_PREF";
17-
private static final String IS_LOGGED_IN = "IS_LOGGED_IN";
1817

1918
private static SharedPreferences getSharedPreference(Context context) {
2019
return context.getSharedPreferences(APP_SHARED_PREFS, Context.MODE_PRIVATE);
@@ -44,10 +43,6 @@ public static boolean getCachedInAppMessagingPausedStatus(Context context) {
4443
return getSharedPreference(context).getBoolean(IN_APP_MESSAGING_PAUSED_PREF, true);
4544
}
4645

47-
public static boolean getCachedIsLoggedIn(Context context) {
48-
return getSharedPreference(context).getBoolean(IS_LOGGED_IN, false);
49-
}
50-
5146
public static void cacheOneSignalAppId(Context context, String appId) {
5247
getSharedPreference(context).edit().putString(OS_APP_ID_SHARED_PREF, appId).apply();
5348
}
@@ -67,8 +62,4 @@ public static void cacheLocationSharedStatus(Context context, boolean subscribed
6762
public static void cacheInAppMessagingPausedStatus(Context context, boolean paused) {
6863
getSharedPreference(context).edit().putBoolean(IN_APP_MESSAGING_PAUSED_PREF, paused).apply();
6964
}
70-
71-
public static void cacheIsLoggedIn(Context context, boolean isLoggedIn) {
72-
getSharedPreference(context).edit().putBoolean(IS_LOGGED_IN, isLoggedIn).apply();
73-
}
7465
}

Examples/OneSignalDemo/app/src/main/res/layout/main_activity_layout.xml

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@
245245
android:visibility="visible"/>
246246

247247
</LinearLayout>
248+
249+
<!-- Login -->
248250
<LinearLayout
249251
android:layout_width="match_parent"
250252
android:layout_height="56dp"
@@ -258,7 +260,7 @@
258260
android:orientation="vertical">
259261

260262
<Button
261-
android:id="@+id/main_activity_switch_user_button"
263+
android:id="@+id/main_activity_login_user_button"
262264
android:layout_width="match_parent"
263265
android:layout_height="match_parent"
264266
android:text="@string/login_user"
@@ -268,6 +270,30 @@
268270
android:visibility="visible"/>
269271

270272
</LinearLayout>
273+
274+
<!-- Logout -->
275+
<LinearLayout
276+
android:layout_width="match_parent"
277+
android:layout_height="56dp"
278+
android:layout_gravity="center"
279+
android:layout_marginStart="12dp"
280+
android:layout_marginTop="4dp"
281+
android:layout_marginEnd="12dp"
282+
android:layout_marginBottom="12dp"
283+
android:background="@color/colorPrimary"
284+
android:gravity="center"
285+
android:orientation="vertical">
286+
287+
<Button
288+
android:id="@+id/main_activity_logout_user_button"
289+
android:layout_width="match_parent"
290+
android:layout_height="match_parent"
291+
android:text="@string/logout_user"
292+
android:textSize="19sp"
293+
android:textColor="@android:color/white"
294+
android:background="@drawable/ripple_selector_white_red"
295+
android:visibility="visible"/>
296+
</LinearLayout>
271297
</LinearLayout>
272298

273299
<!-- Aliases -->

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ class ConfigModel : Model() {
1212
get() = getStringProperty(::appId.name)
1313
set(value) { setStringProperty(::appId.name, value) }
1414

15+
/**
16+
* This device's push subscription ID.
17+
*/
18+
var pushSubscriptionId: String?
19+
get() = getOptStringProperty(::pushSubscriptionId.name)
20+
set(value) { setOptStringProperty(::pushSubscriptionId.name, value) }
21+
1522
/**
1623
* The API URL String.
1724
*/

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -62,40 +62,40 @@ internal class ConfigModelStoreListener(
6262
var success = false
6363
do {
6464
try {
65-
val params = _paramsBackendService.fetchParams(
66-
appId,
67-
_subscriptionManager.subscriptions.push.id.ifEmpty { null }
68-
)
65+
val params = _paramsBackendService.fetchParams(appId, _subscriptionManager.subscriptions.push.id.ifEmpty { null })
6966

67+
// copy current model into new model, then override with what comes down.
7068
val config = ConfigModel()
69+
config.initializeFromModel(null, _configModelStore.model)
70+
71+
// these are always copied from the backend params
7172
config.appId = appId
72-
config.enterprise = params.enterprise ?: _configModelStore.model.enterprise
73-
config.useIdentityVerification = params.useIdentityVerification ?: _configModelStore.model.useIdentityVerification
7473
config.notificationChannels = params.notificationChannels
75-
config.firebaseAnalytics = params.firebaseAnalytics ?: _configModelStore.model.firebaseAnalytics
76-
config.restoreTTLFilter = params.restoreTTLFilter ?: _configModelStore.model.restoreTTLFilter
7774
config.googleProjectNumber = params.googleProjectNumber
78-
config.clearGroupOnSummaryClick = params.clearGroupOnSummaryClick ?: _configModelStore.model.clearGroupOnSummaryClick
79-
config.receiveReceiptEnabled = params.receiveReceiptEnabled ?: _configModelStore.model.receiveReceiptEnabled
80-
config.disableGMSMissingPrompt = params.disableGMSMissingPrompt ?: _configModelStore.model.disableGMSMissingPrompt
81-
config.unsubscribeWhenNotificationsDisabled = params.unsubscribeWhenNotificationsDisabled ?: _configModelStore.model.unsubscribeWhenNotificationsDisabled
82-
config.locationShared = params.locationShared ?: _configModelStore.model.locationShared
83-
config.requiresPrivacyConsent = params.requiresUserPrivacyConsent ?: _configModelStore.model.requiresPrivacyConsent
84-
config.opRepoExecutionInterval = params.opRepoExecutionInterval ?: _configModelStore.model.opRepoExecutionInterval
85-
config.givenPrivacyConsent = _configModelStore.model.givenPrivacyConsent
86-
87-
config.influenceParams.notificationLimit = params.influenceParams.notificationLimit ?: _configModelStore.model.influenceParams.notificationLimit
88-
config.influenceParams.indirectNotificationAttributionWindow = params.influenceParams.indirectNotificationAttributionWindow ?: _configModelStore.model.influenceParams.indirectNotificationAttributionWindow
89-
config.influenceParams.iamLimit = params.influenceParams.iamLimit ?: _configModelStore.model.influenceParams.iamLimit
90-
config.influenceParams.indirectIAMAttributionWindow = params.influenceParams.indirectIAMAttributionWindow ?: _configModelStore.model.influenceParams.indirectIAMAttributionWindow
91-
config.influenceParams.isDirectEnabled = params.influenceParams.isDirectEnabled ?: _configModelStore.model.influenceParams.isDirectEnabled
92-
config.influenceParams.isIndirectEnabled = params.influenceParams.isIndirectEnabled ?: _configModelStore.model.influenceParams.isIndirectEnabled
93-
config.influenceParams.isUnattributedEnabled = params.influenceParams.isUnattributedEnabled ?: _configModelStore.model.influenceParams.isUnattributedEnabled
94-
9575
config.fcmParams.projectId = params.fcmParams.projectId
9676
config.fcmParams.appId = params.fcmParams.appId
9777
config.fcmParams.apiKey = params.fcmParams.apiKey
9878

79+
// these are only copied from the backend params when the backend has set them.
80+
params.enterprise?.let { config.enterprise = it }
81+
params.useIdentityVerification?.let { config.useIdentityVerification = it }
82+
params.firebaseAnalytics?.let { config.firebaseAnalytics = it }
83+
params.restoreTTLFilter?.let { config.restoreTTLFilter = it }
84+
params.clearGroupOnSummaryClick?.let { config.clearGroupOnSummaryClick = it }
85+
params.receiveReceiptEnabled?.let { config.receiveReceiptEnabled = it }
86+
params.disableGMSMissingPrompt?.let { config.disableGMSMissingPrompt = it }
87+
params.unsubscribeWhenNotificationsDisabled?.let { config.unsubscribeWhenNotificationsDisabled = it }
88+
params.locationShared?.let { config.locationShared = it }
89+
params.requiresUserPrivacyConsent?.let { config.requiresPrivacyConsent = it }
90+
params.opRepoExecutionInterval?.let { config.opRepoExecutionInterval = it }
91+
params.influenceParams.notificationLimit?.let { config.influenceParams.notificationLimit = it }
92+
params.influenceParams.indirectNotificationAttributionWindow?.let { config.influenceParams.indirectNotificationAttributionWindow = it }
93+
params.influenceParams.iamLimit?.let { config.influenceParams.iamLimit = it }
94+
params.influenceParams.indirectIAMAttributionWindow?.let { config.influenceParams.indirectIAMAttributionWindow = it }
95+
params.influenceParams.isDirectEnabled?.let { config.influenceParams.isDirectEnabled = it }
96+
params.influenceParams.isIndirectEnabled?.let { config.influenceParams.isIndirectEnabled = it }
97+
params.influenceParams.isUnattributedEnabled?.let { config.influenceParams.isUnattributedEnabled }
98+
9999
_configModelStore.replace(config, ModelChangeTags.HYDRATE)
100100
success = true
101101
} catch (ex: BackendException) {

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,10 @@ internal class OneSignalImp : IOneSignal, IServiceProvider {
266266

267267
// only allow one login/logout at a time
268268
_loginMutex.withLock {
269+
if (_identityModelStore!!.model.externalId == null) {
270+
return
271+
}
272+
269273
createAndSwitchToNewUser()
270274
_operationRepo!!.enqueue(LoginUserOperation(_configModel!!.appId, _identityModelStore!!.model.onesignalId, _identityModelStore!!.model.externalId))
271275
// TODO: remove JWT Token for all future requests.
@@ -291,22 +295,29 @@ internal class OneSignalImp : IOneSignal, IServiceProvider {
291295
val subscriptions = mutableListOf<SubscriptionModel>()
292296

293297
// Create the push subscription for this device under the new user, copying the current
294-
// user's push subscription if one exists.
295-
val currentPushSubscription = _subscriptionModelStore!!.list().firstOrNull { it.type == SubscriptionType.PUSH }
298+
// user's push subscription if one exists. We also copy the ID. If the ID is local there
299+
// will already be a CreateSubscriptionOperation on the queue. If the ID is remote the subscription
300+
// will be automatically transferred over to this new user being created. If there is no
301+
// current push subscription we do a "normal" replace which will drive adding a CreateSubscriptionOperation
302+
// to the queue.
303+
val currentPushSubscription = _subscriptionModelStore!!.list().firstOrNull { it.id == _configModel!!.pushSubscriptionId }
296304
val newPushSubscription = SubscriptionModel()
297305

298-
newPushSubscription.id = IDManager.createLocalId()
306+
newPushSubscription.id = currentPushSubscription?.id ?: IDManager.createLocalId()
299307
newPushSubscription.type = SubscriptionType.PUSH
300308
newPushSubscription.optedIn = currentPushSubscription?.optedIn ?: true
301309
newPushSubscription.address = currentPushSubscription?.address ?: ""
302310
newPushSubscription.status = currentPushSubscription?.status ?: SubscriptionStatus.NO_PERMISSION
303311

312+
// ensure we always know this devices push subscription ID
313+
_configModel!!.pushSubscriptionId = newPushSubscription.id
314+
304315
subscriptions.add(newPushSubscription)
305316

306317
// The next 4 lines makes this user the effective user locally. We clear the subscriptions
307-
// first as an internal change because we don't want to drive deleting the cleared subscriptions
318+
// first as a `NO_PROPOGATE` change because we don't want to drive deleting the cleared subscriptions
308319
// on the backend. Once cleared we can then setup the new identity/properties model, and add
309-
// the new user's subscriptions as a "normal" change, which will drive changes to the backend.
320+
// the new user's subscriptions as a `NORMAL` change, which will drive changes to the backend.
310321
_subscriptionModelStore!!.clear(ModelChangeTags.NO_PROPOGATE)
311322
_identityModelStore!!.replace(identityModel)
312323
_propertiesModelStore!!.replace(propertiesModel)

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/ISubscriptionBackendService.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,17 @@ interface ISubscriptionBackendService {
3131
* Delete an existing subscription.
3232
*
3333
* @param appId The ID of the OneSignal application this subscription exists under.
34-
* @param subscriptionId The ID of the subscription to update.
34+
* @param subscriptionId The ID of the subscription to delete.
3535
*/
3636
suspend fun deleteSubscription(appId: String, subscriptionId: String)
37+
38+
/**
39+
* Transfer an existing subscription to the user specified.
40+
*
41+
* @param appId The ID of the OneSignal application this subscription exists under.
42+
* @param subscriptionId The ID of the subscription to transfer.
43+
* @param aliasLabel The alias label of the user to transfer the subscription under.
44+
* @param aliasValue The identifier within the [aliasLabel] that identifies the user to transfer under.
45+
*/
46+
suspend fun transferSubscription(appId: String, subscriptionId: String, aliasLabel: String, aliasValue: String)
3747
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/SubscriptionBackendService.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,15 @@ internal class SubscriptionBackendService(
6969
throw BackendException(response.statusCode, response.payload)
7070
}
7171
}
72+
73+
override suspend fun transferSubscription(appId: String, subscriptionId: String, aliasLabel: String, aliasValue: String) {
74+
val requestJSON = JSONObject()
75+
.put("identity", JSONObject().put(aliasLabel, aliasValue))
76+
77+
val response = _httpClient.patch("apps/$appId/subscriptions/$subscriptionId/owner", requestJSON)
78+
79+
if (!response.isSuccess) {
80+
throw BackendException(response.statusCode, response.payload)
81+
}
82+
}
7283
}

0 commit comments

Comments
 (0)