diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt index 7f051303b3..de9debf0ea 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt @@ -1,7 +1,10 @@ package com.onesignal.internal import android.content.Context +import android.os.Build import com.onesignal.IOneSignal +import com.onesignal.common.AndroidUtils +import com.onesignal.common.DeviceUtils import com.onesignal.common.IDManager import com.onesignal.common.OneSignalUtils import com.onesignal.common.modeling.ModelChangeTags @@ -284,6 +287,12 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { legacyUserSyncJSON.safeString("identifier") ?: "" pushSubscriptionModel.status = SubscriptionStatus.fromInt(notificationTypes) ?: SubscriptionStatus.NO_PERMISSION + + pushSubscriptionModel.sdk = OneSignalUtils.SDK_VERSION + pushSubscriptionModel.deviceOS = Build.VERSION.RELEASE + pushSubscriptionModel.carrier = DeviceUtils.getCarrierName(services.getService().appContext) ?: "" + pushSubscriptionModel.appVersion = AndroidUtils.getAppVersion(services.getService().appContext) ?: "" + configModel!!.pushSubscriptionId = legacyPlayerId subscriptionModelStore!!.add( pushSubscriptionModel, @@ -465,6 +474,10 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { newPushSubscription.optedIn = currentPushSubscription?.optedIn ?: true newPushSubscription.address = currentPushSubscription?.address ?: "" newPushSubscription.status = currentPushSubscription?.status ?: SubscriptionStatus.NO_PERMISSION + newPushSubscription.sdk = OneSignalUtils.SDK_VERSION + newPushSubscription.deviceOS = Build.VERSION.RELEASE + newPushSubscription.carrier = DeviceUtils.getCarrierName(services.getService().appContext) ?: "" + newPushSubscription.appVersion = AndroidUtils.getAppVersion(services.getService().appContext) ?: "" // ensure we always know this devices push subscription ID configModel!!.pushSubscriptionId = newPushSubscription.id diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/RefreshUserOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/RefreshUserOperationExecutor.kt index 7ee10334dd..b92e3cbca6 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/RefreshUserOperationExecutor.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/RefreshUserOperationExecutor.kt @@ -106,6 +106,10 @@ internal class RefreshUserOperationExecutor( } } subscriptionModel.optedIn = subscriptionModel.status != SubscriptionStatus.UNSUBSCRIBE + subscriptionModel.sdk = subscription.sdk ?: "" + subscriptionModel.deviceOS = subscription.deviceOS ?: "" + subscriptionModel.carrier = subscription.carrier ?: "" + subscriptionModel.appVersion = subscription.appVersion ?: "" // We only add a push subscription if it is this device's push subscription. if (subscriptionModel.type != SubscriptionType.PUSH || subscriptionModel.id == _configModelStore.model.pushSubscriptionId) { diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/ISubscriptionManager.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/ISubscriptionManager.kt index 4df6412733..51e439d1a3 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/ISubscriptionManager.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/ISubscriptionManager.kt @@ -11,7 +11,7 @@ interface ISubscriptionManager : IEventNotifier { fun addEmailSubscription(email: String) - fun addOrUpdatePushSubscription( + fun addOrUpdatePushSubscriptionToken( pushToken: String?, pushTokenStatus: SubscriptionStatus, ) diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/SubscriptionModel.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/SubscriptionModel.kt index f8ff8b206a..a76434fa76 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/SubscriptionModel.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/SubscriptionModel.kt @@ -108,4 +108,29 @@ class SubscriptionModel : Model() { set(value) { setEnumProperty(::status.name, value) } + + // Prior to v5.0.5, we did not save the following properties, so we must default get() to "" + var sdk: String + get() = getStringProperty(::sdk.name) { "" } + set(value) { + setStringProperty(::sdk.name, value) + } + + var deviceOS: String + get() = getStringProperty(::deviceOS.name) { "" } + set(value) { + setStringProperty(::deviceOS.name, value) + } + + var carrier: String + get() = getStringProperty(::carrier.name) { "" } + set(value) { + setStringProperty(::carrier.name, value) + } + + var appVersion: String + get() = getStringProperty(::appVersion.name) { "" } + set(value) { + setStringProperty(::appVersion.name, value) + } } diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/SubscriptionModelStore.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/SubscriptionModelStore.kt index 1e76b8a892..fd02219da4 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/SubscriptionModelStore.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/SubscriptionModelStore.kt @@ -1,8 +1,34 @@ package com.onesignal.user.internal.subscriptions +import com.onesignal.common.modeling.ModelChangeTags import com.onesignal.common.modeling.SimpleModelStore import com.onesignal.core.internal.preferences.IPreferencesService open class SubscriptionModelStore(prefs: IPreferencesService) : SimpleModelStore({ SubscriptionModel() -}, "subscriptions", prefs) +}, "subscriptions", prefs) { + override fun replaceAll( + models: List, + tag: String, + ) { + if (tag != ModelChangeTags.HYDRATE) { + return super.replaceAll(models, tag) + } + // When hydrating an existing push subscription model, use existing device properties + synchronized(models) { + for (model in models) { + if (model.type == SubscriptionType.PUSH) { + val existingPushModel = get(model.id) + if (existingPushModel != null) { + model.sdk = existingPushModel.sdk + model.deviceOS = existingPushModel.deviceOS + model.carrier = existingPushModel.carrier + model.appVersion = existingPushModel.appVersion + } + break + } + } + super.replaceAll(models, tag) + } + } +} diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/impl/SubscriptionManager.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/impl/SubscriptionManager.kt index 6f8c465695..c119ab62d0 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/impl/SubscriptionManager.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/impl/SubscriptionManager.kt @@ -1,11 +1,18 @@ package com.onesignal.user.internal.subscriptions.impl +import android.os.Build +import com.onesignal.common.AndroidUtils +import com.onesignal.common.DeviceUtils import com.onesignal.common.IDManager +import com.onesignal.common.OneSignalUtils import com.onesignal.common.events.EventProducer import com.onesignal.common.modeling.IModelStoreChangeHandler import com.onesignal.common.modeling.ModelChangedArgs +import com.onesignal.core.internal.application.IApplicationService import com.onesignal.debug.LogLevel import com.onesignal.debug.internal.logging.Logging +import com.onesignal.session.internal.session.ISessionLifecycleHandler +import com.onesignal.session.internal.session.ISessionService import com.onesignal.user.internal.EmailSubscription import com.onesignal.user.internal.PushSubscription import com.onesignal.user.internal.SmsSubscription @@ -32,8 +39,10 @@ import com.onesignal.user.subscriptions.PushSubscriptionChangedState * subscription model. */ internal class SubscriptionManager( + private val _applicationService: IApplicationService, + private val _sessionService: ISessionService, private val _subscriptionModelStore: SubscriptionModelStore, -) : ISubscriptionManager, IModelStoreChangeHandler { +) : ISubscriptionManager, IModelStoreChangeHandler, ISessionLifecycleHandler { private val events = EventProducer() override var subscriptions: SubscriptionList = SubscriptionList(listOf(), UninitializedPushSubscription()) override val pushSubscriptionModel: SubscriptionModel @@ -45,8 +54,17 @@ internal class SubscriptionManager( } _subscriptionModelStore.subscribe(this) + _sessionService.subscribe(this) } + override fun onSessionStarted() { + refreshPushSubscriptionState() + } + + override fun onSessionActive() { } + + override fun onSessionEnded(duration: Long) { } + override fun addEmailSubscription(email: String) { addSubscriptionToModels(SubscriptionType.EMAIL, email) } @@ -55,7 +73,7 @@ internal class SubscriptionManager( addSubscriptionToModels(SubscriptionType.SMS, sms) } - override fun addOrUpdatePushSubscription( + override fun addOrUpdatePushSubscriptionToken( pushToken: String?, pushTokenStatus: SubscriptionStatus, ) { @@ -215,4 +233,29 @@ internal class SubscriptionManager( } } } + + /** + * Called when app has gained focus and the subscription state should be refreshed. + */ + private fun refreshPushSubscriptionState() { + val pushSub = subscriptions.push + + if (pushSub is UninitializedPushSubscription) { + return + } + val pushSubModel = (pushSub as Subscription).model + + pushSubModel.sdk = OneSignalUtils.SDK_VERSION + pushSubModel.deviceOS = Build.VERSION.RELEASE + + val carrier = DeviceUtils.getCarrierName(_applicationService.appContext) + carrier?.let { + pushSubModel.carrier = carrier + } + + val appVersion = AndroidUtils.getAppVersion(_applicationService.appContext) + appVersion?.let { + pushSubModel.appVersion = appVersion + } + } } diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/subscriptions/SubscriptionManagerTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/subscriptions/SubscriptionManagerTests.kt index 897c3ee4ea..168c9c4cee 100644 --- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/subscriptions/SubscriptionManagerTests.kt +++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/subscriptions/SubscriptionManagerTests.kt @@ -2,6 +2,8 @@ package com.onesignal.user.internal.subscriptions import com.onesignal.common.modeling.ModelChangeTags import com.onesignal.common.modeling.ModelChangedArgs +import com.onesignal.core.internal.application.IApplicationService +import com.onesignal.session.internal.session.ISessionService import com.onesignal.user.internal.subscriptions.impl.SubscriptionManager import com.onesignal.user.subscriptions.ISmsSubscription import io.kotest.core.spec.style.FunSpec @@ -24,6 +26,8 @@ class SubscriptionManagerTests : FunSpec({ test("initializes subscriptions from model store") { // Given val mockSubscriptionModelStore = mockk() + val mockApplicationService = mockk() + val mockSessionService = mockk(relaxed = true) val pushSubscription = SubscriptionModel() pushSubscription.id = "subscription1" pushSubscription.type = SubscriptionType.PUSH @@ -49,7 +53,7 @@ class SubscriptionManagerTests : FunSpec({ every { mockSubscriptionModelStore.subscribe(any()) } just runs every { mockSubscriptionModelStore.list() } returns listOfSubscriptions - val subscriptionManager = SubscriptionManager(mockSubscriptionModelStore) + val subscriptionManager = SubscriptionManager(mockApplicationService, mockSessionService, mockSubscriptionModelStore) // When val subscriptions = subscriptionManager.subscriptions @@ -71,13 +75,15 @@ class SubscriptionManagerTests : FunSpec({ test("add email subscription adds to model store") { // Given val mockSubscriptionModelStore = mockk() + val mockApplicationService = mockk() + val mockSessionService = mockk(relaxed = true) val listOfSubscriptions = listOf() every { mockSubscriptionModelStore.subscribe(any()) } just runs every { mockSubscriptionModelStore.add(any()) } just runs every { mockSubscriptionModelStore.list() } returns listOfSubscriptions - val subscriptionManager = SubscriptionManager(mockSubscriptionModelStore) + val subscriptionManager = SubscriptionManager(mockApplicationService, mockSessionService, mockSubscriptionModelStore) // When subscriptionManager.addEmailSubscription("name@company.com") @@ -98,13 +104,15 @@ class SubscriptionManagerTests : FunSpec({ test("add sms subscription adds to model store") { // Given val mockSubscriptionModelStore = mockk() + val mockApplicationService = mockk() + val mockSessionService = mockk(relaxed = true) val listOfSubscriptions = listOf() every { mockSubscriptionModelStore.subscribe(any()) } just runs every { mockSubscriptionModelStore.add(any()) } just runs every { mockSubscriptionModelStore.list() } returns listOfSubscriptions - val subscriptionManager = SubscriptionManager(mockSubscriptionModelStore) + val subscriptionManager = SubscriptionManager(mockApplicationService, mockSessionService, mockSubscriptionModelStore) // When subscriptionManager.addSmsSubscription("+15558675309") @@ -125,16 +133,18 @@ class SubscriptionManagerTests : FunSpec({ test("add push subscription adds to model store") { // Given val mockSubscriptionModelStore = mockk() + val mockApplicationService = mockk() + val mockSessionService = mockk(relaxed = true) val listOfSubscriptions = listOf() every { mockSubscriptionModelStore.subscribe(any()) } just runs every { mockSubscriptionModelStore.add(any()) } just runs every { mockSubscriptionModelStore.list() } returns listOfSubscriptions - val subscriptionManager = SubscriptionManager(mockSubscriptionModelStore) + val subscriptionManager = SubscriptionManager(mockApplicationService, mockSessionService, mockSubscriptionModelStore) // When - subscriptionManager.addOrUpdatePushSubscription("pushToken", SubscriptionStatus.SUBSCRIBED) + subscriptionManager.addOrUpdatePushSubscriptionToken("pushToken", SubscriptionStatus.SUBSCRIBED) // Then verify { @@ -152,6 +162,8 @@ class SubscriptionManagerTests : FunSpec({ test("update push subscription updates model store") { // Given val mockSubscriptionModelStore = mockk() + val mockApplicationService = mockk() + val mockSessionService = mockk(relaxed = true) val pushSubscription = SubscriptionModel() pushSubscription.id = "subscription1" @@ -167,10 +179,10 @@ class SubscriptionManagerTests : FunSpec({ every { mockSubscriptionModelStore.list() } returns listOfSubscriptions every { mockSubscriptionModelStore.get("subscription1") } returns pushSubscription - val subscriptionManager = SubscriptionManager(mockSubscriptionModelStore) + val subscriptionManager = SubscriptionManager(mockApplicationService, mockSessionService, mockSubscriptionModelStore) // When - subscriptionManager.addOrUpdatePushSubscription("pushToken2", SubscriptionStatus.FIREBASE_FCM_ERROR_IOEXCEPTION_OTHER) + subscriptionManager.addOrUpdatePushSubscriptionToken("pushToken2", SubscriptionStatus.FIREBASE_FCM_ERROR_IOEXCEPTION_OTHER) // Then pushSubscription.address shouldBe "pushToken2" @@ -180,6 +192,8 @@ class SubscriptionManagerTests : FunSpec({ test("remove email subscription removes from model store") { // Given val mockSubscriptionModelStore = mockk() + val mockApplicationService = mockk() + val mockSessionService = mockk(relaxed = true) val emailSubscription = SubscriptionModel() emailSubscription.id = "subscription1" @@ -195,7 +209,7 @@ class SubscriptionManagerTests : FunSpec({ every { mockSubscriptionModelStore.list() } returns listOfSubscriptions every { mockSubscriptionModelStore.remove("subscription1") } just runs - val subscriptionManager = SubscriptionManager(mockSubscriptionModelStore) + val subscriptionManager = SubscriptionManager(mockApplicationService, mockSessionService, mockSubscriptionModelStore) // When subscriptionManager.removeEmailSubscription("name@company.com") @@ -207,6 +221,8 @@ class SubscriptionManagerTests : FunSpec({ test("remove sms subscription removes from model store") { // Given val mockSubscriptionModelStore = mockk() + val mockApplicationService = mockk() + val mockSessionService = mockk(relaxed = true) val emailSubscription = SubscriptionModel() emailSubscription.id = "subscription1" @@ -222,7 +238,7 @@ class SubscriptionManagerTests : FunSpec({ every { mockSubscriptionModelStore.list() } returns listOfSubscriptions every { mockSubscriptionModelStore.remove("subscription1") } just runs - val subscriptionManager = SubscriptionManager(mockSubscriptionModelStore) + val subscriptionManager = SubscriptionManager(mockApplicationService, mockSessionService, mockSubscriptionModelStore) // When subscriptionManager.removeSmsSubscription("+18458675309") @@ -241,6 +257,8 @@ class SubscriptionManagerTests : FunSpec({ smsSubscription.address = "+18458675309" val mockSubscriptionModelStore = mockk() + val mockApplicationService = mockk() + val mockSessionService = mockk(relaxed = true) val listOfSubscriptions = listOf() every { mockSubscriptionModelStore.subscribe(any()) } just runs @@ -248,7 +266,7 @@ class SubscriptionManagerTests : FunSpec({ val spySubscriptionChangedHandler = spyk() - val subscriptionManager = SubscriptionManager(mockSubscriptionModelStore) + val subscriptionManager = SubscriptionManager(mockApplicationService, mockSessionService, mockSubscriptionModelStore) subscriptionManager.subscribe(spySubscriptionChangedHandler) // When @@ -280,6 +298,8 @@ class SubscriptionManagerTests : FunSpec({ emailSubscription.address = "+18458675309" val mockSubscriptionModelStore = mockk() + val mockApplicationService = mockk() + val mockSessionService = mockk(relaxed = true) val listOfSubscriptions = listOf(emailSubscription) every { mockSubscriptionModelStore.subscribe(any()) } just runs @@ -287,7 +307,7 @@ class SubscriptionManagerTests : FunSpec({ val spySubscriptionChangedHandler = spyk() - val subscriptionManager = SubscriptionManager(mockSubscriptionModelStore) + val subscriptionManager = SubscriptionManager(mockApplicationService, mockSessionService, mockSubscriptionModelStore) subscriptionManager.subscribe(spySubscriptionChangedHandler) // When @@ -321,6 +341,8 @@ class SubscriptionManagerTests : FunSpec({ smsSubscription.address = "+18458675309" val mockSubscriptionModelStore = mockk() + val mockApplicationService = mockk() + val mockSessionService = mockk(relaxed = true) val listOfSubscriptions = listOf(smsSubscription) every { mockSubscriptionModelStore.subscribe(any()) } just runs @@ -328,7 +350,7 @@ class SubscriptionManagerTests : FunSpec({ val spySubscriptionChangedHandler = spyk() - val subscriptionManager = SubscriptionManager(mockSubscriptionModelStore) + val subscriptionManager = SubscriptionManager(mockApplicationService, mockSessionService, mockSubscriptionModelStore) subscriptionManager.subscribe(spySubscriptionChangedHandler) // When diff --git a/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/listeners/DeviceRegistrationListener.kt b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/listeners/DeviceRegistrationListener.kt index 49b9f95e2e..1904d31948 100644 --- a/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/listeners/DeviceRegistrationListener.kt +++ b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/listeners/DeviceRegistrationListener.kt @@ -71,7 +71,7 @@ internal class DeviceRegistrationListener( if (pushSubscription.token.isNotEmpty()) { val permission = _notificationsManager.permission - _subscriptionManager.addOrUpdatePushSubscription( + _subscriptionManager.addOrUpdatePushSubscriptionToken( null, if (permission) SubscriptionStatus.SUBSCRIBED else SubscriptionStatus.NO_PERMISSION, ) @@ -79,7 +79,7 @@ internal class DeviceRegistrationListener( suspendifyOnThread { val pushTokenAndStatus = _pushTokenManager.retrievePushToken() val permission = _notificationsManager.permission - _subscriptionManager.addOrUpdatePushSubscription( + _subscriptionManager.addOrUpdatePushSubscriptionToken( pushTokenAndStatus.token, if (permission) pushTokenAndStatus.status else SubscriptionStatus.NO_PERMISSION, )