From 148c1a30c1e7b45542e79672cbd6b37969cfd9e1 Mon Sep 17 00:00:00 2001
From: jinliu9508 <jinliu9508@gmail.com>
Date: Tue, 14 Nov 2023 02:25:31 -0500
Subject: [PATCH] Add UserStateObserver following the structure of
 pushSubscriptionObserver

Assumption:
1. we want to store the change notifier in UserManager

Still Need:
1 implementation on where user state is updated
2 better comments in some places
3. testing units
---
 .../java/com/onesignal/user/IUserManager.kt   | 12 +++++++++
 .../onesignal/user/internal/UserManager.kt    |  8 ++++++
 .../user/internal/state/IUserStateObserver.kt | 16 +++++++++++
 .../user/internal/state/UserChangedState.kt   | 16 +++++++++++
 .../user/internal/state/UserState.kt          | 27 +++++++++++++++++++
 .../notifications/consumer-rules.pro          |  4 +++
 6 files changed, 83 insertions(+)
 create mode 100644 OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/state/IUserStateObserver.kt
 create mode 100644 OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/state/UserChangedState.kt
 create mode 100644 OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/state/UserState.kt

diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/IUserManager.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/IUserManager.kt
index e9de656d0d..1f783f9771 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/IUserManager.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/IUserManager.kt
@@ -1,6 +1,7 @@
 package com.onesignal.user
 
 import com.onesignal.OneSignal
+import com.onesignal.user.internal.state.IUserStateObserver
 import com.onesignal.user.subscriptions.IPushSubscription
 
 /**
@@ -133,4 +134,15 @@ interface IUserManager {
      * @param keys The collection of keys, all of which will be removed from the current user.
      */
     fun removeTags(keys: Collection<String>)
+
+    /**
+     * Add an observer to the user state, allowing the provider to be
+     * notified whenever the user state has changed.
+     */
+    fun addObserver(observer: IUserStateObserver)
+
+    /**
+     * Remove an observer from the user state.
+     */
+    fun removeObserver(observer: IUserStateObserver)
 }
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/UserManager.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/UserManager.kt
index dbb8add277..71f849ebc0 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/UserManager.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/UserManager.kt
@@ -1,6 +1,7 @@
 package com.onesignal.user.internal
 
 import com.onesignal.common.OneSignalUtils
+import com.onesignal.common.events.EventProducer
 import com.onesignal.core.internal.language.ILanguageContext
 import com.onesignal.debug.LogLevel
 import com.onesignal.debug.internal.logging.Logging
@@ -10,6 +11,7 @@ import com.onesignal.user.internal.identity.IdentityModel
 import com.onesignal.user.internal.identity.IdentityModelStore
 import com.onesignal.user.internal.properties.PropertiesModel
 import com.onesignal.user.internal.properties.PropertiesModelStore
+import com.onesignal.user.internal.state.IUserStateObserver
 import com.onesignal.user.internal.subscriptions.ISubscriptionManager
 import com.onesignal.user.internal.subscriptions.SubscriptionList
 import com.onesignal.user.subscriptions.IPushSubscription
@@ -32,6 +34,8 @@ internal open class UserManager(
     val subscriptions: SubscriptionList
         get() = _subscriptionManager.subscriptions
 
+    val changeHandlersNotifier = EventProducer<IUserStateObserver>()
+
     override val pushSubscription: IPushSubscription
         get() = _subscriptionManager.subscriptions.push
 
@@ -218,4 +222,8 @@ internal open class UserManager(
             _propertiesModel.tags.remove(it)
         }
     }
+
+    override fun addObserver(observer: IUserStateObserver) = changeHandlersNotifier.subscribe(observer)
+
+    override fun removeObserver(observer: IUserStateObserver) = changeHandlersNotifier.unsubscribe(observer)
 }
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/state/IUserStateObserver.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/state/IUserStateObserver.kt
new file mode 100644
index 0000000000..5cc77c33ba
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/state/IUserStateObserver.kt
@@ -0,0 +1,16 @@
+package com.onesignal.user.internal.state
+
+
+/**
+ * A user state changed handler. Implement this interface and provide the implementation
+ * to be notified when the user has changed.
+ */
+interface IUserStateObserver {
+    /**
+     * Called when the user state this change handler was added to, has changed. A
+     * user state can change when user has logged in or out
+     *
+     * @param state The user changed state.
+     */
+    fun onUserStateChange(state: UserChangedState)
+}
\ No newline at end of file
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/state/UserChangedState.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/state/UserChangedState.kt
new file mode 100644
index 0000000000..0a6db62a78
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/state/UserChangedState.kt
@@ -0,0 +1,16 @@
+package com.onesignal.user.internal.state
+
+import org.json.JSONObject
+
+class UserChangedState(
+        val previous: UserState,
+        val current: UserState,
+        val switchedUsers: Boolean,
+) {
+    fun toJSONObject(): JSONObject {
+        return JSONObject()
+                .put("previous", previous.toJSONObject())
+                .put("current", current.toJSONObject())
+                .put("switchedUsers", switchedUsers)
+    }
+}
\ No newline at end of file
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/state/UserState.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/state/UserState.kt
new file mode 100644
index 0000000000..57567ebcc5
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/state/UserState.kt
@@ -0,0 +1,27 @@
+package com.onesignal.user.internal.state
+
+import org.json.JSONObject
+
+/**
+ * A user state.
+ */
+class UserState (
+        /**
+         * The unique identifier for OneSignal account. This will be an empty string
+         * until the user has been successfully logged in on the backend and
+         * assigned an ID.  Use [addObserver] to be notified when the [onesignalId] has
+         * been successfully assigned.
+         */
+        val onesignalId: String?,
+
+        /**
+         *  ????? What should be a good comment here ?????
+         */
+        val externalId: String?,
+) {
+    fun toJSONObject(): JSONObject {
+        return JSONObject()
+                .put("onesignalId", onesignalId)
+                .put("externalId", externalId)
+    }
+}
\ No newline at end of file
diff --git a/OneSignalSDK/onesignal/notifications/consumer-rules.pro b/OneSignalSDK/onesignal/notifications/consumer-rules.pro
index d9fc948356..fa6eae266f 100644
--- a/OneSignalSDK/onesignal/notifications/consumer-rules.pro
+++ b/OneSignalSDK/onesignal/notifications/consumer-rules.pro
@@ -20,6 +20,10 @@
     void onPushSubscriptionChange(com.onesignal.user.subscriptions.PushSubscriptionChangedState);
 }
 
+-keep class ** implements com.onesignal.user.internal.state.IUserStateObserver {
+    void onUserChange(com.onesignal.user.internal.state.UserChangedState);
+}
+
 -keep class ** implements com.onesignal.notifications.INotificationServiceExtension{
    void onNotificationReceived(com.onesignal.notifications.INotificationReceivedEvent);
 }