Skip to content

Commit 66a156f

Browse files
committed
Add user state observer
1 parent f9c206e commit 66a156f

File tree

5 files changed

+154
-3
lines changed

5 files changed

+154
-3
lines changed

android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import android.util.Log;
44

5+
import com.onesignal.user.state.UserChangedState;
6+
import com.onesignal.user.state.UserState;
57
import com.onesignal.user.subscriptions.ISubscription;
68
import com.onesignal.user.subscriptions.IPushSubscription;
79
import com.onesignal.user.subscriptions.PushSubscriptionChangedState;
@@ -187,6 +189,18 @@ static HashMap<String, Object> convertPushSubscriptionState(PushSubscriptionStat
187189
return hash;
188190
}
189191

192+
static HashMap<String, Object> convertUserState(UserState state) throws JSONException {
193+
HashMap<String, Object> hash = new HashMap<>();
194+
195+
String onesignalId = setNullIfEmpty(state.getOnesignalId());
196+
String externalId = setNullIfEmpty(state.getExternalId());
197+
198+
hash.put("onesignalId", onesignalId);
199+
hash.put("externalId", externalId);
200+
201+
return hash;
202+
}
203+
190204
static HashMap<String, Object> convertOnPushSubscriptionChange(PushSubscriptionChangedState changedState) throws JSONException {
191205
HashMap<String, Object> hash = new HashMap<>();
192206

@@ -197,6 +211,14 @@ static HashMap<String, Object> convertOnPushSubscriptionChange(PushSubscriptionC
197211
return hash;
198212
}
199213

214+
static HashMap<String, Object> convertOnUserStateChange(UserChangedState changedState) throws JSONException {
215+
HashMap<String, Object> hash = new HashMap<>();
216+
217+
218+
hash.put("current", convertUserState(changedState.getCurrent()));
219+
220+
return hash;
221+
}
200222

201223
static HashMap<String, Object> convertJSONObjectToHashMap(JSONObject object) throws JSONException {
202224
HashMap<String, Object> hash = new HashMap<>();
@@ -242,4 +264,9 @@ else if (val instanceof JSONObject)
242264

243265
return list;
244266
}
267+
268+
/** Helper method to return null value if string is empty **/
269+
static String setNullIfEmpty(String value) {
270+
return value.isEmpty() ? null : value;
271+
}
245272
}

android/src/main/java/com/onesignal/flutter/OneSignalUser.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import com.onesignal.OneSignal;
44
import com.onesignal.debug.LogLevel;
5+
import com.onesignal.debug.internal.logging.Logging;
6+
import com.onesignal.user.state.IUserStateObserver;
7+
import com.onesignal.user.state.UserChangedState;
58

69
import org.json.JSONException;
710
import org.json.JSONObject;
@@ -18,8 +21,7 @@
1821
import io.flutter.plugin.common.PluginRegistry;
1922
import io.flutter.plugin.common.PluginRegistry.Registrar;
2023

