Skip to content

Commit 572819f

Browse files
authored
chore: add core location types, config, and EventBus event (#654)
1 parent eb4d17e commit 572819f

File tree

13 files changed

+223
-8
lines changed

13 files changed

+223
-8
lines changed

core/api/core.api

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@ public final class io/customer/sdk/communication/Event$DeleteDeviceTokenEvent :
1616
public fun <init> ()V
1717
}
1818

19+
public final class io/customer/sdk/communication/Event$LocationData {
20+
public fun <init> (DD)V
21+
public final fun component1 ()D
22+
public final fun component2 ()D
23+
public final fun copy (DD)Lio/customer/sdk/communication/Event$LocationData;
24+
public static synthetic fun copy$default (Lio/customer/sdk/communication/Event$LocationData;DDILjava/lang/Object;)Lio/customer/sdk/communication/Event$LocationData;
25+
public fun equals (Ljava/lang/Object;)Z
26+
public final fun getLatitude ()D
27+
public final fun getLongitude ()D
28+
public fun hashCode ()I
29+
public fun toString ()Ljava/lang/String;
30+
}
31+
1932
public final class io/customer/sdk/communication/Event$RegisterDeviceTokenEvent : io/customer/sdk/communication/Event {
2033
public fun <init> (Ljava/lang/String;)V
2134
public final fun component1 ()Ljava/lang/String;
@@ -58,6 +71,17 @@ public final class io/customer/sdk/communication/Event$TrackInAppMetricEvent : i
5871
public fun toString ()Ljava/lang/String;
5972
}
6073

74+
public final class io/customer/sdk/communication/Event$TrackLocationEvent : io/customer/sdk/communication/Event {
75+
public fun <init> (Lio/customer/sdk/communication/Event$LocationData;)V
76+
public final fun component1 ()Lio/customer/sdk/communication/Event$LocationData;
77+
public final fun copy (Lio/customer/sdk/communication/Event$LocationData;)Lio/customer/sdk/communication/Event$TrackLocationEvent;
78+
public static synthetic fun copy$default (Lio/customer/sdk/communication/Event$TrackLocationEvent;Lio/customer/sdk/communication/Event$LocationData;ILjava/lang/Object;)Lio/customer/sdk/communication/Event$TrackLocationEvent;
79+
public fun equals (Ljava/lang/Object;)Z
80+
public final fun getLocation ()Lio/customer/sdk/communication/Event$LocationData;
81+
public fun hashCode ()I
82+
public fun toString ()Ljava/lang/String;
83+
}
84+
6185
public final class io/customer/sdk/communication/Event$TrackPushMetricEvent : io/customer/sdk/communication/Event {
6286
public fun <init> (Ljava/lang/String;Lio/customer/sdk/events/Metric;Ljava/lang/String;)V
6387
public final fun component1 ()Ljava/lang/String;

core/src/main/kotlin/io/customer/sdk/communication/Event.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,23 @@ sealed class Event {
5151
) : Event()
5252

5353
class DeleteDeviceTokenEvent : Event()
54+
55+
/**
56+
* Event emitted when a location update should be tracked.
57+
* Published by the Location module and consumed by DataPipeline
58+
* to send location data to Customer.io servers.
59+
*/
60+
data class TrackLocationEvent(
61+
val location: LocationData
62+
) : Event()
63+
64+
/**
65+
* Location data in a framework-agnostic format.
66+
* Used to pass location information between modules without
67+
* requiring Android location framework imports.
68+
*/
69+
data class LocationData(
70+
val latitude: Double,
71+
val longitude: Double
72+
)
5473
}

core/src/main/kotlin/io/customer/sdk/util/EventNames.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@ object EventNames {
1111

1212
// Event name fired by AndroidLifecyclePlugin when app enters background
1313
const val APPLICATION_BACKGROUNDED = "Application Backgrounded"
14+
15+
// Event name for location updates tracked by the Location module
16+
const val LOCATION_UPDATE = "Location Update"
1417
}

datapipelines/src/main/kotlin/io/customer/sdk/CustomerIO.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,21 @@ class CustomerIO private constructor(
151151
eventBus.subscribe<Event.RegisterDeviceTokenEvent> {
152152
registerDeviceToken(deviceToken = it.token)
153153
}
154+
eventBus.subscribe<Event.TrackLocationEvent> {
155+
trackLocation(it)
156+
}
157+
}
158+
159+
private fun trackLocation(event: Event.TrackLocationEvent) {
160+
val location = event.location
161+
logger.debug("tracking location update: lat=${location.latitude}, lng=${location.longitude}")
162+
track(
163+
name = EventNames.LOCATION_UPDATE,
164+
properties = mapOf(
165+
"lat" to location.latitude,
166+
"lng" to location.longitude
167+
)
168+
)
154169
}
155170

