1
1
package com.amplitude.android
2
2
3
+ import com.amplitude.android.Amplitude.Companion.DUMMY_ENTER_FOREGROUND_EVENT
4
+ import com.amplitude.android.Amplitude.Companion.DUMMY_EXIT_FOREGROUND_EVENT
5
+ import com.amplitude.android.Amplitude.Companion.END_SESSION_EVENT
6
+ import com.amplitude.android.Amplitude.Companion.START_SESSION_EVENT
3
7
import com.amplitude.core.Storage
8
+ import com.amplitude.core.Storage.Constants
9
+ import com.amplitude.core.Storage.Constants.LAST_EVENT_ID
10
+ import com.amplitude.core.Storage.Constants.LAST_EVENT_TIME
11
+ import com.amplitude.core.Storage.Constants.PREVIOUS_SESSION_ID
4
12
import com.amplitude.core.events.BaseEvent
5
13
import com.amplitude.core.platform.Timeline
6
14
import kotlinx.coroutines.channels.Channel
@@ -23,21 +31,20 @@ class Timeline(
23
31
var lastEventTime: Long = - 1L
24
32
25
33
internal fun start () {
26
- amplitude.amplitudeScope.launch(amplitude.storageIODispatcher) {
27
- // Wait until build (including possible legacy data migration) is finished.
28
- amplitude.isBuilt.await()
29
-
30
- if (initialSessionId == null ) {
31
- _sessionId .set(
32
- amplitude.storage.read(Storage .Constants .PREVIOUS_SESSION_ID )?.toLongOrNull()
33
- ? : - 1
34
- )
35
- }
36
- lastEventId = amplitude.storage.read(Storage .Constants .LAST_EVENT_ID )?.toLongOrNull() ? : 0
37
- lastEventTime = amplitude.storage.read(Storage .Constants .LAST_EVENT_TIME )?.toLongOrNull() ? : - 1
34
+ with (amplitude) {
35
+ amplitudeScope.launch(storageIODispatcher) {
36
+ // Wait until build (including possible legacy data migration) is finished.
37
+ isBuilt.await()
38
+
39
+ if (initialSessionId == null ) {
40
+ _sessionId .set(storage.readLong(PREVIOUS_SESSION_ID , - 1 ))
41
+ }
42
+ lastEventId = storage.readLong(LAST_EVENT_ID , 0 )
43
+ lastEventTime = storage.readLong(LAST_EVENT_TIME , - 1 )
38
44
39
- for (message in eventMessageChannel) {
40
- processEventMessage(message)
45
+ for (message in eventMessageChannel) {
46
+ processEventMessage(message)
47
+ }
41
48
}
42
49
}
43
50
}
@@ -56,93 +63,89 @@ class Timeline(
56
63
57
64
private suspend fun processEventMessage (message : EventQueueMessage ) {
58
65
val event = message.event
59
- var sessionEvents: Iterable <BaseEvent >? = null
60
- val eventTimestamp = event.timestamp!!
66
+ val eventTimestamp = event.timestamp!! // Guaranteed non-null by process()
61
67
val eventSessionId = event.sessionId
62
- var skipEvent = false
63
-
64
- if (event.eventType == Amplitude .START_SESSION_EVENT ) {
65
- setSessionId(eventSessionId ? : eventTimestamp)
66
- refreshSessionTime(eventTimestamp)
67
- } else if (event.eventType == Amplitude .END_SESSION_EVENT ) {
68
- // do nothing
69
- } else if (event.eventType == Amplitude .DUMMY_ENTER_FOREGROUND_EVENT ) {
70
- skipEvent = true
71
- sessionEvents = startNewSessionIfNeeded(eventTimestamp)
72
- _foreground = true
73
- } else if (event.eventType == Amplitude .DUMMY_EXIT_FOREGROUND_EVENT ) {
74
- skipEvent = true
75
- refreshSessionTime(eventTimestamp)
76
- _foreground = false
77
- } else {
78
- if (! _foreground ) {
79
- sessionEvents = startNewSessionIfNeeded(eventTimestamp)
80
- } else {
68
+
69
+ var localSessionEvents: List <BaseEvent > = emptyList()
70
+
71
+ when (event.eventType) {
72
+ START_SESSION_EVENT -> {
73
+ setSessionId(eventSessionId ? : eventTimestamp)
81
74
refreshSessionTime(eventTimestamp)
82
75
}
83
- }
84
76
85
- if ( ! skipEvent && event.sessionId == null ) {
86
- event.sessionId = sessionId
87
- }
77
+ END_SESSION_EVENT -> {
78
+ // No specific action needed before processing this event type
79
+ }
88
80
89
- val savedLastEventId = lastEventId
81
+ DUMMY_ENTER_FOREGROUND_EVENT -> {
82
+ localSessionEvents = startNewSessionIfNeeded(eventTimestamp)
83
+ _foreground = true
84
+ }
90
85
91
- sessionEvents?.let {
92
- it.forEach { e ->
93
- e.eventId ? : let {
94
- val newEventId = lastEventId + 1
95
- e.eventId = newEventId
96
- lastEventId = newEventId
97
- }
86
+ DUMMY_EXIT_FOREGROUND_EVENT -> {
87
+ refreshSessionTime(eventTimestamp)
88
+ _foreground = false
98
89
}
99
- }
100
90
101
- if (! skipEvent) {
102
- event.eventId ? : let {
103
- val newEventId = lastEventId + 1
104
- event.eventId = newEventId
105
- lastEventId = newEventId
91
+ else -> {
92
+ // Regular event
93
+ if (! _foreground ) {
94
+ localSessionEvents = startNewSessionIfNeeded(eventTimestamp)
95
+ } else {
96
+ refreshSessionTime(eventTimestamp)
97
+ }
106
98
}
107
99
}
108
100
109
- if (lastEventId > savedLastEventId) {
110
- amplitude.storage.write(Storage .Constants .LAST_EVENT_ID , lastEventId.toString())
101
+ val initialLastEventId = lastEventId
102
+
103
+ // Process any local session events first
104
+ for (sessionEvent in localSessionEvents) {
105
+ sessionEvent.eventId = sessionEvent.eventId ? : ++ lastEventId
106
+ super .process(sessionEvent)
111
107
}
112
108
113
- sessionEvents?.let {
114
- it.forEach { e ->
115
- super .process(e)
109
+ // Process the incoming event
110
+ val dummyEvent = event.eventType == DUMMY_ENTER_FOREGROUND_EVENT ||
111
+ event.eventType == DUMMY_EXIT_FOREGROUND_EVENT
112
+ if (! dummyEvent) {
113
+ event.eventId = event.eventId ? : ++ lastEventId
114
+ // Assign sessionId to the current event if it's not a dummy event and doesn't have one
115
+ if (event.sessionId == null ) {
116
+ event.sessionId = this .sessionId // Use this.sessionId for clarity
116
117
}
118
+ super .process(event)
117
119
}
118
120
119
- if (! skipEvent) {
120
- super .process(event)
121
+ // Persist lastEventId if it changed
122
+ if (lastEventId > initialLastEventId) {
123
+ amplitude.storage.write(LAST_EVENT_ID , lastEventId.toString())
121
124
}
122
125
}
123
126
124
- private suspend fun startNewSessionIfNeeded (timestamp : Long ): Iterable <BaseEvent >? {
127
+ private suspend fun startNewSessionIfNeeded (timestamp : Long ): List <BaseEvent > {
125
128
if (inSession() && isWithinMinTimeBetweenSessions(timestamp)) {
126
129
refreshSessionTime(timestamp)
127
- return null
130
+ return emptyList()
128
131
}
129
132
return startNewSession(timestamp)
130
133
}
131
134
132
135
private suspend fun setSessionId (timestamp : Long ) {
133
136
_sessionId .set(timestamp)
134
- amplitude.storage.write(Storage . Constants . PREVIOUS_SESSION_ID , sessionId.toString())
137
+ amplitude.storage.write(PREVIOUS_SESSION_ID , sessionId.toString())
135
138
}
136
139
137
- private suspend fun startNewSession (timestamp : Long ): Iterable <BaseEvent > {
140
+ private suspend fun startNewSession (timestamp : Long ): List <BaseEvent > {
138
141
val sessionEvents = mutableListOf<BaseEvent >()
139
142
val configuration = amplitude.configuration as Configuration
140
143
val trackingSessionEvents = AutocaptureOption .SESSIONS in configuration.autocapture
141
144
142
145
// end previous session
143
146
if (trackingSessionEvents && inSession()) {
144
147
val sessionEndEvent = BaseEvent ()
145
- sessionEndEvent.eventType = Amplitude . END_SESSION_EVENT
148
+ sessionEndEvent.eventType = END_SESSION_EVENT
146
149
sessionEndEvent.timestamp = if (lastEventTime > 0 ) lastEventTime else null
147
150
sessionEndEvent.sessionId = sessionId
148
151
sessionEvents.add(sessionEndEvent)
@@ -153,7 +156,7 @@ class Timeline(
153
156
refreshSessionTime(timestamp)
154
157
if (trackingSessionEvents) {
155
158
val sessionStartEvent = BaseEvent ()
156
- sessionStartEvent.eventType = Amplitude . START_SESSION_EVENT
159
+ sessionStartEvent.eventType = START_SESSION_EVENT
157
160
sessionStartEvent.timestamp = timestamp
158
161
sessionStartEvent.sessionId = sessionId
159
162
sessionEvents.add(sessionStartEvent)
@@ -167,17 +170,22 @@ class Timeline(
167
170
return
168
171
}
169
172
lastEventTime = timestamp
170
- amplitude.storage.write(Storage . Constants . LAST_EVENT_TIME , lastEventTime.toString())
173
+ amplitude.storage.write(LAST_EVENT_TIME , lastEventTime.toString())
171
174
}
172
175
173
176
private fun isWithinMinTimeBetweenSessions (timestamp : Long ): Boolean {
174
- val sessionLimit: Long = (amplitude.configuration as Configuration ).minTimeBetweenSessionsMillis
177
+ val sessionLimit: Long =
178
+ (amplitude.configuration as Configuration ).minTimeBetweenSessionsMillis
175
179
return timestamp - lastEventTime < sessionLimit
176
180
}
177
181
178
182
private fun inSession (): Boolean {
179
183
return sessionId >= 0
180
184
}
185
+
186
+ private fun Storage.readLong (key : Constants , default : Long ): Long {
187
+ return read(key)?.toLongOrNull() ? : default
188
+ }
181
189
}
182
190
183
191
data class EventQueueMessage (
0 commit comments