Skip to content

Commit c0cd393

Browse files
committed
fallback module
1 parent 9eb1210 commit c0cd393

File tree

2 files changed

+125
-10
lines changed

2 files changed

+125
-10
lines changed

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import com.google.firebase.annotations.concurrent.Background
2424
import com.google.firebase.app
2525
import com.google.firebase.sessions.api.FirebaseSessionsDependencies
2626
import com.google.firebase.sessions.settings.SessionsSettings
27+
import dagger.Lazy
2728
import javax.inject.Inject
2829
import javax.inject.Singleton
2930
import kotlin.coroutines.CoroutineContext
@@ -38,14 +39,13 @@ constructor(
3839
private val firebaseApp: FirebaseApp,
3940
private val settings: SessionsSettings,
4041
@Background backgroundDispatcher: CoroutineContext,
41-
sessionsActivityLifecycleCallbacks: SessionsActivityLifecycleCallbacks,
42+
sessionsActivityLifecycleCallbacks: Lazy<SessionsActivityLifecycleCallbacks>,
43+
sessionsFallbackActivityLifecycleCallbacks: Lazy<SessionsFallbackActivityLifecycleCallbacks>,
4244
) {
43-
4445
init {
4546
Log.d(TAG, "Initializing Firebase Sessions SDK.")
4647
val appContext = firebaseApp.applicationContext.applicationContext
4748
if (appContext is Application) {
48-
appContext.registerActivityLifecycleCallbacks(sessionsActivityLifecycleCallbacks)
4949

5050
CoroutineScope(backgroundDispatcher).launch {
5151
val subscribers = FirebaseSessionsDependencies.getRegisteredSubscribers()
@@ -55,14 +55,18 @@ constructor(
5555
settings.updateSettings()
5656
if (!settings.sessionsEnabled) {
5757
Log.d(TAG, "Sessions SDK disabled. Not listening to lifecycle events.")
58+
appContext.registerActivityLifecycleCallbacks(
59+
sessionsFallbackActivityLifecycleCallbacks.get().activityLifecycleCallbacks
60+
)
5861
} else {
59-
firebaseApp.addLifecycleEventListener { _, _ ->
60-
// Log.w(
61-
// TAG,
62-
// "FirebaseApp instance deleted. Sessions library will stop collecting data.",
63-
// )
64-
// TODO(mrober): Clean up on firebase app delete
65-
}
62+
appContext.registerActivityLifecycleCallbacks(sessionsActivityLifecycleCallbacks.get())
63+
}
64+
firebaseApp.addLifecycleEventListener { _, _ ->
65+
// Log.w(
66+
// TAG,
67+
// "FirebaseApp instance deleted. Sessions library will stop collecting data.",
68+
// )
69+
// TODO(mrober): Clean up on firebase app delete
6670
}
6771
}
6872
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright 2025 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.app.Activity
20+
import android.app.Application.ActivityLifecycleCallbacks
21+
import android.os.Bundle
22+
import android.util.Log
23+
import com.google.firebase.annotations.concurrent.Background
24+
import com.google.firebase.sessions.api.FirebaseSessionsDependencies
25+
import com.google.firebase.sessions.api.SessionSubscriber
26+
import com.google.firebase.sessions.settings.SessionsSettings
27+
import javax.inject.Inject
28+
import javax.inject.Singleton
29+
import kotlin.coroutines.CoroutineContext
30+
import kotlinx.coroutines.CoroutineScope
31+
import kotlinx.coroutines.launch
32+
33+
@Singleton
34+
internal class SessionsFallbackActivityLifecycleCallbacks
35+
@Inject
36+
constructor(
37+
private val sessionsSettings: SessionsSettings,
38+
private val sessionGenerator: SessionGenerator,
39+
private val timeProvider: TimeProvider,
40+
@Background private val backgroundDispatcher: CoroutineContext,
41+
) {
42+
private var localSessionData: SessionData
43+
init {
44+
localSessionData =
45+
SessionData(
46+
sessionDetails = sessionGenerator.generateNewSession(null),
47+
backgroundTime = timeProvider.currentTime()
48+
)
49+
50+
CoroutineScope(backgroundDispatcher).launch {
51+
// Only notify subscriber for session change, not send to event to firelog
52+
FirebaseSessionsDependencies.getRegisteredSubscribers().values.forEach { subscriber ->
53+
subscriber.onSessionChanged(
54+
SessionSubscriber.SessionDetails(localSessionData.sessionDetails.sessionId)
55+
)
56+
Log.d(
57+
TAG,
58+
"Notified ${subscriber.sessionSubscriberName} of new session $localSessionData.sessionId with fallback mode"
59+
)
60+
}
61+
}
62+
}
63+
64+
fun appBackground() {
65+
localSessionData.copy(backgroundTime = timeProvider.currentTime())
66+
}
67+
68+
fun appForeground() {
69+
// Fallback mode, not using datastore, pre multi-process support stage
70+
if (shouldInitiateNewSession(localSessionData)) {
71+
val newSessionDetails = sessionGenerator.generateNewSession(localSessionData.sessionDetails)
72+
localSessionData.copy(sessionDetails = newSessionDetails)
73+
CoroutineScope(backgroundDispatcher).launch {
74+
// Only notify subscriber for session change, not send to event to firelog
75+
FirebaseSessionsDependencies.getRegisteredSubscribers().values.forEach { subscriber ->
76+
subscriber.onSessionChanged(SessionSubscriber.SessionDetails(newSessionDetails.sessionId))
77+
Log.d(
78+
TAG,
79+
"Notified ${subscriber.sessionSubscriberName} of new session $newSessionDetails.sessionId with fallback mode"
80+
)
81+
}
82+
}
83+
}
84+
}
85+
86+
internal val activityLifecycleCallbacks =
87+
object : ActivityLifecycleCallbacks {
88+
override fun onActivityResumed(activity: Activity) = appForeground()
89+
90+
override fun onActivityPaused(activity: Activity) = appBackground()
91+
92+
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) = Unit
93+
94+
override fun onActivityStarted(activity: Activity) = Unit
95+
96+
override fun onActivityStopped(activity: Activity) = Unit
97+
98+
override fun onActivityDestroyed(activity: Activity) = Unit
99+
100+
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit
101+
}
102+
103+
private fun shouldInitiateNewSession(sessionData: SessionData): Boolean {
104+
val interval = timeProvider.currentTime() - sessionData.backgroundTime
105+
return interval > sessionsSettings.sessionRestartTimeout
106+
}
107+
108+
private companion object {
109+
const val TAG = "SessionsFallbackALC"
110+
}
111+
}

0 commit comments

Comments
 (0)