Skip to content

Commit 3913038

Browse files
committed
wip
1 parent 0507bc2 commit 3913038

File tree

5 files changed

+67
-81
lines changed

5 files changed

+67
-81
lines changed

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

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ 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
2827
import javax.inject.Inject
2928
import javax.inject.Singleton
3029
import kotlin.coroutines.CoroutineContext
@@ -39,13 +38,15 @@ constructor(
3938
private val firebaseApp: FirebaseApp,
4039
private val settings: SessionsSettings,
4140
@Background backgroundDispatcher: CoroutineContext,
42-
sessionsActivityLifecycleCallbacks: Lazy<SessionsActivityLifecycleCallbacks>,
43-
sessionsFallbackActivityLifecycleCallbacks: Lazy<SessionsFallbackActivityLifecycleCallbacks>,
41+
sessionsActivityLifecycleCallbacks: SharedSessionRepository,
42+
sessionsFallbackActivityLifecycleCallbacks: SessionsFallbackActivityLifecycleCallbacks,
4443
) {
4544
init {
4645
Log.d(TAG, "Initializing Firebase Sessions SDK.")
4746
val appContext = firebaseApp.applicationContext.applicationContext
4847
if (appContext is Application) {
48+
SessionInitiator.lifecycleClient = sessionsActivityLifecycleCallbacks
49+
appContext.registerActivityLifecycleCallbacks(SessionInitiator)
4950

5051
CoroutineScope(backgroundDispatcher).launch {
5152
val subscribers = FirebaseSessionsDependencies.getRegisteredSubscribers()
@@ -55,11 +56,8 @@ constructor(
5556
settings.updateSettings()
5657
if (!settings.sessionsEnabled) {
5758
Log.d(TAG, "Sessions SDK disabled. Not listening to lifecycle events.")
58-
appContext.registerActivityLifecycleCallbacks(
59-
sessionsFallbackActivityLifecycleCallbacks.get().activityLifecycleCallbacks
60-
)
61-
} else {
62-
appContext.registerActivityLifecycleCallbacks(sessionsActivityLifecycleCallbacks.get())
59+
sessionsActivityLifecycleCallbacks.unregister()
60+
SessionInitiator.lifecycleClient = sessionsFallbackActivityLifecycleCallbacks
6361
}
6462
firebaseApp.addLifecycleEventListener { _, _ ->
6563
// Log.w(

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,6 @@ internal interface FirebaseSessionsComponent {
114114

115115
@Binds @Singleton fun settingsCache(impl: SettingsCacheImpl): SettingsCache
116116

117-
@Binds
118-
@Singleton
119-
fun sharedSessionRepository(impl: SharedSessionRepositoryImpl): SharedSessionRepository
120-
121117
companion object {
122118
private const val TAG = "FirebaseSessions"
123119

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023 Google LLC
2+
* Copyright 2025 Google LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,22 +19,31 @@ package com.google.firebase.sessions
1919
import android.app.Activity
2020
import android.app.Application.ActivityLifecycleCallbacks
2121
import android.os.Bundle
22-
import javax.inject.Inject
23-
import javax.inject.Singleton
2422

25-
/**
26-
* Lifecycle callbacks that will inform the [SharedSessionRepository] whenever an [Activity] in this
27-
* application process goes foreground or background.
28-
*/
29-
@Singleton
30-
internal class SessionsActivityLifecycleCallbacks
31-
@Inject
32-
constructor(private val sharedSessionRepository: SharedSessionRepository) :
33-
ActivityLifecycleCallbacks {
34-
35-
override fun onActivityResumed(activity: Activity) = sharedSessionRepository.appForeground()
23+
internal interface SessionLifecycleClient {
24+
var localSessionData: SessionData
25+
fun appForegrounded()
26+
fun appBackgrounded()
27+
fun unregister() = Unit
28+
}
3629

37-
override fun onActivityPaused(activity: Activity) = sharedSessionRepository.appBackground()
30+
internal object SessionInitiator : ActivityLifecycleCallbacks {
31+
var currentLocalSession: SessionDetails? = null
32+
get() {
33+
return lifecycleClient?.localSessionData?.sessionDetails
34+
}
35+
var lifecycleClient: SessionLifecycleClient? = null
36+
set(lifecycleClient) {
37+
field = lifecycleClient
38+
}
39+
40+
override fun onActivityResumed(activity: Activity) {
41+
lifecycleClient?.appForegrounded()
42+
}
43+
44+
override fun onActivityPaused(activity: Activity) {
45+
lifecycleClient?.appBackgrounded()
46+
}
3847

3948
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) = Unit
4049

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

Lines changed: 13 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@
1616

1717
package com.google.firebase.sessions
1818

19-
import android.app.Activity
20-
import android.app.Application.ActivityLifecycleCallbacks
21-
import android.os.Bundle
2219
import android.util.Log
2320
import com.google.firebase.annotations.concurrent.Background
2421
import com.google.firebase.sessions.api.FirebaseSessionsDependencies
@@ -42,47 +39,30 @@ constructor(
4239
private val sessionGenerator: SessionGenerator,
4340
private val timeProvider: TimeProvider,
4441
@Background private val backgroundDispatcher: CoroutineContext,
45-
) {
46-
private var localSessionData: SessionData
42+
) : SessionLifecycleClient {
43+
44+
override var localSessionData: SessionData =
45+
SessionData(
46+
sessionDetails = sessionGenerator.generateNewSession(null),
47+
timeProvider.currentTime()
48+
)
49+
4750
init {
48-
localSessionData =
49-
SessionData(
50-
sessionDetails = sessionGenerator.generateNewSession(null),
51-
backgroundTime = timeProvider.currentTime()
52-
)
53-
val sessionId = localSessionData.sessionDetails.sessionId
54-
notifySubscribers(sessionId)
51+
notifySubscribers(localSessionData.sessionDetails.sessionId)
5552
}
5653

57-
fun appBackground() {
58-
localSessionData.copy(backgroundTime = timeProvider.currentTime())
54+
override fun appBackgrounded() {
55+
localSessionData = localSessionData.copy(backgroundTime = timeProvider.currentTime())
5956
}
6057

61-
fun appForeground() {
58+
override fun appForegrounded() {
6259
if (shouldInitiateNewSession(localSessionData)) {
6360
val newSessionDetails = sessionGenerator.generateNewSession(localSessionData.sessionDetails)
64-
localSessionData.copy(sessionDetails = newSessionDetails)
61+
localSessionData = localSessionData.copy(sessionDetails = newSessionDetails)
6562
notifySubscribers(localSessionData.sessionDetails.sessionId)
6663
}
6764
}
6865

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-
8666
private fun notifySubscribers(sessionId: String) {
8767
CoroutineScope(backgroundDispatcher).launch {
8868
// Only notify subscriber for session change, not send to event to firelog

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

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,13 @@ import javax.inject.Inject
2727
import javax.inject.Singleton
2828
import kotlin.coroutines.CoroutineContext
2929
import kotlinx.coroutines.CoroutineScope
30+
import kotlinx.coroutines.Job
31+
import kotlinx.coroutines.cancel
32+
import kotlinx.coroutines.flow.cancellable
3033
import kotlinx.coroutines.launch
3134

32-
/** Repository to persist session data to be shared between all app processes. */
33-
internal interface SharedSessionRepository {
34-
fun appBackground()
35-
36-
fun appForeground()
37-
}
38-
3935
@Singleton
40-
internal class SharedSessionRepositoryImpl
36+
internal class SharedSessionRepository
4137
@Inject
4238
constructor(
4339
private val sessionsSettings: SessionsSettings,
@@ -46,26 +42,29 @@ constructor(
4642
private val timeProvider: TimeProvider,
4743
private val sessionDataStore: DataStore<SessionData>,
4844
@Background private val backgroundDispatcher: CoroutineContext,
49-
) : SharedSessionRepository {
45+
) : SessionLifecycleClient {
5046
/** Local copy of the session data. Can get out of sync, must be double-checked in datastore. */
51-
private lateinit var localSessionData: SessionData
47+
override lateinit var localSessionData: SessionData
48+
49+
private var jobForCancel: Job? = null
5250

5351
init {
54-
CoroutineScope(backgroundDispatcher).launch {
55-
sessionDataStore.data.collect { sessionData ->
56-
localSessionData = sessionData
57-
val sessionId = sessionData.sessionDetails.sessionId
52+
jobForCancel =
53+
CoroutineScope(backgroundDispatcher).launch {
54+
sessionDataStore.data.cancellable().collect { sessionData ->
55+
localSessionData = sessionData
56+
val sessionId = sessionData.sessionDetails.sessionId
5857

59-
FirebaseSessionsDependencies.getRegisteredSubscribers().values.forEach { subscriber ->
60-
// Notify subscribers, regardless of sampling and data collection state
61-
subscriber.onSessionChanged(SessionSubscriber.SessionDetails(sessionId))
62-
Log.d(TAG, "Notified ${subscriber.sessionSubscriberName} of new session $sessionId")
58+
FirebaseSessionsDependencies.getRegisteredSubscribers().values.forEach { subscriber ->
59+
// Notify subscribers, regardless of sampling and data collection state
60+
subscriber.onSessionChanged(SessionSubscriber.SessionDetails(sessionId))
61+
Log.d(TAG, "Notified ${subscriber.sessionSubscriberName} of new session $sessionId")
62+
}
6363
}
6464
}
65-
}
6665
}
6766

68-
override fun appBackground() {
67+
override fun appBackgrounded() {
6968
if (!::localSessionData.isInitialized) {
7069
Log.d(TAG, "App backgrounded, but local SessionData not initialized")
7170
return
@@ -81,7 +80,7 @@ constructor(
8180
}
8281
}
8382

84-
override fun appForeground() {
83+
override fun appForegrounded() {
8584
if (!::localSessionData.isInitialized) {
8685
Log.d(TAG, "App foregrounded, but local SessionData not initialized")
8786
return
@@ -105,6 +104,10 @@ constructor(
105104
}
106105
}
107106

107+
override fun unregister() {
108+
jobForCancel?.cancel("Datastore turned off, stop flow")
109+
}
110+
108111
private fun shouldInitiateNewSession(sessionData: SessionData): Boolean {
109112
val interval = timeProvider.currentTime() - sessionData.backgroundTime
110113
return interval > sessionsSettings.sessionRestartTimeout

0 commit comments

Comments
 (0)