Skip to content

Commit aa00d9b

Browse files
committed
Revert "chore: split LocationTracker into enrichment and sync concerns, add NOT_DETERMINED auth status"
This reverts commit e480d68.
1 parent e480d68 commit aa00d9b

File tree

12 files changed

+252
-324
lines changed

12 files changed

+252
-324
lines changed

location/src/main/kotlin/io/customer/location/LocationCoordinates.kt

Lines changed: 0 additions & 9 deletions
This file was deleted.

location/src/main/kotlin/io/customer/location/LocationEnrichmentProvider.kt

Lines changed: 0 additions & 58 deletions
This file was deleted.

location/src/main/kotlin/io/customer/location/LocationOrchestrator.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import kotlinx.coroutines.CancellationException
1313
internal class LocationOrchestrator(
1414
private val config: LocationModuleConfig,
1515
private val logger: Logger,
16-
private val syncCoordinator: LocationSyncCoordinator,
16+
private val locationTracker: LocationTracker,
1717
private val locationProvider: LocationProvider
1818
) {
1919

@@ -34,7 +34,7 @@ internal class LocationOrchestrator(
3434
granularity = LocationGranularity.DEFAULT
3535
)
3636
logger.debug("Tracking location: lat=${snapshot.latitude}, lng=${snapshot.longitude}")
37-
syncCoordinator.onLocationReceived(snapshot.latitude, snapshot.longitude)
37+
locationTracker.onLocationReceived(snapshot.latitude, snapshot.longitude)
3838
} catch (e: CancellationException) {
3939
logger.debug("Location request was cancelled.")
4040
throw e

location/src/main/kotlin/io/customer/location/LocationServicesImpl.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import kotlinx.coroutines.launch
1414
internal class LocationServicesImpl(
1515
private val config: LocationModuleConfig,
1616
private val logger: Logger,
17-
private val syncCoordinator: LocationSyncCoordinator,
17+
private val locationTracker: LocationTracker,
1818
private val orchestrator: LocationOrchestrator,
1919
private val scope: CoroutineScope
2020
) : LocationServices {
@@ -34,7 +34,7 @@ internal class LocationServicesImpl(
3434

3535
logger.debug("Tracking location: lat=$latitude, lng=$longitude")
3636

37-
syncCoordinator.onLocationReceived(latitude, longitude)
37+
locationTracker.onLocationReceived(latitude, longitude)
3838
}
3939

4040
override fun setLastKnownLocation(location: Location) {

location/src/main/kotlin/io/customer/location/LocationSyncCoordinator.kt

Lines changed: 0 additions & 94 deletions
This file was deleted.
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package io.customer.location
2+
3+
import io.customer.location.store.LocationPreferenceStore
4+
import io.customer.location.sync.LocationSyncFilter
5+
import io.customer.sdk.core.pipeline.DataPipeline
6+
import io.customer.sdk.core.pipeline.IdentifyHook
7+
import io.customer.sdk.core.util.Logger
8+
import io.customer.sdk.util.EventNames
9+
10+
/**
11+
* Coordinates all location state management: persistence, restoration,
12+
* identify context enrichment, and sending location track events.
13+
*
14+
* Location reaches the backend through two independent paths:
15+
*
16+
* 1. **Identify context enrichment** — implements [IdentifyHook].
17+
* Every identify() call enriches the event context with the latest
18+
* location coordinates. This is unfiltered — a new user always gets
19+
* the device's current location on their profile immediately.
20+
*
21+
* 2. **"Location Update" track event** — sent via [DataPipeline.track].
22+
* Gated by a userId check and a sync filter (24h / 1km threshold)
23+
* to avoid redundant events. This creates a discrete event in the
24+
* user's activity timeline for journey/segment triggers.
25+
*
26+
* Profile switch handling is intentionally not tracked here.
27+
* On clearIdentify(), [resetContext] clears all state (cache, persistence,
28+
* sync filter) synchronously during analytics.reset(). On identify(), the
29+
* new user's profile receives the location via path 1 regardless of the
30+
* sync filter's state.
31+
*/
32+
internal class LocationTracker(
33+
private val dataPipeline: DataPipeline?,
34+
private val locationPreferenceStore: LocationPreferenceStore,
35+
private val locationSyncFilter: LocationSyncFilter,
36+
private val logger: Logger
37+
) : IdentifyHook {
38+
39+
@Volatile
40+
private var lastLocation: LocationCoordinates? = null
41+
42+
override fun getIdentifyContext(): Map<String, Any> {
43+
val location = lastLocation ?: return emptyMap()
44+
return mapOf(
45+
"location_latitude" to location.latitude,
46+
"location_longitude" to location.longitude
47+
)
48+
}
49+
50+
/**
51+
* Called synchronously by analytics.reset() during clearIdentify.
52+
* Clears all location state: in-memory cache, persisted coordinates,
53+
* and sync filter — similar to how device tokens and other per-user
54+
* state are cleared on reset. This runs before ResetEvent is published,
55+
* guaranteeing no stale data is available for a subsequent identify().
56+
*/
57+
override fun resetContext() {
58+
lastLocation = null
59+
locationPreferenceStore.clearCachedLocation()
60+
locationSyncFilter.clearSyncedData()
61+
logger.debug("Location state reset")
62+
}
63+
64+
/**
65+
* Reads persisted cached location from the preference store and sets the
66+
* in-memory cache so that identify events have location context
67+
* immediately after SDK restart.
68+
*/
69+
fun restorePersistedLocation() {
70+
val lat = locationPreferenceStore.getCachedLatitude() ?: return
71+
val lng = locationPreferenceStore.getCachedLongitude() ?: return
72+
lastLocation = LocationCoordinates(latitude = lat, longitude = lng)
73+
logger.debug("Restored persisted location: lat=$lat, lng=$lng")
74+
}
75+
76+
/**
77+
* Processes an incoming location: caches in memory, persists
78+
* coordinates, and attempts to send a location track event.
79+
*/
80+
fun onLocationReceived(latitude: Double, longitude: Double) {
81+
logger.debug("Location update received: lat=$latitude, lng=$longitude")
82+
83+
lastLocation = LocationCoordinates(latitude = latitude, longitude = longitude)
84+
locationPreferenceStore.saveCachedLocation(latitude, longitude)
85+
86+
trySendLocationTrack(latitude, longitude)
87+
}
88+
89+
/**
90+
* Called when a user is identified. Attempts to sync the cached
91+
* location as a track event for the newly identified user.
92+
*
93+
* The identify event itself already carries location via
94+
* [getIdentifyContext] — this method handles the supplementary
95+
* "Location Update" track event, subject to the sync filter.
96+
*/
97+
fun onUserIdentified() {
98+
syncCachedLocationIfNeeded()
99+
}
100+
101+
/**
102+
* Re-evaluates the cached location for sending.
103+
* Called on identify (via [onUserIdentified]) and on cold start
104+
* (via replayed UserChangedEvent) to handle cases where a location
105+
* was cached but not yet sent for the current user.
106+
*/
107+
internal fun syncCachedLocationIfNeeded() {
108+
val lat = locationPreferenceStore.getCachedLatitude() ?: return
109+
val lng = locationPreferenceStore.getCachedLongitude() ?: return
110+
111+
logger.debug("Re-evaluating cached location: lat=$lat, lng=$lng")
112+
trySendLocationTrack(lat, lng)
113+
}
114+
115+
/**
116+
* Applies the userId gate and sync filter, then sends a location
117+
* track event via [DataPipeline] if both pass.
118+
*/
119+
private fun trySendLocationTrack(latitude: Double, longitude: Double) {
120+
val pipeline = dataPipeline ?: return
121+
if (!pipeline.isUserIdentified) return
122+
if (!locationSyncFilter.filterAndRecord(latitude, longitude)) return
123+
124+
logger.debug("Sending location track: lat=$latitude, lng=$longitude")
125+
pipeline.track(
126+
name = EventNames.LOCATION_UPDATE,
127+
properties = mapOf(
128+
"latitude" to latitude,
129+
"longitude" to longitude
130+
)
131+
)
132+
}
133+
}
134+
135+
/**
136+
* Internal location coordinate holder, replacing the cross-module Event.LocationData.
137+
*/
138+
internal data class LocationCoordinates(
139+
val latitude: Double,
140+
val longitude: Double
141+
)

location/src/main/kotlin/io/customer/location/ModuleLocation.kt

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -76,27 +76,20 @@ class ModuleLocation @JvmOverloads constructor(
7676
val locationSyncFilter = LocationSyncFilter(
7777
LocationSyncStoreImpl(context, logger)
7878
)
79-
val enrichmentProvider = LocationEnrichmentProvider(store, logger)
80-
val syncCoordinator = LocationSyncCoordinator(
81-
dataPipeline,
82-
store,
83-
locationSyncFilter,
84-
enrichmentProvider,
85-
logger
86-
)
79+
val locationTracker = LocationTracker(dataPipeline, store, locationSyncFilter, logger)
8780

8881
val locationProvider = FusedLocationProvider(context)
8982
val orchestrator = LocationOrchestrator(
9083
config = moduleConfig,
9184
logger = logger,
92-
syncCoordinator = syncCoordinator,
85+
locationTracker = locationTracker,
9386
locationProvider = locationProvider
9487
)
9588

9689
_locationServices = LocationServicesImpl(
9790
config = moduleConfig,
9891
logger = logger,
99-
syncCoordinator = syncCoordinator,
92+
locationTracker = locationTracker,
10093
orchestrator = orchestrator,
10194
scope = locationScope
10295
)
@@ -106,19 +99,20 @@ class ModuleLocation @JvmOverloads constructor(
10699
// for the public API, so callers get silent no-ops with helpful log messages.
107100
if (!moduleConfig.isEnabled) return
108101

109-
enrichmentProvider.restorePersistedLocation()
102+
locationTracker.restorePersistedLocation()
110103

111-
// Register both as IdentifyHooks — enrichment provider adds location to
112-
// identify context, sync coordinator clears sync filter state on reset.
113-
SDKComponent.identifyHookRegistry.register(enrichmentProvider)
114-
SDKComponent.identifyHookRegistry.register(syncCoordinator)
104+
// Register as IdentifyHook so location is added to identify event context
105+
// and cleared synchronously during analytics.reset(). This ensures every
106+
// identify() call carries the device's current location in the event context —
107+
// the primary way location reaches a user's profile.
108+
SDKComponent.identifyHookRegistry.register(locationTracker)
115109

116110
// On identify, attempt to send a supplementary "Location Update" track event.
117111
// The identify event itself already carries location via context enrichment —
118112
// this track event is for journey/segment triggers in the user's timeline.
119113
eventBus.subscribe<Event.UserChangedEvent> {
120114
if (!it.userId.isNullOrEmpty()) {
121-
syncCoordinator.onUserIdentified()
115+
locationTracker.onUserIdentified()
122116
}
123117
}
124118

0 commit comments

Comments
 (0)