156171
private fun migrateTrackingEvents() {

location/api/location.api

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
public final class io/customer/location/LocationModuleConfig : io/customer/sdk/core/module/CustomerIOModuleConfig {
2+
public synthetic fun <init> (ZLkotlin/jvm/internal/DefaultConstructorMarker;)V
3+
public final fun getEnableLocationTracking ()Z
24
}
35

46
public final class io/customer/location/LocationModuleConfig$Builder : io/customer/sdk/core/module/CustomerIOModuleConfig$Builder {
57
public fun <init> ()V
68
public fun build ()Lio/customer/location/LocationModuleConfig;
79
public synthetic fun build ()Lio/customer/sdk/core/module/CustomerIOModuleConfig;
10+
public final fun setEnableLocationTracking (Z)Lio/customer/location/LocationModuleConfig$Builder;
811
}
912

1013
public final class io/customer/location/ModuleLocation : io/customer/sdk/core/module/CustomerIOModule {
1114
public static final field Companion Lio/customer/location/ModuleLocation$Companion;
1215
public static final field MODULE_NAME Ljava/lang/String;
16+
public fun <init> ()V
1317
public fun <init> (Lio/customer/location/LocationModuleConfig;)V
18+
public synthetic fun <init> (Lio/customer/location/LocationModuleConfig;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
1419
public fun getModuleConfig ()Lio/customer/location/LocationModuleConfig;
1520
public synthetic fun getModuleConfig ()Lio/customer/sdk/core/module/CustomerIOModuleConfig;
1621
public fun getModuleName ()Ljava/lang/String;

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

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,33 @@ import io.customer.sdk.core.module.CustomerIOModuleConfig
66
* Location module configurations that can be used to customize
77
* location tracking behavior based on the provided configurations.
88
*/
9-
class LocationModuleConfig private constructor() : CustomerIOModuleConfig {
9+
class LocationModuleConfig private constructor(
10+
/**
11+
* Whether location tracking is enabled.
12+
*
13+
* When false, the location module is effectively disabled and all location
14+
* tracking operations will no-op silently.
15+
*/
16+
val enableLocationTracking: Boolean
17+
) : CustomerIOModuleConfig {
1018

1119
class Builder : CustomerIOModuleConfig.Builder<LocationModuleConfig> {
20+
private var enableLocationTracking: Boolean = true
21+
22+
/**
23+
* Sets whether location tracking is enabled.
24+
* When disabled, all location operations will no-op silently.
25+
* Default is true.
26+
*/
27+
fun setEnableLocationTracking(enable: Boolean): Builder {
28+
this.enableLocationTracking = enable
29+
return this
30+
}
31+
1232
override fun build(): LocationModuleConfig {
13-
return LocationModuleConfig()
33+
return LocationModuleConfig(
34+
enableLocationTracking = enableLocationTracking
35+
)
1436
}
1537
}
1638
}

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

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,28 @@ import io.customer.sdk.core.module.CustomerIOModule
77
* Location module for Customer.io SDK.
88
*
99
* This module provides location tracking capabilities including:
10-
* - Permission and consent management
11-
* - One-shot location capture
12-
* - Continuous location tracking
10+
* - Manual location setting from host app's existing location system
11+
* - One-shot SDK-managed location capture
12+
*
13+
* Usage:
14+
* ```
15+
* val config = CustomerIOConfigBuilder(appContext, "your-api-key")
16+
* .addCustomerIOModule(
17+
* ModuleLocation(
18+
* LocationModuleConfig.Builder()
19+
* .setEnableLocationTracking(true)
20+
* .build()
21+
* )
22+
* )
23+
* .build()
24+
*
25+
* CustomerIO.initialize(config)
26+
* ```
1327
*/
14-
class ModuleLocation(
15-
config: LocationModuleConfig
28+
class ModuleLocation @JvmOverloads constructor(
29+
override val moduleConfig: LocationModuleConfig = LocationModuleConfig.Builder().build()
1630
) : CustomerIOModule<LocationModuleConfig> {
1731
override val moduleName: String = MODULE_NAME
18-
override val moduleConfig: LocationModuleConfig = config
1932

2033
override fun initialize() {
2134
// Module initialization will be implemented in future PRs
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.customer.location.provider
2+
3+
import io.customer.location.type.AuthorizationStatus
4+
import io.customer.location.type.LocationGranularity
5+
import io.customer.location.type.LocationSnapshot
6+
7+
/**
8+
* Abstracts system location services. Implementations wrap platform-specific
9+
* location providers (e.g. FusedLocationProviderClient).
10+
*
11+
* This component does not request location permission. The host app must handle
12+
* runtime permission requests and only call location APIs once authorized.
13+
*/
14+
internal interface LocationProvider {
15+
/**
16+
* One-shot location request. Returns the location result directly.
17+
*
18+
* @param granularity desired accuracy level for the request
19+
* @return the captured location snapshot
20+
* @throws LocationRequestException if location could not be obtained
21+
*/
22+
suspend fun requestLocation(granularity: LocationGranularity): LocationSnapshot
23+
24+
/**
25+
* Current authorization state for location access.
26+
* Used for pre-checks before requesting location.
27+
*/
28+
suspend fun currentAuthorizationStatus(): AuthorizationStatus
29+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.customer.location.provider
2+
3+
import io.customer.location.type.LocationProviderError
4+
5+
/**
6+
* Exception thrown when a location request fails.
7+
* Wraps [LocationProviderError] to provide structured error information.
8+
*/
9+
internal class LocationRequestException(
10+
val error: LocationProviderError,
11+
message: String = "Location request failed: $error",
12+
cause: Throwable? = null
13+
) : Exception(message, cause)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.customer.location.type
2+
3+
/**
4+
* Authorization status for location access.
5+
* Maps Android runtime permission states to SDK-level values
6+
* without depending on Android framework classes.
7+
*
8+
* Note: On Android, [DENIED] covers both "never asked" and "explicitly denied"
9+
* because [android.content.pm.PackageManager.checkPermission] cannot distinguish
10+
* between the two without an Activity context.
11+
*/
12+
internal enum class AuthorizationStatus {
13+
/** Permission not granted (either never asked or explicitly denied). */
14+
DENIED,
15+
16+
/** Foreground-only location access granted. */
17+
AUTHORIZED_FOREGROUND,
18+
19+
/** Background + foreground location access granted. */
20+
AUTHORIZED_BACKGROUND;
21+
22+
/** Whether the app is authorized to use location (foreground or background). */
23+
val isAuthorized: Boolean
24+
get() = this == AUTHORIZED_FOREGROUND || this == AUTHORIZED_BACKGROUND
25+
}

0 commit comments

Comments
 (0)