Skip to content

Commit 2f3b118

Browse files
jinliu9508AR Abdul Azeeznan-li
authored
Fix: NPE when setting timezone on app focus (#2505)
Co-authored-by: AR Abdul Azeez <[email protected]> Co-authored-by: Nan <[email protected]>
1 parent da3517c commit 2f3b118

File tree

4 files changed

+77
-64
lines changed

4 files changed

+77
-64
lines changed

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/session/impl/SessionListener.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.onesignal.session.internal.session.impl
22

3+
import com.onesignal.common.TimeUtils
34
import com.onesignal.common.threading.suspendifyOnIO
45
import com.onesignal.core.internal.config.ConfigModelStore
56
import com.onesignal.core.internal.operations.IOperationRepo
@@ -11,6 +12,7 @@ import com.onesignal.session.internal.session.ISessionService
1112
import com.onesignal.user.internal.identity.IdentityModelStore
1213
import com.onesignal.user.internal.operations.TrackSessionEndOperation
1314
import com.onesignal.user.internal.operations.TrackSessionStartOperation
15+
import com.onesignal.user.internal.properties.PropertiesModelStore
1416

1517
/**
1618
* The [SessionListener] is responsible for subscribing itself as an [ISessionLifecycleHandler]
@@ -33,17 +35,20 @@ internal class SessionListener(
3335
private val _sessionService: ISessionService,
3436
private val _configModelStore: ConfigModelStore,
3537
private val _identityModelStore: IdentityModelStore,
38+
private val _propertiesModelStore: PropertiesModelStore,
3639
private val _outcomeEventsController: IOutcomeEventsController,
3740
) : IStartableService, ISessionLifecycleHandler {
3841
override fun start() {
3942
_sessionService.subscribe(this)
4043
}
4144

4245
override fun onSessionStarted() {
46+
_propertiesModelStore.model.timezone = TimeUtils.getTimeZoneId()
4347
_operationRepo.enqueue(TrackSessionStartOperation(_configModelStore.model.appId, _identityModelStore.model.onesignalId), true)
4448
}
4549

4650
override fun onSessionActive() {
51+
_propertiesModelStore.model.timezone = TimeUtils.getTimeZoneId()
4752
}
4853

4954
override fun onSessionEnded(duration: Long) {

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

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@ package com.onesignal.user.internal
22

33
import com.onesignal.common.IDManager
44
import com.onesignal.common.OneSignalUtils
5-
import com.onesignal.common.TimeUtils
65
import com.onesignal.common.events.EventProducer
76
import com.onesignal.common.modeling.ISingletonModelStoreChangeHandler
87
import com.onesignal.common.modeling.ModelChangedArgs
9-
import com.onesignal.core.internal.application.IApplicationLifecycleHandler
10-
import com.onesignal.core.internal.application.IApplicationService
118
import com.onesignal.core.internal.language.ILanguageContext
129
import com.onesignal.debug.LogLevel
1310
import com.onesignal.debug.internal.logging.Logging
@@ -29,8 +26,7 @@ internal open class UserManager(
2926
private val _identityModelStore: IdentityModelStore,
3027
private val _propertiesModelStore: PropertiesModelStore,
3128
private val _languageContext: ILanguageContext,
32-
private val _applicationService: IApplicationService,
33-
) : IUserManager, IApplicationLifecycleHandler, ISingletonModelStoreChangeHandler<IdentityModel> {
29+
) : IUserManager, ISingletonModelStoreChangeHandler<IdentityModel> {
3430
override val onesignalId: String
3531
get() = if (IDManager.isLocalId(_identityModel.onesignalId)) "" else _identityModel.onesignalId
3632

@@ -59,7 +55,6 @@ internal open class UserManager(
5955
}
6056

6157
init {
62-
_applicationService.addApplicationLifecycleHandler(this)
6358
_identityModelStore.subscribe(this)
6459
}
6560

@@ -265,11 +260,4 @@ internal open class UserManager(
265260
}
266261
}
267262
}
268-
269-
override fun onFocus(firedOnSubscribe: Boolean) {
270-
// Detect any user properties updates that changed
271-
_propertiesModel.timezone = TimeUtils.getTimeZoneId()
272-
}
273-
274-
override fun onUnfocused() { }
275263
}
Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,38 @@
11
package com.onesignal.session.internal.session.impl
22

3+
import com.onesignal.common.TimeUtils
34
import com.onesignal.mocks.MockHelper
45
import com.onesignal.user.internal.operations.TrackSessionStartOperation
56
import io.kotest.core.spec.style.FunSpec
7+
import io.mockk.every
68
import io.mockk.mockk
9+
import io.mockk.mockkObject
10+
import io.mockk.unmockkAll
711
import io.mockk.verify
812

13+
private class Mocks {
14+
val mockOperationRepo = mockk<com.onesignal.core.internal.operations.IOperationRepo>(relaxed = true)
15+
16+
val propertiesModelStore = MockHelper.propertiesModelStore()
17+
18+
val sessionListener =
19+
SessionListener(
20+
mockOperationRepo,
21+
mockk<com.onesignal.session.internal.session.ISessionService>(relaxed = true),
22+
MockHelper.configModelStore(),
23+
MockHelper.identityModelStore(),
24+
propertiesModelStore,
25+
mockk<com.onesignal.session.internal.outcomes.IOutcomeEventsController>(relaxed = true),
26+
)
27+
}
28+
929
class SessionListenerTests : FunSpec({
1030

1131
test("onSessionStarted enqueues TrackSessionStartOperation") {
1232
// Given
13-
val mockOperationRepo = mockk<com.onesignal.core.internal.operations.IOperationRepo>(relaxed = true)
14-
15-
val sessionListener =
16-
SessionListener(
17-
mockOperationRepo,
18-
mockk<com.onesignal.session.internal.session.ISessionService>(relaxed = true),
19-
MockHelper.configModelStore(),
20-
MockHelper.identityModelStore(),
21-
mockk<com.onesignal.session.internal.outcomes.IOutcomeEventsController>(relaxed = true),
22-
)
33+
val mocks = Mocks()
34+
val mockOperationRepo = mocks.mockOperationRepo
35+
val sessionListener = mocks.sessionListener
2336

2437
// When
2538
sessionListener.onSessionStarted()
@@ -29,4 +42,47 @@ class SessionListenerTests : FunSpec({
2942
mockOperationRepo.enqueue(any<TrackSessionStartOperation>(), true)
3043
}
3144
}
45+
46+
test("onSessionStarted updates timezone") {
47+
// Given
48+
val mocks = Mocks()
49+
val sessionListener = mocks.sessionListener
50+
val propertiesModel = mocks.propertiesModelStore.model
51+
var timezoneId = "1"
52+
mockkObject(TimeUtils)
53+
every { TimeUtils.getTimeZoneId() } returns timezoneId
54+
55+
// When
56+
sessionListener.onSessionStarted()
57+
58+
// Then
59+
propertiesModel.timezone = timezoneId
60+
61+
// Once timezone is changed, repetitive onSessionStarted call will update timezone
62+
timezoneId = "2"
63+
sessionListener.onSessionStarted()
64+
propertiesModel.timezone = timezoneId
65+
66+
unmockkAll()
67+
}
68+
69+
test("onSessionActive updates timezone") {
70+
// Given
71+
val mocks = Mocks()
72+
val sessionListener = mocks.sessionListener
73+
val propertiesModel = mocks.propertiesModelStore.model
74+
var timezoneId = "1"
75+
mockkObject(TimeUtils)
76+
every { TimeUtils.getTimeZoneId() } returns timezoneId
77+
78+
// When
79+
sessionListener.onSessionStarted()
80+
81+
// Once timezone is changed, onSessionActive will update timezone
82+
timezoneId = "2"
83+
sessionListener.onSessionActive()
84+
propertiesModel.timezone = timezoneId
85+
86+
unmockkAll()
87+
}
3288
})

OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/UserManagerTests.kt

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

3-
import com.onesignal.common.TimeUtils
43
import com.onesignal.core.internal.language.ILanguageContext
54
import com.onesignal.mocks.MockHelper
65
import com.onesignal.user.internal.subscriptions.ISubscriptionManager
@@ -11,10 +10,8 @@ import io.kotest.matchers.shouldNotBe
1110
import io.mockk.every
1211
import io.mockk.just
1312
import io.mockk.mockk
14-
import io.mockk.mockkObject
1513
import io.mockk.runs
1614
import io.mockk.slot
17-
import io.mockk.unmockkObject
1815
import io.mockk.verify
1916

2017
class UserManagerTests : FunSpec({
@@ -29,7 +26,7 @@ class UserManagerTests : FunSpec({
2926
every { languageContext.language = capture(languageSlot) } answers { }
3027

3128
val userManager =
32-
UserManager(mockSubscriptionManager, MockHelper.identityModelStore(), MockHelper.propertiesModelStore(), languageContext, MockHelper.applicationService())
29+
UserManager(mockSubscriptionManager, MockHelper.identityModelStore(), MockHelper.propertiesModelStore(), languageContext)
3330

3431
// When
3532
userManager.setLanguage("new-language")
@@ -47,7 +44,7 @@ class UserManagerTests : FunSpec({
4744
}
4845

4946
val userManager =
50-
UserManager(mockSubscriptionManager, identityModelStore, MockHelper.propertiesModelStore(), MockHelper.languageContext(), MockHelper.applicationService())
47+
UserManager(mockSubscriptionManager, identityModelStore, MockHelper.propertiesModelStore(), MockHelper.languageContext())
5148

5249
// When
5350
val externalId = userManager.externalId
@@ -66,7 +63,7 @@ class UserManagerTests : FunSpec({
6663
}
6764

6865
val userManager =
69-
UserManager(mockSubscriptionManager, identityModelStore, MockHelper.propertiesModelStore(), MockHelper.languageContext(), MockHelper.applicationService())
66+
UserManager(mockSubscriptionManager, identityModelStore, MockHelper.propertiesModelStore(), MockHelper.languageContext())
7067

7168
// When
7269
val alias1 = userManager.aliases["my-alias-key1"]
@@ -105,7 +102,7 @@ class UserManagerTests : FunSpec({
105102
}
106103

107104
val userManager =
108-
UserManager(mockSubscriptionManager, MockHelper.identityModelStore(), propertiesModelStore, MockHelper.languageContext(), MockHelper.applicationService())
105+
UserManager(mockSubscriptionManager, MockHelper.identityModelStore(), propertiesModelStore, MockHelper.languageContext())
109106

110107
// When
111108
val tag1 = propertiesModelStore.model.tags["my-tag-key1"]
@@ -144,7 +141,7 @@ class UserManagerTests : FunSpec({
144141
it.tags["my-tag-key1"] = "my-tag-value1"
145142
}
146143

147-
val userManager = UserManager(mockSubscriptionManager, MockHelper.identityModelStore(), propertiesModelStore, MockHelper.languageContext(), MockHelper.applicationService())
144+
val userManager = UserManager(mockSubscriptionManager, MockHelper.identityModelStore(), propertiesModelStore, MockHelper.languageContext())
148145

149146
// When
150147
val tagSnapshot1 = userManager.getTags()
@@ -177,7 +174,6 @@ class UserManagerTests : FunSpec({
177174
MockHelper.identityModelStore(),
178175
MockHelper.propertiesModelStore(),
179176
MockHelper.languageContext(),
180-
MockHelper.applicationService(),
181177
)
182178

183179
// When
@@ -195,36 +191,4 @@ class UserManagerTests : FunSpec({
195191
verify(exactly = 1) { mockSubscriptionManager.addSmsSubscription("+15558675309") }
196192
verify(exactly = 1) { mockSubscriptionManager.removeSmsSubscription("+15558675309") }
197193
}
198-
199-
test("onFocus updates timezone") {
200-
// Given
201-
val mockTimeZone = "Europe/Foo"
202-
mockkObject(TimeUtils)
203-
every { TimeUtils.getTimeZoneId() } returns mockTimeZone
204-
205-
val mockPropertiesModelStore = MockHelper.propertiesModelStore()
206-
207-
val userManager =
208-
UserManager(
209-
mockk<ISubscriptionManager>(),
210-
MockHelper.identityModelStore(),
211-
mockPropertiesModelStore,
212-
MockHelper.languageContext(),
213-
MockHelper.applicationService(),
214-
)
215-
216-
val propertiesModel = mockPropertiesModelStore.model
217-
propertiesModel.timezone shouldNotBe mockTimeZone
218-
219-
try {
220-
// When
221-
userManager.onFocus(firedOnSubscribe = false)
222-
223-
// Then
224-
propertiesModel.timezone shouldBe mockTimeZone
225-
} finally {
226-
// Clean up the mock
227-
unmockkObject(TimeUtils)
228-
}
229-
}
230194
})

0 commit comments

Comments
 (0)