Skip to content

chore: add manual location API (setLastKnownLocation)#655

Merged
Shahroz16 merged 1 commit intofeat/real-time-locationfrom
feat/location-manual-api
Feb 17, 2026
Merged

chore: add manual location API (setLastKnownLocation)#655
Shahroz16 merged 1 commit intofeat/real-time-locationfrom
feat/location-manual-api

Conversation

@Shahroz16
Copy link
Contributor

@Shahroz16 Shahroz16 commented Feb 16, 2026

Summary

  • Add public LocationServices interface with setLastKnownLocation, requestLocationUpdateOnce, stopLocationUpdates
  • Add internal LocationServicesImpl with coordinate validation and config checks for manual location
  • Expose locationServices on ModuleLocation as a lateinit var (initialized during SDK init)
  • requestLocationUpdateOnce and stopLocationUpdates are stubbed (implemented in the next PR)

This PR introduces the customer-facing location API. Host apps with their own location system can call setLastKnownLocation(lat, lng) or
pass an Android Location object to send location data to Customer.io.


Note

Medium Risk
Introduces a new public API and wiring that emits location events; incorrect initialization order or coordinate handling could lead to missing/extra tracking, though behavior is mostly guarded by validation and config checks.

Overview
Adds a new public LocationServices interface and exposes it via ModuleLocation.locationServices, initialized during module startup with a no-op fallback that logs errors if accessed before CustomerIO.initialize().

Implements manual location tracking in LocationServicesImpl.setLastKnownLocation(...) by validating coordinates, honoring enableLocationTracking, and publishing a TrackLocationEvent; requestLocationUpdateOnce()/stopLocationUpdates() are added to the API but currently stubbed to log only.

Written by Cursor Bugbot for commit b18ac22. This will update automatically on new commits. Configure here.

@Shahroz16 Shahroz16 requested a review from a team as a code owner February 16, 2026 09:11
@Shahroz16 Shahroz16 self-assigned this Feb 16, 2026

