Skip to content

Commit 52ea988

Browse files
authored
Merge pull request #1909 from OneSignal/AddUserStateChangeObserver
Add getter for onesignalId and UserStateObserver
2 parents c5f48ad + 62e12c4 commit 52ea988

File tree

10 files changed

+147
-13
lines changed

10 files changed

+147
-13
lines changed

Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/application/MainApplication.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import androidx.multidex.MultiDexApplication;
99

1010
import com.onesignal.OneSignal;
11-
import com.onesignal.inAppMessages.IInAppMessage;
1211
import com.onesignal.inAppMessages.IInAppMessageClickListener;
1312
import com.onesignal.inAppMessages.IInAppMessageClickEvent;
1413
import com.onesignal.inAppMessages.IInAppMessageDidDismissEvent;
@@ -26,6 +25,9 @@
2625
import com.onesignal.sdktest.constant.Text;
2726
import com.onesignal.sdktest.notification.OneSignalNotificationSender;
2827
import com.onesignal.sdktest.util.SharedPreferenceUtil;
28+
import com.onesignal.user.state.IUserStateObserver;
29+
import com.onesignal.user.state.UserChangedState;
30+
import com.onesignal.user.state.UserState;
2931

3032
import org.json.JSONObject;
3133

@@ -116,6 +118,14 @@ public void onWillDisplay(@NonNull INotificationWillDisplayEvent event) {
116118
}
117119
});
118120

121+
OneSignal.getUser().addObserver(new IUserStateObserver() {
122+
@Override
123+
public void onUserStateChange(@NonNull UserChangedState state) {
124+
UserState currentUserState = state.getCurrent();
125+
Log.v(Tag.LOG_TAG, "onUserStateChange fired " + currentUserState.toJSONObject());
126+
}
127+
});
128+
119129
OneSignal.getInAppMessages().setPaused(true);
120130
OneSignal.getLocation().setShared(false);
121131

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import android.app.Activity;
44
import android.content.Context;
55
import com.google.android.material.appbar.AppBarLayout;
6-
76
import androidx.annotation.NonNull;
87
import androidx.annotation.Nullable;
98
import androidx.annotation.RequiresApi;
@@ -13,10 +12,8 @@
1312
import androidx.recyclerview.widget.LinearLayoutManager;
1413
import androidx.recyclerview.widget.RecyclerView;
1514
import androidx.appcompat.widget.Toolbar;
16-
1715
import android.content.Intent;
1816
import android.os.Build;
19-
import android.util.Log;
2017
import android.util.Pair;
2118
import android.view.View;
2219
import android.view.ViewTreeObserver;
@@ -25,13 +22,10 @@
2522
import android.widget.RelativeLayout;
2623
import android.widget.Switch;
2724
import android.widget.TextView;
28-
2925
import com.onesignal.Continue;
3026
import com.onesignal.OneSignal;
3127
import com.onesignal.sdktest.adapter.SubscriptionRecyclerViewAdapter;
32-
import com.onesignal.user.subscriptions.IEmailSubscription;
3328
import com.onesignal.user.subscriptions.IPushSubscription;
34-
import com.onesignal.user.subscriptions.ISmsSubscription;
3529
import com.onesignal.sdktest.R;
3630
import com.onesignal.sdktest.activity.SecondaryActivity;
3731
import com.onesignal.sdktest.adapter.InAppMessageRecyclerViewAdapter;
@@ -57,7 +51,6 @@
5751
import com.onesignal.user.subscriptions.ISubscription;
5852
import com.onesignal.user.subscriptions.IPushSubscriptionObserver;
5953
import com.onesignal.user.subscriptions.PushSubscriptionChangedState;
60-
6154
import java.util.ArrayList;
6255
import java.util.HashMap;
6356
import java.util.Map;