21-
public class OneSignalUser extends FlutterRegistrarResponder implements MethodCallHandler {
22-
private MethodChannel channel;
24+
public class OneSignalUser extends FlutterRegistrarResponder implements MethodCallHandler, IUserStateObserver {
2325

2426
static void registerWith(BinaryMessenger messenger) {
2527
OneSignalUser controller = new OneSignalUser();
@@ -54,6 +56,8 @@ else if (call.method.contentEquals("OneSignal#removeTags"))
5456
this.removeTags(call, result);
5557
else if (call.method.contentEquals("OneSignal#getTags"))
5658
this.getTags(call, result);
59+
else if (call.method.contentEquals("OneSignal#lifecycleInit"))
60+
this.lifecycleInit();
5761
else
5862
replyNotImplemented(result);
5963
}
@@ -67,6 +71,10 @@ private void setLanguage(MethodCall call, Result result) {
6771
replySuccess(result, null);
6872
}
6973

74+
private void lifecycleInit() {
75+
OneSignal.getUser().addObserver(this);
76+
}
77+
7078
private void getOnesignalId(MethodCall call, Result result) {
7179
String onesignalId = OneSignal.getUser().getOnesignalId();
7280
if (onesignalId.isEmpty()) {
@@ -150,4 +158,14 @@ private void removeTags(MethodCall call, Result result) {
150158
private void getTags(MethodCall call, Result result) {
151159
replySuccess(result, OneSignal.getUser().getTags());
152160
}
161+
162+
@Override
163+
public void onUserStateChange(UserChangedState userChangedState) {
164+
try {
165+
invokeMethodOnUiThread("OneSignal#onUserStateChange", OneSignalSerializer.convertOnUserStateChange(userChangedState));
166+
} catch (JSONException e) {
167+
e.getStackTrace();
168+
Logging.error("Encountered an error attempting to convert UserChangedState object to hash map:" + e.toString(), null);
169+
}
170+
}
153171
}

ios/Classes/OSFlutterUser.m

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
6969
[self addSms:call withResult:result];
7070
else if ([@"OneSignal#removeSms" isEqualToString:call.method])
7171
[self removeSms:call withResult:result];
72-
72+
else if ([@"OneSignal#lifecycleInit" isEqualToString:call.method])
73+
[self lifecycleInit:call withResult:result];
7374
else
7475
result(FlutterMethodNotImplemented);
7576
}
@@ -136,11 +137,41 @@ - (void)removeSms:(FlutterMethodCall *)call withResult:(FlutterResult)result {
136137
result(nil);
137138
}
138139

140+
- (void)lifecycleInit:(FlutterMethodCall *)call withResult:(FlutterResult)result {
141+
[OneSignal.User addObserver:self];
142+
result(nil);
143+
}
144+
145+
- (void)onUserStateDidChangeWithState:(OSUserChangedState *)state {
146+
NSString *onesignalId = [self getStringOrNSNull:state.current.onesignalId];
147+
NSString *externalId = [self getStringOrNSNull:state.current.externalId];
148+
149+
NSMutableDictionary *result = [NSMutableDictionary new];
150+
151+
NSMutableDictionary *currentObject = [NSMutableDictionary new];
152+
153+
currentObject[@"onesignalId"] = onesignalId;
154+
currentObject[@"externalId"] = externalId;
155+
result[@"current"] = currentObject;
156+
157+
[self.channel invokeMethod:@"OneSignal#onUserStateChange" arguments:result];
158+
}
159+
139160
- (void)getOnesignalId:(FlutterMethodCall *)call withResult:(FlutterResult)result {
140161
result(OneSignal.User.onesignalId);
141162
}
142163

143164
- (void)getExternalId:(FlutterMethodCall *)call withResult:(FlutterResult)result {
144165
result(OneSignal.User.externalId);
145166
}
167+
168+
/** Helper method to return NSNull if string is empty or nil **/
169+
- (NSString *)getStringOrNSNull:(NSString *)string {
170+
// length method can be used on nil and strings
171+
if (string.length > 0) {
172+
return string;
173+
} else {
174+
return [NSNull null];
175+
}
176+
}
146177
@end

lib/onesignal_flutter.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class OneSignal {
4848
static void initialize(String appId) {
4949
_channel.invokeMethod('OneSignal#initialize', {'appId': appId});
5050
InAppMessages.lifecycleInit();
51+
User.lifecycleInit();
5152
User.pushSubscription.lifecycleInit();
5253
Notifications.lifecycleInit();
5354
}

lib/src/user.dart

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,45 @@
11
import 'dart:async';
22
import 'package:flutter/services.dart';
33

4+
import 'package:onesignal_flutter/src/utils.dart';
45
import 'package:onesignal_flutter/src/pushsubscription.dart';
56

7+
typedef void OnUserChangeObserver(OSUserChangedState stateChanges);
8+
9+
/// Represents the current user state with OneSignal.
10+
class OSUserState extends JSONStringRepresentable {
11+
String? onesignalId;
12+
String? externalId;
13+
14+
OSUserState(Map<String, dynamic> json) {
15+
if (json.containsKey('onesignalId'))
16+
this.onesignalId = json['onesignalId'] as String?;
17+
if (json.containsKey('externalId'))
18+
this.externalId = json['externalId'] as String?;
19+
}
20+
21+
String jsonRepresentation() {
22+
return convertToJsonString(
23+
{'onesignalId': this.onesignalId, 'externalId': this.externalId});
24+
}
25+
}
26+
27+
/// An instance of this class describes a change in the user state.
28+
class OSUserChangedState extends JSONStringRepresentable {
29+
late OSUserState current;
30+
31+
OSUserChangedState(Map<String, dynamic> json) {
32+
if (json.containsKey('current'))
33+
this.current = OSUserState(json['current'].cast<String, dynamic>());
34+
}
35+
36+
String jsonRepresentation() {
37+
return convertToJsonString(<String, dynamic>{
38+
'current': current.jsonRepresentation(),
39+
});
40+
}
41+
}
42+
643
class OneSignalUser {
744
static OneSignalPushSubscription _pushSubscription =
845
new OneSignalPushSubscription();
@@ -12,6 +49,12 @@ class OneSignalUser {
1249
// private channels used to bridge to ObjC/Java
1350
MethodChannel _channel = const MethodChannel('OneSignal#user');
1451

52+
List<OnUserChangeObserver> _observers = <OnUserChangeObserver>[];
53+
// constructor method
54+
OneSignalUser() {
55+
this._channel.setMethodCallHandler(_handleMethod);
56+
}
57+
1558
/// Sets the user's language.
1659
///
1760
/// Sets the user's language to [language] this also applies to
@@ -125,4 +168,35 @@ class OneSignalUser {
125168
await _channel.invokeMethod("OneSignal#getOnesignalId");
126169
return onesignalId;
127170
}
171+
172+
/// Add an observer that fires when the OneSignal User state changes.
173+
/// *Important* When using the observer to retrieve the onesignalId, check the
174+
/// externalId as well to confirm the values are associated with the expected user.*
175+
void addObserver(OnUserChangeObserver observer) {
176+
_observers.add(observer);
177+
}
178+
179+
// Remove a user state observer that has been previously added.
180+
void removeObserver(OnUserChangeObserver observer) {
181+
_observers.remove(observer);
182+
}
183+
184+
Future<void> lifecycleInit() async {
185+
return await _channel.invokeMethod("OneSignal#lifecycleInit");
186+
}
187+
188+
// Private function that gets called by ObjC/Java
189+
Future<Null> _handleMethod(MethodCall call) async {
190+
if (call.method == 'OneSignal#onUserStateChange') {
191+
this._onUserStateChange(
192+
OSUserChangedState(call.arguments.cast<String, dynamic>()));
193+
}
194+
return null;
195+
}
196+
197+
void _onUserStateChange(OSUserChangedState stateChanges) async {
198+
for (var observer in _observers) {
199+
observer(stateChanges);
200+
}
201+
}
128202
}

0 commit comments

Comments
 (0)