override fun stopLocationUpdates() {
// Will be implemented in the SDK-managed location PR
logger.debug("stopLocationUpdates is not yet implemented.")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Location update APIs are non-functional

Medium Severity

requestLocationUpdateOnce() and stopLocationUpdates() are exposed in LocationServices but currently only log messages and perform no location request or cancellation. Calling these APIs appears successful but never emits a TrackLocationEvent, so SDK-managed location tracking is effectively unavailable despite the public contract.

Additional Locations (1)

Fix in Cursor Fix in Web

@github-actions
Copy link

github-actions bot commented Feb 16, 2026

Sample app builds 📱

Below you will find the list of the latest versions of the sample apps. It's recommended to always download the latest builds of the sample apps to accurately test the pull request.


@codecov
Copy link

codecov bot commented Feb 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (feat/real-time-location@572819f). Learn more about missing BASE report.

Additional details and impacted files
@@                    Coverage Diff                     @@
##             feat/real-time-location     #655   +/-   ##
==========================================================
  Coverage                           ?   68.20%           
  Complexity                         ?      760           
==========================================================
  Files                              ?      142           
  Lines                              ?     4322           
  Branches                           ?      582           
==========================================================
  Hits                               ?     2948           
  Misses                             ?     1148           
  Partials                           ?      226           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions
Copy link

Build available to test
Version: feat-location-manual-api-SNAPSHOT
Repository: https://central.sonatype.com/repository/maven-snapshots/

@github-actions
Copy link

  • java_layout: feat/location-manual-api (1771233257)

@github-actions
Copy link

github-actions bot commented Feb 16, 2026

📏 SDK Binary Size Comparison Report

No changes detected in SDK binary size ✅

@github-actions
Copy link

  • kotlin_compose: feat/location-manual-api (1771233263)

@Shahroz16 Shahroz16 force-pushed the feat/location-core-types branch from 7843bc0 to 2102026 Compare February 16, 2026 18:12
@Shahroz16 Shahroz16 force-pushed the feat/location-manual-api branch from cc37ac3 to 2fba264 Compare February 16, 2026 18:12
@github-actions
Copy link

  • java_layout: feat/location-manual-api (1771265613)

@github-actions
Copy link

  • kotlin_compose: feat/location-manual-api (1771265612)

*
* @throws UninitializedPropertyAccessException if accessed before initialization
*/
lateinit var locationServices: LocationServices
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be nice to add a reason why we should be accessing it only after initialize to help avoid breaking when making changes around this in future

@Shahroz16 Shahroz16 force-pushed the feat/location-manual-api branch from 2fba264 to 26ec3ae Compare February 17, 2026 10:03
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

* This guards against race conditions during app startup or incorrect call order.
*/
val locationServices: LocationServices
get() = _locationServices ?: UninitializedLocationServices(SDKComponent.logger)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cached service stays permanently uninitialized

Medium Severity

locationServices returns a new UninitializedLocationServices instance when _locationServices is null. If code reads and stores ModuleLocation.instance().locationServices before module initialization completes, that stored object never switches to LocationServicesImpl, so later setLastKnownLocation calls keep no-oping even after SDK initialization.

Additional Locations (1)

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getter evaluates on every access, it's not a cached field. Normal usage (ModuleLocation.instance().locationServices.setLastKnownLocation(...)) always hits the getter, so it
picks up the real implementation after init. The only risk is if a caller stores the reference before CustomerIO.initialize() completes, which is a very narrow window since both
happen sequentially in Application.onCreate(). Even in that case, the no-op logs an error on every call, making it easy to spot during development.

fun instance(): ModuleLocation {
return SDKComponent.modules[MODULE_NAME] as? ModuleLocation
?: throw IllegalStateException("ModuleLocation not initialized")
?: throw IllegalStateException("ModuleLocation not initialized. Add ModuleLocation to CustomerIOConfigBuilder before calling CustomerIO.initialize().")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-init access still throws via instance

Medium Severity

locationServices has a no-op fallback for pre-initialization access, but the documented access path ModuleLocation.instance().locationServices still throws before SDK initialization. instance() fails before the fallback can run, so early calls can crash instead of no-oping.

Additional Locations (1)

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance() throwing before registration is intentional and consistent with all other SDK modules (ModuleMessagingInApp.instance(), etc.). It surfaces a configuration error, the
developer forgot to call addCustomerIOModule(ModuleLocation()). The no-op fallback on locationServices covers a different case: the module is registered but initialize() hasn't run yet. These are two separate failure modes with two appropriate behaviors, crash for misconfiguration, no-op for timing.

@github-actions
Copy link

  • java_layout: feat/location-manual-api (1771322633)

@github-actions
Copy link

  • kotlin_compose: feat/location-manual-api (1771322659)

Base automatically changed from feat/location-core-types to feat/real-time-location February 17, 2026 10:16
Implement the manual location tracking API allowing host apps to send
their own location data to Customer.io:

- LocationServices interface: public API with setLastKnownLocation
  (raw lat/lng and Android Location overloads), requestLocationUpdateOnce,
  and stopLocationUpdates stubs
- LocationServicesImpl: validates coordinates, checks config, posts
  TrackLocationEvent via EventBus
- UninitializedLocationServices: error-logging stub for pre-init calls
- ModuleLocation: wires LocationServicesImpl on initialize()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Shahroz16 Shahroz16 force-pushed the feat/location-manual-api branch from 26ec3ae to b18ac22 Compare February 17, 2026 10:29
@github-actions
Copy link

  • java_layout: feat/location-manual-api (1771324209)

@github-actions
Copy link

  • kotlin_compose: feat/location-manual-api (1771324210)

@Shahroz16 Shahroz16 merged commit 79bb2c3 into feat/real-time-location Feb 17, 2026
37 checks passed
@Shahroz16 Shahroz16 deleted the feat/location-manual-api branch February 17, 2026 10:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants