Skip to content

Commit 0507bc2

Browse files
committed
fallback module
1 parent 9eb1210 commit 0507bc2

File tree

2 files changed

+121
-10
lines changed

2 files changed

+121
-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: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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+
/**
34+
* This is the fallback module for datastore implementation (SharedSessionRepository). We are
35+
* fallback to pre multi-process support behavior.
36+
*/
37+
@Singleton
38+
internal class SessionsFallbackActivityLifecycleCallbacks
39+
@Inject
40+
constructor(
41+
private val sessionsSettings: SessionsSettings,
42+
private val sessionGenerator: SessionGenerator,
43+
private val timeProvider: TimeProvider,
44+
@Background private val backgroundDispatcher: CoroutineContext,
45+
) {
46+
private var localSessionData: SessionData
47+
init {
48+
localSessionData =
49+
SessionData(
50+
sessionDetails = sessionGenerator.generateNewSession(null),
51+
backgroundTime = timeProvider.currentTime()
52+
)
53+
val sessionId = localSessionData.sessionDetails.sessionId
54+
notifySubscribers(sessionId)
55+
}
56+
57+
fun appBackground() {
58+
localSessionData.copy(backgroundTime = timeProvider.currentTime())
59+
}
60+
61+
fun appForeground() {
62+
if (shouldInitiateNewSession(localSessionData)) {
63+
val newSessionDetails = sessionGenerator.generateNewSession(localSessionData.sessionDetails)
64+
localSessionData.copy(sessionDetails = newSessionDetails)
65+
notifySubscribers(localSessionData.sessionDetails.sessionId)
66+
}
67+
}
68+
69+
internal val activityLifecycleCallbacks =
70+
object : ActivityLifecycleCallbacks {
71+
override fun onActivityResumed(activity: Activity) = appForeground()
72+
73+
override fun onActivityPaused(activity: Activity) = appBackground()
74+
75+
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) = Unit
76+
77+
override fun onActivityStarted(activity: Activity) = Unit
78+
79+
override fun onActivityStopped(activity: Activity) = Unit
80+
81+
override fun onActivityDestroyed(activity: Activity) = Unit
82+
83+
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit
84+
}
85+
86+
private fun notifySubscribers(sessionId: String) {
87+
CoroutineScope(backgroundDispatcher).launch {
88+
// Only notify subscriber for session change, not send to event to firelog
89+
FirebaseSessionsDependencies.getRegisteredSubscribers().values.forEach { subscriber ->
90+
subscriber.onSessionChanged(SessionSubscriber.SessionDetails(sessionId))
91+
Log.d(
92+
TAG,
93+
"Notified ${subscriber.sessionSubscriberName} of new session $sessionId with fallback mode"
94+
)
95+
}
96+
}
97+
}
98+
99+
private fun shouldInitiateNewSession(sessionData: SessionData): Boolean {
100+
val interval = timeProvider.currentTime() - sessionData.backgroundTime
101+
return interval > sessionsSettings.sessionRestartTimeout
102+
}
103+
104+
private companion object {
105+
const val TAG = "SessionsFallbackALC"
106+
}
107+
}

0 commit comments

Comments
 (0)