Skip to content

Commit 0358ae9

Browse files
authored
Use kotlin.time.Duration instead of Long for time intervals (#4733)
* Use kotlin.time.Duration instead of Long for time intervals * Adjust test constants * Simplify elapsedRealtime to always be millis precision
1 parent 6e5072d commit 0358ae9

File tree

6 files changed

+104
-67
lines changed

6 files changed

+104
-67
lines changed

firebase-sessions/firebase-sessions.gradle.kts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
// Copyright 2023 Google LLC
2-
//
3-
// Licensed under the Apache License, Version 2.0 (the "License");
4-
// you may not use this file except in compliance with the License.
5-
// You may obtain a copy of the License at
6-
//
7-
// http://www.apache.org/licenses/LICENSE-2.0
8-
//
9-
// Unless required by applicable law or agreed to in writing, software
10-
// distributed under the License is distributed on an "AS IS" BASIS,
11-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12-
// See the License for the specific language governing permissions and
13-
// limitations under the License.
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
1416

1517
plugins {
1618
id("firebase-library")

firebase-sessions/src/androidTest/kotlin/com/google/firebase/sessions/FirebaseSessionsTests.kt

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
// Copyright 2023 Google LLC
2-
//
3-
// Licensed under the Apache License, Version 2.0 (the "License");
4-
// you may not use this file except in compliance with the License.
5-
// You may obtain a copy of the License at
6-
//
7-
// http://www.apache.org/licenses/LICENSE-2.0
8-
//
9-
// Unless required by applicable law or agreed to in writing, software
10-
// distributed under the License is distributed on an "AS IS" BASIS,
11-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12-
// See the License for the specific language governing permissions and
13-
// limitations under the License.
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
1416

1517
package com.google.firebase.sessions
1618

firebase-sessions/src/main/kotlin/com/google/firebase/sessions/FirebaseSessions.kt

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
// Copyright 2023 Google LLC
2-
//
3-
// Licensed under the Apache License, Version 2.0 (the "License");
4-
// you may not use this file except in compliance with the License.
5-
// You may obtain a copy of the License at
6-
//
7-
// http://www.apache.org/licenses/LICENSE-2.0
8-
//
9-
// Unless required by applicable law or agreed to in writing, software
10-
// distributed under the License is distributed on an "AS IS" BASIS,
11-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12-
// See the License for the specific language governing permissions and
13-
// limitations under the License.
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
1416

1517
package com.google.firebase.sessions
1618

@@ -23,7 +25,7 @@ import com.google.firebase.ktx.app
2325

2426
class FirebaseSessions internal constructor(firebaseApp: FirebaseApp) {
2527
init {
26-
val sessionInitiator = SessionInitiator(System::currentTimeMillis, this::initiateSessionStart)
28+
val sessionInitiator = SessionInitiator(WallClock::elapsedRealtime, this::initiateSessionStart)
2729
val context = firebaseApp.applicationContext.applicationContext
2830
if (context is Application) {
2931
context.registerActivityLifecycleCallbacks(sessionInitiator.activityLifecycleCallbacks)

firebase-sessions/src/main/kotlin/com/google/firebase/sessions/SessionInitiator.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package com.google.firebase.sessions
1919
import android.app.Activity
2020
import android.app.Application.ActivityLifecycleCallbacks
2121
import android.os.Bundle
22+
import kotlin.time.Duration
23+
import kotlin.time.Duration.Companion.minutes
2224

2325
/**
2426
* The [SessionInitiator] is responsible for calling the [initiateSessionStart] callback whenever a
@@ -28,23 +30,23 @@ import android.os.Bundle
2830
* @hide
2931
*/
3032
internal class SessionInitiator(
31-
private val currentTimeMs: () -> Long,
33+
private val elapsedRealtime: () -> Duration,
3234
private val initiateSessionStart: () -> Unit
3335
) {
34-
private var backgroundTimeMs = currentTimeMs()
35-
private val sessionTimeoutMs = 30 * 60 * 1000L // TODO(mrober): Get session timeout from settings
36+
private var backgroundTime = elapsedRealtime()
37+
private val sessionTimeout = 30.minutes // TODO(mrober): Get session timeout from settings
3638

3739
init {
3840
initiateSessionStart()
3941
}
4042

4143
fun appBackgrounded() {
42-
backgroundTimeMs = currentTimeMs()
44+
backgroundTime = elapsedRealtime()
4345
}
4446

4547
fun appForegrounded() {
46-
val intervalMs = currentTimeMs() - backgroundTimeMs
47-
if (intervalMs > sessionTimeoutMs) {
48+
val interval = elapsedRealtime() - backgroundTime
49+
if (interval > sessionTimeout) {
4850
initiateSessionStart()
4951
}
5052
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.sessions
18+
19+
import android.os.SystemClock
20+
import kotlin.time.Duration
21+
import kotlin.time.Duration.Companion.milliseconds
22+
23+
/** Util object for "wall clock" time functions. */
24+
internal object WallClock {
25+
/** Gets the [Duration] elapsed in "wall clock" time since device boot. */
26+
fun elapsedRealtime(): Duration = SystemClock.elapsedRealtime().milliseconds
27+
}

firebase-sessions/src/test/kotlin/com/google/firebase/sessions/SessionInitiatorTest.kt

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,17 @@
1717
package com.google.firebase.sessions
1818

1919
import com.google.common.truth.Truth.assertThat
20+
import kotlin.time.Duration
21+
import kotlin.time.Duration.Companion.minutes
2022
import org.junit.Test
2123

2224
class SessionInitiatorTest {
23-
class FakeTime {
24-
var currentTimeMs = 0L
25+
class FakeClock {
26+
var elapsed = Duration.ZERO
2527
private set
2628

27-
fun addTimeMs(intervalMs: Long) {
28-
currentTimeMs += intervalMs
29+
fun addInterval(interval: Duration) {
30+
elapsed += interval
2931
}
3032
}
3133

@@ -43,24 +45,24 @@ class SessionInitiatorTest {
4345
val sessionStartCounter = SessionStartCounter()
4446

4547
// Simulate a cold start by simply constructing the SessionInitiator object
46-
SessionInitiator({ 0 }, sessionStartCounter::initiateSessionStart)
48+
SessionInitiator(Duration::ZERO, sessionStartCounter::initiateSessionStart)
4749

4850
assertThat(sessionStartCounter.count).isEqualTo(1)
4951
}
5052

5153
@Test
5254
fun appForegrounded_largeInterval_initiatesSession() {
53-
val fakeTime = FakeTime()
55+
val fakeClock = FakeClock()
5456
val sessionStartCounter = SessionStartCounter()
5557

5658
val sessionInitiator =
57-
SessionInitiator(fakeTime::currentTimeMs, sessionStartCounter::initiateSessionStart)
59+
SessionInitiator(fakeClock::elapsed, sessionStartCounter::initiateSessionStart)
5860

5961
// First session on cold start
6062
assertThat(sessionStartCounter.count).isEqualTo(1)
6163

6264
// Enough tome to initiate a new session, and then foreground
63-
fakeTime.addTimeMs(LARGE_INTERVAL_MS)
65+
fakeClock.addInterval(LARGE_INTERVAL)
6466
sessionInitiator.appForegrounded()
6567

6668
// Another session initiated
@@ -69,17 +71,17 @@ class SessionInitiatorTest {
6971

7072
@Test
7173
fun appForegrounded_smallInterval_doesNotInitiatesSession() {
72-
val fakeTime = FakeTime()
74+
val fakeClock = FakeClock()
7375
val sessionStartCounter = SessionStartCounter()
7476

7577
val sessionInitiator =
76-
SessionInitiator(fakeTime::currentTimeMs, sessionStartCounter::initiateSessionStart)
78+
SessionInitiator(fakeClock::elapsed, sessionStartCounter::initiateSessionStart)
7779

7880
// First session on cold start
7981
assertThat(sessionStartCounter.count).isEqualTo(1)
8082

8183
// Not enough time to initiate a new session, and then foreground
82-
fakeTime.addTimeMs(SMALL_INTERVAL_MS)
84+
fakeClock.addInterval(SMALL_INTERVAL)
8385
sessionInitiator.appForegrounded()
8486

8587
// No new session
@@ -88,44 +90,44 @@ class SessionInitiatorTest {
8890

8991
@Test
9092
fun appForegrounded_background_foreground_largeIntervals_initiatesSessions() {
91-
val fakeTime = FakeTime()
93+
val fakeClock = FakeClock()
9294
val sessionStartCounter = SessionStartCounter()
9395

9496
val sessionInitiator =
95-
SessionInitiator(fakeTime::currentTimeMs, sessionStartCounter::initiateSessionStart)
97+
SessionInitiator(fakeClock::elapsed, sessionStartCounter::initiateSessionStart)
9698

9799
assertThat(sessionStartCounter.count).isEqualTo(1)
98100

99-
fakeTime.addTimeMs(LARGE_INTERVAL_MS)
101+
fakeClock.addInterval(LARGE_INTERVAL)
100102
sessionInitiator.appForegrounded()
101103

102104
assertThat(sessionStartCounter.count).isEqualTo(2)
103105

104106
sessionInitiator.appBackgrounded()
105-
fakeTime.addTimeMs(LARGE_INTERVAL_MS)
107+
fakeClock.addInterval(LARGE_INTERVAL)
106108
sessionInitiator.appForegrounded()
107109

108110
assertThat(sessionStartCounter.count).isEqualTo(3)
109111
}
110112

111113
@Test
112114
fun appForegrounded_background_foreground_smallIntervals_doesNotInitiateNewSessions() {
113-
val fakeTime = FakeTime()
115+
val fakeClock = FakeClock()
114116
val sessionStartCounter = SessionStartCounter()
115117

116118
val sessionInitiator =
117-
SessionInitiator(fakeTime::currentTimeMs, sessionStartCounter::initiateSessionStart)
119+
SessionInitiator(fakeClock::elapsed, sessionStartCounter::initiateSessionStart)
118120

119121
// First session on cold start
120122
assertThat(sessionStartCounter.count).isEqualTo(1)
121123

122-
fakeTime.addTimeMs(SMALL_INTERVAL_MS)
124+
fakeClock.addInterval(SMALL_INTERVAL)
123125
sessionInitiator.appForegrounded()
124126

125127
assertThat(sessionStartCounter.count).isEqualTo(1)
126128

127129
sessionInitiator.appBackgrounded()
128-
fakeTime.addTimeMs(SMALL_INTERVAL_MS)
130+
fakeClock.addInterval(SMALL_INTERVAL)
129131
sessionInitiator.appForegrounded()
130132

131133
assertThat(sessionStartCounter.count).isEqualTo(1)
@@ -134,7 +136,7 @@ class SessionInitiatorTest {
134136
}
135137

136138
companion object {
137-
private const val SMALL_INTERVAL_MS = 3 * 1000L // not enough time to initiate a new session
138-
private const val LARGE_INTERVAL_MS = 90 * 60 * 1000L // enough to initiate another session
139+
private val SMALL_INTERVAL = 29.minutes // not enough time to initiate a new session
140+
private val LARGE_INTERVAL = 31.minutes // enough to initiate another session
139141
}
140142
}

0 commit comments

Comments
 (0)