MIGRATION_GUIDE.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,12 @@ The user name space is accessible via `OneSignal.User` (in Kotlin) or `OneSignal
259259
| `val pushSubscription: IPushSubscription` | `IPushSubscription getPushSubscription()` | *The push subscription associated to the current user.* |
260260
| `fun setLanguage(value: String)` | `void setLanguage(String value)` | *Set the 2-character language either as a detected language or explicitly set for this user.* |
261261
| `fun pushSubscription.addChangeHandler(handler: ISubscriptionChangedHandler)` | `void pushSubscription.addChangeHandler(ISubscriptionChangedHandler handler)` | *Adds a change handler that will run whenever the push subscription has been changed.* |
262+
| `val onesignalId: String` | `String getOnesignalId()` | *Returns the OneSignal ID for the current user, which can be the empty string if it is not yet available.* |
263+
| `val externalId: String` | `String getExternalId()` | *Returns the external ID for the current user, which can be the empty string if not set.* |
264+
| `fun addObserver(observer: IUserStateObserver)` | `void addObserve(IUserStateObserver observer)` | *The `IUserStateObserver.onUserStateChange` method will be fired on the passed-in object when the user state changes. The User State contains the onesignalId and externalId (which can be empty strings), and the observer will be fired when these values change.* |
265+
| `fun removeObserver(observer: IUserStateObserver)` | `void removeObserver(IUserStateObserver observer)` | *Remove a user state observer that has been previously added.* |
262266
| `fun addAlias(label: String, id: String)` | `void addAlias(String label, String id)` | *Set an alias for the current user. If this alias already exists it will be overwritten.* |
263-
| `fun addAliases(aliases: Map<String, String>)` | `void addAliases(Map<String, String> aliases)` | S*et aliases for the current user. If any alias already exists it will be overwritten.* |
267+
| `fun addAliases(aliases: Map<String, String>)` | `void addAliases(Map<String, String> aliases)` | *Set aliases for the current user. If any alias already exists it will be overwritten.* |
264268
| `fun removeAlias(label: String)` | `void removeAlias(String label)` | *Remove an alias from the current user.* |
265269
| `fun removeAliases(labels: Collection<String>)` | `void removeAliases(Collection<String> labels)` | *Remove multiple aliases from the current user.* |
266270
| `fun addEmail(email: String)` | `void addEmail(String email)` | *Add a new email subscription to the current user.* |

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/IUserManager.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.onesignal.user
22

33
import com.onesignal.OneSignal
4+
import com.onesignal.user.state.IUserStateObserver
45
import com.onesignal.user.subscriptions.IPushSubscription
56

67
/**
@@ -25,6 +26,19 @@ interface IUserManager {
2526
*/
2627
val pushSubscription: IPushSubscription
2728

29+
/**
30+
* The UUID generated by OneSignal to represent a user, empty if this is currently unavailable
31+
*/
32+
val onesignalId: String
33+
34+
/**
35+
* The External ID is OneSignal's default and recommended alias label. This should be the main
36+
* identifier you use to identify users. It is set when calling the [OneSignal.login] method.
37+
*
38+
* This is empty if the External ID has not been set.
39+
*/
40+
val externalId: String
41+
2842
/**
2943
* Set the 2-character language either as a detected language or explicitly set for this user. See
3044
* See [Supported Languages | OneSignal](https://documentation.onesignal.com/docs/language-localization#what-languages-are-supported)
@@ -138,4 +152,18 @@ interface IUserManager {
138152
* Return a copy of all local tags from the current user.
139153
*/
140154
fun getTags(): Map<String, String>
155+
156+
/**
157+
* Add an observer to the user state, allowing the provider to be
158+
* notified whenever the user state has changed.
159+
*
160+
* Important: When using the observer to retrieve the onesignalId, check the externalId as well
161+
* to confirm the values are associated with the expected user.
162+
*/
163+
fun addObserver(observer: IUserStateObserver)
164+
165+
/**
166+
* Remove an observer from the user state.
167+
*/
168+
fun removeObserver(observer: IUserStateObserver)
141169
}

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

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package com.onesignal.user.internal
22

3+
import com.onesignal.common.IDManager
34
import com.onesignal.common.OneSignalUtils
5+
import com.onesignal.common.events.EventProducer
6+
import com.onesignal.common.modeling.ISingletonModelStoreChangeHandler
7+
import com.onesignal.common.modeling.ModelChangedArgs
48
import com.onesignal.core.internal.language.ILanguageContext
59
import com.onesignal.debug.LogLevel
610
import com.onesignal.debug.internal.logging.Logging
@@ -12,23 +16,31 @@ import com.onesignal.user.internal.properties.PropertiesModel
1216
import com.onesignal.user.internal.properties.PropertiesModelStore
1317
import com.onesignal.user.internal.subscriptions.ISubscriptionManager
1418
import com.onesignal.user.internal.subscriptions.SubscriptionList
19+
import com.onesignal.user.state.IUserStateObserver
20+
import com.onesignal.user.state.UserChangedState
21+
import com.onesignal.user.state.UserState
1522
import com.onesignal.user.subscriptions.IPushSubscription
1623

1724
internal open class UserManager(
1825
private val _subscriptionManager: ISubscriptionManager,
1926
private val _identityModelStore: IdentityModelStore,
2027
private val _propertiesModelStore: PropertiesModelStore,
2128
private val _languageContext: ILanguageContext,
22-
) : IUserManager {
23-
val externalId: String?
24-
get() = _identityModel.externalId
29+
) : IUserManager, ISingletonModelStoreChangeHandler<IdentityModel> {
30+
override val onesignalId: String
31+
get() = if (IDManager.isLocalId(_identityModel.onesignalId)) "" else _identityModel.onesignalId
32+
33+
override val externalId: String
34+
get() = _identityModel.externalId ?: ""
2535

2636
val aliases: Map<String, String>
2737
get() = _identityModel.filter { it.key != IdentityModel::id.name }.toMap()
2838

2939
val subscriptions: SubscriptionList
3040
get() = _subscriptionManager.subscriptions
3141

42+
val changeHandlersNotifier = EventProducer<IUserStateObserver>()
43+
3244
override val pushSubscription: IPushSubscription
3345
get() = _subscriptionManager.subscriptions.push
3446

@@ -42,6 +54,10 @@ internal open class UserManager(
4254
_languageContext.language = value
4355
}
4456

57+
init {
58+
_identityModelStore.subscribe(this)
59+
}
60+
4561
override fun addAlias(
4662
label: String,
4763
id: String,
@@ -219,4 +235,29 @@ internal open class UserManager(
219235
override fun getTags(): Map<String, String> {
220236
return _propertiesModel.tags.toMap()
221237
}
238+
239+
override fun addObserver(observer: IUserStateObserver) {
240+
changeHandlersNotifier.subscribe(observer)
241+
}
242+
243+
override fun removeObserver(observer: IUserStateObserver) {
244+
changeHandlersNotifier.unsubscribe(observer)
245+
}
246+
247+
override fun onModelReplaced(
248+
model: IdentityModel,
249+
tag: String,
250+
) { }
251+
252+
override fun onModelUpdated(
253+
args: ModelChangedArgs,
254+
tag: String,
255+
) {
256+
if (args.property == IdentityConstants.ONESIGNAL_ID) {
257+
val newUserState = UserState(args.newValue.toString(), externalId)
258+
this.changeHandlersNotifier.fire {
259+
it.onUserStateChange(UserChangedState(newUserState))
260+
}
261+
}
262+
}
222263
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.onesignal.user.state
2+
3+
/**
4+
* A user state changed observer. Implement this interface and provide the implementation
5+
* to be notified when the user state has changed.
6+
*/
7+
interface IUserStateObserver {
8+
/**
9+
* Called when the user state this change handler was added to, has changed. A
10+
* user state can change when user has logged in or out
11+
*
12+
* @param state The user changed state.
13+
*/
14+
fun onUserStateChange(state: UserChangedState)
15+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.onesignal.user.state
2+
3+
import org.json.JSONObject
4+
5+
class UserChangedState(
6+
val current: UserState,
7+
) {
8+
fun toJSONObject(): JSONObject {
9+
return JSONObject()
10+
.put("current", current.toJSONObject())
11+
}
12+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.onesignal.user.state
2+
3+
import org.json.JSONObject
4+
5+
/**
6+
* A user state.
7+
*/
8+
class UserState(
9+
/**
10+
* The unique identifier for your OneSignal account. This will be an empty string until the
11+
* user has been successfully logged in on the backend and assigned an ID.
12+
* Use [addObserver] to be notified when the [onesignalId] has been successfully assigned.
13+
*/
14+
val onesignalId: String,
15+
/**
16+
* The external identifier that you use to identify users. Use [addObserver] to be notified
17+
* when the [externalId] has been successfully assigned. This will be an empty string if no
18+
* external identifier has been assigned to the associated [onesignalId].
19+
*/
20+
val externalId: String,
21+
) {
22+
fun toJSONObject(): JSONObject {
23+
return JSONObject()
24+
.put("onesignalId", onesignalId)
25+
.put("externalId", externalId)
26+
}
27+
}

OneSignalSDK/onesignal/core/src/test/java/com/onesignal/mocks/MockHelper.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ object MockHelper {
6565
action(identityModel)
6666
}
6767

68-
val mockIdentityStore = mockk<IdentityModelStore>()
68+
val mockIdentityStore = mockk<IdentityModelStore>(relaxed = true)
6969

7070
every { mockIdentityStore.model } returns identityModel
7171

OneSignalSDK/onesignal/notifications/consumer-rules.pro

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
void onPushSubscriptionChange(com.onesignal.user.subscriptions.PushSubscriptionChangedState);
2121
}
2222

23+
-keep class ** implements com.onesignal.user.state.IUserStateObserver {
24+
void onUserStateChange(com.onesignal.user.state.UserChangedState);
25+
}
26+
2327
-keep class ** implements com.onesignal.notifications.INotificationServiceExtension{
2428
void onNotificationReceived(com.onesignal.notifications.INotificationReceivedEvent);
2529
}

0 commit comments

Comments
 (0)