From 1182083fe539f2ca93848ca716a04473782f422b Mon Sep 17 00:00:00 2001 From: jinliu9508 Date: Thu, 8 Feb 2024 02:36:33 -0500 Subject: [PATCH] Add getters and observer for oneSignalId and externalId --- MIGRATION_GUIDE_v3_to_v5.md | 13 + .../Assets/OneSignal/CHANGELOG.md | 4 + .../Example/OneSignalExampleBehaviour.cs | 17 + .../Example/OneSignalExampleScene.unity | 594 +++++++++++++++++- .../Plugins/Android/mainTemplate.gradle | 2 +- .../AndroidResolverDependencies.xml | 2 +- .../Editor/OneSignalAndroidDependencies.xml | 2 +- .../Runtime/AndroidUserManager.cs | 35 ++ .../Editor/Platform/UserManager.cs | 11 + .../Runtime/User/IUserManager.cs | 33 + .../Runtime/User/Internal/UserState.cs | 46 ++ .../Runtime/User/Internal/UserState.cs.meta | 11 + .../Runtime/User/Models/IPushSubscription.cs | 2 +- .../Runtime/User/Models/IUserState.cs | 46 ++ .../Runtime/User/Models/IUserState.cs.meta | 11 + .../Runtime/User/Models/UserChangedState.cs | 39 ++ .../User/Models/UserChangedState.cs.meta | 11 + .../Samples~/OneSignalExampleBehaviour.cs | 6 + .../Plugins/iOS/OneSignalUnityBridgeUser.mm | 58 ++ .../Runtime/iOSUserManager.cs | 35 ++ 20 files changed, 973 insertions(+), 5 deletions(-) create mode 100644 com.onesignal.unity.core/Runtime/User/Internal/UserState.cs create mode 100644 com.onesignal.unity.core/Runtime/User/Internal/UserState.cs.meta create mode 100644 com.onesignal.unity.core/Runtime/User/Models/IUserState.cs create mode 100644 com.onesignal.unity.core/Runtime/User/Models/IUserState.cs.meta create mode 100644 com.onesignal.unity.core/Runtime/User/Models/UserChangedState.cs create mode 100644 com.onesignal.unity.core/Runtime/User/Models/UserChangedState.cs.meta diff --git a/MIGRATION_GUIDE_v3_to_v5.md b/MIGRATION_GUIDE_v3_to_v5.md index 7ae865c8c..14f190ba4 100644 --- a/MIGRATION_GUIDE_v3_to_v5.md +++ b/MIGRATION_GUIDE_v3_to_v5.md @@ -99,6 +99,14 @@ Once (or if) the user is no longer identifiable in your app (i.e. they logged ou Logging out has the affect of reverting to a “device-scoped” user, which is the new owner of the device’s push subscription. +To observe changes to the onesignalId or externalId you can add a custom method to the event: + + OneSignal.User.Changed += _userStateChanged; + + private void _userStateChanged(object sender, UserStateChangedEventArgs e) { + ... + } + ## Subscriptions @@ -165,7 +173,12 @@ The user name space is accessible via `OneSignal.User` and provides access to us | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `IPushSubscription PushSubscription` | *The push subscription associated to the current user.* | | `string Language` | *Set the 2-character language either as a detected language or explicitly set for this user.* | +| `string OneSignalId` | *The UUID generated by OneSignal to represent a user, empty if this is currently unavailable.* | +| `string ExternalId` | *The External ID is OneSignal's default and recommended alias label. This should be the mainidentifier you use to identify users. It is set when calling the [OneSignal.login] method.* | +| `Changed`

`event EventHandler Changed`

`UserStateChangedEventArgs { UserChangedState State }` | *Adds a change event that will run whenever the onesignalId or externalId has been changed.* | +* | | `PushSubsription.Changed`

`event EventHandler Changed`

`PushSubscriptionChangedEventArgs { PushSubscriptionChangedState State }` | *Adds a change event that will run whenever the push subscription has been changed.* | +* | | `void AddAlias(string label, string id)` | *Set an alias for the current user. If this alias already exists it will be overwritten.* | | `void AddAliases(Dictionary aliases)` | S*et aliases for the current user. If any alias already exists it will be overwritten.* | | `void RemoveAlias(string label)` | *Remove an alias from the current user.* | diff --git a/OneSignalExample/Assets/OneSignal/CHANGELOG.md b/OneSignalExample/Assets/OneSignal/CHANGELOG.md index e85fb57cc..a702f1dc2 100644 --- a/OneSignalExample/Assets/OneSignal/CHANGELOG.md +++ b/OneSignalExample/Assets/OneSignal/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Fixed - iOS crash when calling OneSignal.User.PushSubscription.Id and OneSignal.User.PushSubscription.Token when they are null. +### Changed +- Add public getters for OneSignalId and ExternalId in the User namespace +- Add public event handler OneSignal.User.Changed that fires when the OneSignalId or ExternalId changes +- Updated included Android SDK to [5.1.4](https://github.com/OneSignal/OneSignal-Android-SDK/releases/tag/5.1.4) ## [5.0.6] ### Fixed diff --git a/OneSignalExample/Assets/OneSignal/Example/OneSignalExampleBehaviour.cs b/OneSignalExample/Assets/OneSignal/Example/OneSignalExampleBehaviour.cs index 8ad01c338..847dfb25d 100644 --- a/OneSignalExample/Assets/OneSignal/Example/OneSignalExampleBehaviour.cs +++ b/OneSignalExample/Assets/OneSignal/Example/OneSignalExampleBehaviour.cs @@ -33,6 +33,7 @@ using OneSignalSDK; using OneSignalSDK.Notifications; using OneSignalSDK.InAppMessages; +using OneSignalSDK.User; using OneSignalSDK.User.Models; using OneSignalSDK.Debug.Models; using OneSignalSDK.Debug.Utilities; @@ -159,6 +160,7 @@ private void Start() { // Setup the below to listen for and respond to state changes OneSignal.User.PushSubscription.Changed += _pushSubscriptionChanged; + OneSignal.User.Changed += _userStateChanged; } /* @@ -211,6 +213,11 @@ private void _pushSubscriptionChanged(object sender, PushSubscriptionChangedEven _log($"Push subscription changed to current: {JsonUtility.ToJson(e.State.Current)}"); } + private void _userStateChanged(object sender, UserStateChangedEventArgs e) { + _log($"OneSignalId changed : {e.State.Current.OneSignalId}"); + _log($"ExternalId changed : {e.State.Current.ExternalId}"); + } + /* * SDK setup */ @@ -298,6 +305,16 @@ public void RemoveAlias() { OneSignal.User.RemoveAlias(aliasKey); } + public void GetOneSignalId() { + string onesignalId = OneSignal.User.OneSignalId; + _log($"Get OneSignalId {onesignalId}"); + } + + public void GetExternalId() { + string externalId = OneSignal.User.ExternalId; + _log($"Get ExternalId {externalId}"); + } + /* * Push */ diff --git a/OneSignalExample/Assets/OneSignal/Example/OneSignalExampleScene.unity b/OneSignalExample/Assets/OneSignal/Example/OneSignalExampleScene.unity index 8eed5e5ff..1edc6e35b 100644 --- a/OneSignalExample/Assets/OneSignal/Example/OneSignalExampleScene.unity +++ b/OneSignalExample/Assets/OneSignal/Example/OneSignalExampleScene.unity @@ -809,6 +809,85 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 111296087} m_CullTransparentMesh: 1 +--- !u!1 &118443139 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 118443140} + - component: {fileID: 118443142} + - component: {fileID: 118443141} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &118443140 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 118443139} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.9, y: 0.9, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1278232628} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &118443141 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 118443139} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Get OneSignalId +--- !u!222 &118443142 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 118443139} + m_CullTransparentMesh: 1 --- !u!1 &129243063 GameObject: m_ObjectHideFlags: 0 @@ -3868,6 +3947,85 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 484811935} m_CullTransparentMesh: 1 +--- !u!1 &495154762 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 495154763} + - component: {fileID: 495154765} + - component: {fileID: 495154764} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &495154763 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 495154762} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.9, y: 0.9, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 910262211} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &495154764 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 495154762} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Get ExternalId +--- !u!222 &495154765 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 495154762} + m_CullTransparentMesh: 1 --- !u!1 &499047144 GameObject: m_ObjectHideFlags: 0 @@ -6747,6 +6905,139 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 909918831} m_CullTransparentMesh: 1 +--- !u!1 &910262210 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 910262211} + - component: {fileID: 910262214} + - component: {fileID: 910262213} + - component: {fileID: 910262212} + m_Layer: 5 + m_Name: button_getExternalId + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &910262211 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 910262210} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 495154763} + m_Father: {fileID: 1250383578} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &910262212 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 910262210} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 910262213} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 307684281} + m_TargetAssemblyTypeName: OneSignalExampleBehaviour, OneSignal.UnityPackage.Example + m_MethodName: GetExternalId + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 1 +--- !u!114 &910262213 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 910262210} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &910262214 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 910262210} + m_CullTransparentMesh: 1 --- !u!1 &916273501 GameObject: m_ObjectHideFlags: 0 @@ -10067,6 +10358,70 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1248003718} m_CullTransparentMesh: 1 +--- !u!1 &1250383577 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1250383578} + - component: {fileID: 1250383579} + m_Layer: 5 + m_Name: inputcontrols_container + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1250383578 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1250383577} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1278232628} + - {fileID: 910262211} + m_Father: {fileID: 1996857125} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1250383579 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1250383577} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 0 + m_Spacing: 10 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 1 + m_ChildControlWidth: 1 + m_ChildControlHeight: 1 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 --- !u!1 &1250632528 GameObject: m_ObjectHideFlags: 0 @@ -10304,6 +10659,139 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1263276221} m_CullTransparentMesh: 1 +--- !u!1 &1278232627 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1278232628} + - component: {fileID: 1278232631} + - component: {fileID: 1278232630} + - component: {fileID: 1278232629} + m_Layer: 5 + m_Name: button_getOneSignalId + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1278232628 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1278232627} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 118443140} + m_Father: {fileID: 1250383578} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1278232629 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1278232627} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1278232630} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 307684281} + m_TargetAssemblyTypeName: OneSignalExampleBehaviour, OneSignal.UnityPackage.Example + m_MethodName: GetOneSignalId + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 1 +--- !u!114 &1278232630 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1278232627} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1278232631 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1278232627} + m_CullTransparentMesh: 1 --- !u!1 &1296016975 GameObject: m_ObjectHideFlags: 0 @@ -13786,7 +14274,7 @@ MonoBehaviour: m_HandleRect: {fileID: 916273502} m_Direction: 2 m_Value: 1 - m_Size: 0.5315627 + m_Size: 0.50223213 m_NumberOfSteps: 0 m_OnValueChanged: m_PersistentCalls: @@ -14155,6 +14643,7 @@ RectTransform: - {fileID: 1144247376} - {fileID: 1296016976} - {fileID: 629334645} + - {fileID: 1996857125} - {fileID: 262919209} m_Father: {fileID: 2924184} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -16250,6 +16739,109 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1996857124 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1996857125} + - component: {fileID: 1996857128} + - component: {fileID: 1996857127} + - component: {fileID: 1996857126} + m_Layer: 5 + m_Name: getIds_container + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1996857125 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1996857124} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1250383578} + m_Father: {fileID: 1654894820} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1996857126 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1996857124} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.9607844, g: 0.47450984, b: 0.47450984, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1996857127 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1996857124} + m_CullTransparentMesh: 1 +--- !u!114 &1996857128 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1996857124} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 10 + m_Right: 10 + m_Top: 10 + m_Bottom: 10 + m_ChildAlignment: 0 + m_Spacing: 10 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 1 + m_ChildControlWidth: 1 + m_ChildControlHeight: 1 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 --- !u!1 &2009596016 GameObject: m_ObjectHideFlags: 0 diff --git a/OneSignalExample/Assets/Plugins/Android/mainTemplate.gradle b/OneSignalExample/Assets/Plugins/Android/mainTemplate.gradle index 09525b310..7255df042 100644 --- a/OneSignalExample/Assets/Plugins/Android/mainTemplate.gradle +++ b/OneSignalExample/Assets/Plugins/Android/mainTemplate.gradle @@ -6,7 +6,7 @@ apply plugin: 'com.android.library' dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // Android Resolver Dependencies Start - implementation 'com.onesignal:OneSignal:5.1.2' // Packages/com.onesignal.unity.android/Editor/OneSignalAndroidDependencies.xml:6 + implementation 'com.onesignal:OneSignal:5.1.4' // Packages/com.onesignal.unity.android/Editor/OneSignalAndroidDependencies.xml:6 // Android Resolver Dependencies End **DEPS**} diff --git a/OneSignalExample/ProjectSettings/AndroidResolverDependencies.xml b/OneSignalExample/ProjectSettings/AndroidResolverDependencies.xml index 9eb42f5a1..f52f7b1b4 100644 --- a/OneSignalExample/ProjectSettings/AndroidResolverDependencies.xml +++ b/OneSignalExample/ProjectSettings/AndroidResolverDependencies.xml @@ -1,6 +1,6 @@ - com.onesignal:OneSignal:5.1.2 + com.onesignal:OneSignal:5.1.4 diff --git a/com.onesignal.unity.android/Editor/OneSignalAndroidDependencies.xml b/com.onesignal.unity.android/Editor/OneSignalAndroidDependencies.xml index 0bc1db54d..3ced2f24e 100644 --- a/com.onesignal.unity.android/Editor/OneSignalAndroidDependencies.xml +++ b/com.onesignal.unity.android/Editor/OneSignalAndroidDependencies.xml @@ -3,6 +3,6 @@ https://repo.maven.apache.org/maven2 - + \ No newline at end of file diff --git a/com.onesignal.unity.android/Runtime/AndroidUserManager.cs b/com.onesignal.unity.android/Runtime/AndroidUserManager.cs index 430a547ab..e72b6f7dd 100644 --- a/com.onesignal.unity.android/Runtime/AndroidUserManager.cs +++ b/com.onesignal.unity.android/Runtime/AndroidUserManager.cs @@ -27,8 +27,10 @@ using UnityEngine; +using System; using System.Collections.Generic; using OneSignalSDK.User; +using OneSignalSDK.User.Internal; using OneSignalSDK.User.Models; using OneSignalSDK.Android.User.Models; using OneSignalSDK.Android.Utilities; @@ -37,12 +39,21 @@ namespace OneSignalSDK.Android.User { internal sealed class AndroidUserManager : IUserManager { private readonly AndroidJavaObject _user; private AndroidPushSubscription _pushSubscription; + public event EventHandler Changed; public AndroidUserManager(AndroidJavaClass sdkClass) { _user = sdkClass.CallStatic("getUser"); _pushSubscription = new AndroidPushSubscription(_user); } + public string OneSignalId { + get => _user.Call("getOnesignalId"); + } + + public string ExternalId { + get => _user.Call("getExternalId"); + } + public IPushSubscription PushSubscription { get => _pushSubscription; } @@ -93,7 +104,31 @@ public void RemoveSms(string sms) => _user.Call("removeSms", sms); public void Initialize() { + _user.Call("addObserver", new InternalUserChangedHandler(this)); _pushSubscription.Initialize(); } + + private sealed class InternalUserChangedHandler : OneSignalAndroidJavaProxy { + private AndroidUserManager _parent; + + public InternalUserChangedHandler(AndroidUserManager userManager) : base("user.state.IUserStateObserver") { + _parent = userManager; + } + + /// UserChangedState + public void onUserStateChange(AndroidJavaObject state) { + var currentJO = state.Call("getCurrent"); + var current = currentJO.ToSerializable(); + + UserChangedState userChangedState = new UserChangedState(current); + UserStateChangedEventArgs args = new UserStateChangedEventArgs(userChangedState); + + EventHandler handler = _parent.Changed; + if (handler != null) + { + UnityMainThreadDispatch.Post(state => handler(_parent, args)); + } + } + } } } \ No newline at end of file diff --git a/com.onesignal.unity.core/Editor/Platform/UserManager.cs b/com.onesignal.unity.core/Editor/Platform/UserManager.cs index dfbe22775..e9393006d 100644 --- a/com.onesignal.unity.core/Editor/Platform/UserManager.cs +++ b/com.onesignal.unity.core/Editor/Platform/UserManager.cs @@ -25,11 +25,22 @@ * THE SOFTWARE. */ +using System; using System.Collections.Generic; using OneSignalSDK.User.Models; namespace OneSignalSDK.User { internal sealed class UserManager : IUserManager { + public string OneSignalId { + get => ""; + } + + public string ExternalId { + get => ""; + } + + public event EventHandler Changed; + private PushSubscription _subscription = new PushSubscription(); public IPushSubscription PushSubscription { diff --git a/com.onesignal.unity.core/Runtime/User/IUserManager.cs b/com.onesignal.unity.core/Runtime/User/IUserManager.cs index 84c9418f3..f64dd606e 100644 --- a/com.onesignal.unity.core/Runtime/User/IUserManager.cs +++ b/com.onesignal.unity.core/Runtime/User/IUserManager.cs @@ -25,10 +25,25 @@ * THE SOFTWARE. */ +using System; using System.Collections.Generic; +using System.Threading.Tasks; +using UnityEngine; using OneSignalSDK.User.Models; namespace OneSignalSDK.User { + /// + /// Several states associated with the SDK can be changed in and outside of the application. + /// + public class UserStateChangedEventArgs : EventArgs + { + public UserChangedState State { get; } + + public UserStateChangedEventArgs(UserChangedState state) { + State = state; + } + } + /// /// The OneSignal user manager is responsible for managing the current user state. When /// an app starts up for the first time, it is defaulted to having a guest user that is only @@ -46,6 +61,24 @@ namespace OneSignalSDK.User { /// 2. When the identity of the user is lost (i.e. a logout) /// public interface IUserManager { + /// + /// The UUID generated by OneSignal to represent a user, empty if this is currently unavailable. + /// + string OneSignalId { get; } + + /// + /// The External ID is OneSignal's default and recommended alias label. This should be the main + /// identifier you use to identify users. It is set when calling the [OneSignal.login] method. + /// + /// This is empty if the External ID has not been set. + /// + string ExternalId { get; } + + /// + /// When onesignalId or externalId has changed + /// + event EventHandler Changed; + /// /// The 2-character language either as a detected language or explicitly set for this user. See /// https://documentation.onesignal.com/docs/language-localization#what-languages-are-supported diff --git a/com.onesignal.unity.core/Runtime/User/Internal/UserState.cs b/com.onesignal.unity.core/Runtime/User/Internal/UserState.cs new file mode 100644 index 000000000..eab372c57 --- /dev/null +++ b/com.onesignal.unity.core/Runtime/User/Internal/UserState.cs @@ -0,0 +1,46 @@ +/* + * Modified MIT License + * + * Copyright 2023 OneSignal + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. All copies of substantial portions of the Software may only be used in connection + * with services provided by OneSignal. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using OneSignalSDK.User; + +namespace OneSignalSDK.User.Internal { + [Serializable] public sealed class UserState { + public string OneSignalId => onesignalId; + public string ExternalId => externalId; + + #region Native Field Handling + public string onesignalId; + public string externalId; + #endregion + + public UserState(string onesignalId, string externalId) { + this.onesignalId = onesignalId; + this.externalId = externalId; + } + } +} \ No newline at end of file diff --git a/com.onesignal.unity.core/Runtime/User/Internal/UserState.cs.meta b/com.onesignal.unity.core/Runtime/User/Internal/UserState.cs.meta new file mode 100644 index 000000000..c58f687ca --- /dev/null +++ b/com.onesignal.unity.core/Runtime/User/Internal/UserState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0dc78f38e7b0948d99aedd469f4a2a29 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.onesignal.unity.core/Runtime/User/Models/IPushSubscription.cs b/com.onesignal.unity.core/Runtime/User/Models/IPushSubscription.cs index 378a948f6..fabdc919f 100644 --- a/com.onesignal.unity.core/Runtime/User/Models/IPushSubscription.cs +++ b/com.onesignal.unity.core/Runtime/User/Models/IPushSubscription.cs @@ -25,10 +25,10 @@ * THE SOFTWARE. */ +using System; using System.Collections.Generic; using System.Threading.Tasks; using UnityEngine; -using System; namespace OneSignalSDK.User.Models { /// diff --git a/com.onesignal.unity.core/Runtime/User/Models/IUserState.cs b/com.onesignal.unity.core/Runtime/User/Models/IUserState.cs new file mode 100644 index 000000000..6bfcab177 --- /dev/null +++ b/com.onesignal.unity.core/Runtime/User/Models/IUserState.cs @@ -0,0 +1,46 @@ +/* + * Modified MIT License + * + * Copyright 2023 OneSignal + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. All copies of substantial portions of the Software may only be used in connection + * with services provided by OneSignal. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; + +namespace OneSignalSDK.User { + public interface IUserState { + /// + /// The unique identifier for your OneSignal account. This will be an empty string until the + /// user has been successfully logged in on the backend and assigned an ID. + /// Use [Changed] to be notified when the [OneSignalId] has been successfully assigned. + /// + string OneSignalId { get; } + + /// + /// The external identifier that you use to identify users. Use [Changed] to be notified + /// when the [ExternalId] has been successfully assigned. This will be an empty string if no + /// external identifier has been assigned to the associated [OneSignalId]. + /// + string ExternalId { get; } + } +} \ No newline at end of file diff --git a/com.onesignal.unity.core/Runtime/User/Models/IUserState.cs.meta b/com.onesignal.unity.core/Runtime/User/Models/IUserState.cs.meta new file mode 100644 index 000000000..93e556cbf --- /dev/null +++ b/com.onesignal.unity.core/Runtime/User/Models/IUserState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 369a546d971624331b3abbb151bf363a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.onesignal.unity.core/Runtime/User/Models/UserChangedState.cs b/com.onesignal.unity.core/Runtime/User/Models/UserChangedState.cs new file mode 100644 index 000000000..419fe0336 --- /dev/null +++ b/com.onesignal.unity.core/Runtime/User/Models/UserChangedState.cs @@ -0,0 +1,39 @@ +/* + * Modified MIT License + * + * Copyright 2023 OneSignal + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. All copies of substantial portions of the Software may only be used in connection + * with services provided by OneSignal. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using OneSignalSDK.User.Internal; + +namespace OneSignalSDK.User { + public sealed class UserChangedState { + public UserState Current { get; } + + public UserChangedState(UserState state) { + Current = state; + } + } +} \ No newline at end of file diff --git a/com.onesignal.unity.core/Runtime/User/Models/UserChangedState.cs.meta b/com.onesignal.unity.core/Runtime/User/Models/UserChangedState.cs.meta new file mode 100644 index 000000000..5b4c07c05 --- /dev/null +++ b/com.onesignal.unity.core/Runtime/User/Models/UserChangedState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 63e0183774569451eb3aeccb49a7ea1f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.onesignal.unity.core/Samples~/OneSignalExampleBehaviour.cs b/com.onesignal.unity.core/Samples~/OneSignalExampleBehaviour.cs index 1efb742a5..f2a0c64c5 100644 --- a/com.onesignal.unity.core/Samples~/OneSignalExampleBehaviour.cs +++ b/com.onesignal.unity.core/Samples~/OneSignalExampleBehaviour.cs @@ -159,6 +159,7 @@ private void Start() { // Setup the below to listen for and respond to state changes OneSignal.User.PushSubscription.Changed += _pushSubscriptionChanged; + OneSignal.User.Changed += _userStateChanged; } /* @@ -211,6 +212,11 @@ private void _pushSubscriptionChanged(object sender, PushSubscriptionChangedEven _log($"Push subscription changed to current: {JsonUtility.ToJson(e.State.Current)}"); } + private void _userStateChanged(object sender, UserStateChangedEventArgs e) { + _log($"OneSignalId changed : {JsonUtility.ToJson(e.State.Current.OneSignalId)}"); + _log($"ExternalId changed : {JsonUtility.ToJson(e.State.Current.ExternalId)}"); + } + /* * SDK setup */ diff --git a/com.onesignal.unity.ios/Runtime/Plugins/iOS/OneSignalUnityBridgeUser.mm b/com.onesignal.unity.ios/Runtime/Plugins/iOS/OneSignalUnityBridgeUser.mm index 0d4db99e9..5f005fd1f 100644 --- a/com.onesignal.unity.ios/Runtime/Plugins/iOS/OneSignalUnityBridgeUser.mm +++ b/com.onesignal.unity.ios/Runtime/Plugins/iOS/OneSignalUnityBridgeUser.mm @@ -31,6 +31,7 @@ #import "OneSignalBridgeUtil.h" typedef void (*StateListenerDelegate)(const char* current, const char* previous); +typedef void (*UserStateListenerDelegate)(const char* current); /* * Helpers @@ -78,6 +79,45 @@ - (void)onPushSubscriptionDidChangeWithState:(OSPushSubscriptionChangedState*)st @end +/* + * User state observer singleton for global callbacks + */ + +@interface OneSignalUserStateObserver : NSObject + ++ (instancetype) sharedUserObserver; +@property UserStateListenerDelegate userStateDelegate; + +@end + +@implementation OneSignalUserStateObserver + ++ (instancetype) sharedUserObserver { + static dispatch_once_t pred = 0; + static id _sharedObject = nil; + dispatch_once(&pred, ^{ + _sharedObject = [[self alloc] init]; + }); + return _sharedObject; +} + +- (instancetype) init { + if (self = [super init]) { + [OneSignal.User addObserver:self]; + } + + return self; +} + +- (void)onUserStateDidChangeWithState:(OSUserChangedState*)state { + if (_userStateDelegate != nil) { + auto curr = oneSignalJsonStringFromDictionary([[state current] jsonRepresentation]); + _userStateDelegate(curr); + } +} + +@end + /* * Bridge methods * We use strdup because Unity attempts to free the memory after we return the value @@ -160,6 +200,20 @@ void _oneSignalUserRemoveTags(const char* tagsJson) { [OneSignal.User removeTags:tags]; } + const char* _oneSignalUserGetOneSignalId(){ + if (OneSignal.User.OneSignalId == NULL) { + return NULL; + } + return strdup([OneSignal.User.OneSignalId UTF8String]); + } + + const char* _oneSignalUserGetExternalId(){ + if (OneSignal.User.ExternalId == NULL) { + return NULL; + } + return strdup([OneSignal.User.ExternalId UTF8String]); + } + const char* _oneSignalPushSubscriptionGetId() { if (OneSignal.User.pushSubscription.id == NULL) { return NULL; @@ -189,4 +243,8 @@ void _oneSignalPushSubscriptionOptOut() { void _oneSignalPushSubscriptionAddStateChangedCallback(StateListenerDelegate callback) { [[OneSignalUserObserver sharedUserObserver] setPushSubscriptionDelegate:callback]; } + + void _oneSignalUserAddStateChangedCallback(UserStateListenerDelegate callback) { + [[OneSignalUserStateObserver sharedUserObserver] setUserStateDelegate:callback]; + } } diff --git a/com.onesignal.unity.ios/Runtime/iOSUserManager.cs b/com.onesignal.unity.ios/Runtime/iOSUserManager.cs index 7b9822337..57ff77d43 100644 --- a/com.onesignal.unity.ios/Runtime/iOSUserManager.cs +++ b/com.onesignal.unity.ios/Runtime/iOSUserManager.cs @@ -27,14 +27,18 @@ using UnityEngine; +using System; using System.Collections.Generic; using System.Runtime.InteropServices; using OneSignalSDK.User; +using OneSignalSDK.User.Internal; using OneSignalSDK.User.Models; using OneSignalSDK.iOS.User.Models; namespace OneSignalSDK.iOS.User { internal sealed class iOSUserManager : IUserManager { + [DllImport("__Internal")] private static extern string _oneSignalUserGetOneSignalId(); + [DllImport("__Internal")] private static extern string _oneSignalUserGetExternalId(); [DllImport("__Internal")] private static extern void _oneSignalUserSetLanguage(string languageCode); [DllImport("__Internal")] private static extern void _oneSignalUserAddAlias(string aliasLabel, string aliasId); [DllImport("__Internal")] private static extern void _oneSignalUserAddAliases(string aliasesJson); @@ -49,13 +53,29 @@ internal sealed class iOSUserManager : IUserManager { [DllImport("__Internal")] private static extern void _oneSignalUserAddTags(string tagsJson); [DllImport("__Internal")] private static extern void _oneSignalUserRemoveTag(string key); [DllImport("__Internal")] private static extern void _oneSignalUserRemoveTags(string tagsJson); + [DllImport("__Internal")] private static extern void _oneSignalUserAddStateChangedCallback(UserStateListenerDelegate callback); + + public delegate void UserStateListenerDelegate(string current); + + public event EventHandler Changed; private iOSPushSubscription _pushSubscription; + + private static iOSUserManager _instance; public iOSUserManager() { + _instance = this; _pushSubscription = new iOSPushSubscription(); } + public string OneSignalId { + get => _oneSignalUserGetOneSignalId(); + } + + public string ExternalId { + get => _oneSignalUserGetExternalId(); + } + public IPushSubscription PushSubscription { get => _pushSubscription; } @@ -109,6 +129,21 @@ public void RemoveSms(string sms) public void Initialize() { _pushSubscription.Initialize(); + _oneSignalUserAddStateChangedCallback(_onUserStateChanged); + } + + [AOT.MonoPInvokeCallback(typeof(UserStateListenerDelegate))] + private static void _onUserStateChanged(string current) { + var curr = JsonUtility.FromJson(current); + + UserChangedState userChangedState = new UserChangedState(curr); + UserStateChangedEventArgs args = new UserStateChangedEventArgs(userChangedState); + + EventHandler handler = _instance.Changed; + if (handler != null) + { + UnityMainThreadDispatch.Post(state => handler(_instance, args)); + } } } } \ No newline at end of file