chore: add SDK-managed one-shot location (requestLocationUpdateOnce)#657
Conversation
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 Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## feat/real-time-location #657 +/- ##
==========================================================
Coverage ? 68.26%
Complexity ? 760
==========================================================
Files ? 142
Lines ? 4323
Branches ? 582
==========================================================
Hits ? 2951
Misses ? 1149
Partials ? 223 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
location/src/main/kotlin/io/customer/location/provider/FusedLocationProvider.kt
Show resolved
Hide resolved
📏 SDK Binary Size Comparison Report
|
|
|
|
Build available to test |
2a698db to
c710f0e
Compare
location/src/main/kotlin/io/customer/location/provider/FusedLocationProvider.kt
Show resolved
Hide resolved
location/src/main/kotlin/io/customer/location/LocationServicesImpl.kt
Outdated
Show resolved
Hide resolved
|
|
c710f0e to
03eb67c
Compare
|
| cause = exception | ||
| ) | ||
| ) | ||
| } |
There was a problem hiding this comment.
All fused location failures misclassified as timeout errors
Low Severity
The addOnFailureListener wraps every failure from getCurrentLocation as LocationProviderError.TIMEOUT, regardless of the actual cause (e.g., Play Services unavailable, ApiException, SecurityException). This misclassifies errors and makes it harder to diagnose issues from debug logs, since the logged error field will always say TIMEOUT even for unrelated failures.
location/src/main/kotlin/io/customer/location/LocationOrchestrator.kt
Outdated
Show resolved
Hide resolved
|
cc37ac3 to
2fba264
Compare
03eb67c to
c690302
Compare
location/src/main/kotlin/io/customer/location/provider/FusedLocationProvider.kt
Outdated
Show resolved
Hide resolved
|
|
c690302 to
060c288
Compare
|
|
2fba264 to
26ec3ae
Compare
060c288 to
fe5c170
Compare
|
location/src/main/kotlin/io/customer/location/LocationOrchestrator.kt
Outdated
Show resolved
Hide resolved
|
fe5c170 to
3a4bb53
Compare
|
|
3a4bb53 to
b679060
Compare
|
|
b679060 to
6806b70
Compare
| if (!authStatus.isAuthorized) { | ||
| logger.debug("Location permission not granted ($authStatus), ignoring request.") | ||
| return | ||
| } |
There was a problem hiding this comment.
Redundant authorization check in orchestrator and provider
Low Severity
LocationOrchestrator.requestLocationUpdate() calls locationProvider.currentAuthorizationStatus() to check permissions, then immediately calls locationProvider.requestLocation(), which internally calls currentAuthorizationStatus() again. Every one-shot location request performs the same permission check twice. This duplicated logic increases maintenance burden — if the authorization check evolves, it needs consistent updates in both places.
Additional Locations (1)
|
|
6806b70 to
6902cf1
Compare
|
|
6902cf1 to
5590940
Compare
| val logger = SDKComponent.logger | ||
| val eventBus = SDKComponent.eventBus | ||
| val context = SDKComponent.android().applicationContext | ||
| val dispatchers = SDKComponent.dispatchersProvider |
There was a problem hiding this comment.
Some of this could be utilized as SDKComponent extension instead?
There was a problem hiding this comment.
These are the same SDKComponent accessors used by other modules (MessagingInApp, PushFCM). We could create an extension or helper, but that would be a cross-module refactor. We can look into it in later PRs
| locationProvider = locationProvider | ||
| ) | ||
|
|
||
| val locationScope = CoroutineScope(dispatchers.default + SupervisorJob()) |
There was a problem hiding this comment.
Shouldn't we use ScopeProvider so it is easier to test?
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
|
|
||
| <!-- Coarse location is sufficient for city/timezone level tracking --> | ||
| <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> |
There was a problem hiding this comment.
Will this have any impact on customers' apps? Or was this added because only customers who want to use the location feature will include this module and should already be fine with the permission?
There was a problem hiding this comment.
it will only be added for the users who adds the module
|
|
6772e13 to
ce9255c
Compare
|
|
|
|
Implement SDK-managed location tracking using FusedLocationProviderClient: - FusedLocationProvider: wraps Google's FusedLocationProviderClient with getCurrentLocation for one-shot requests, cancellation token support, permission pre-checks, and location services availability detection - LocationOrchestrator: coordinates request lifecycle with config checks, authorization verification, and EventBus posting on success - LocationServicesImpl: coroutine-based job management for one-shot requests with cancellation (cancel previous before starting new) - ModuleLocation: wires FusedLocationProvider, orchestrator, and scope - AndroidManifest: declares ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION permissions (merged into host app manifest) Uses PRIORITY_LOW_POWER (~10km accuracy) for privacy-conscious city/timezone level tracking. SDK never requests permissions - host app is responsible. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ce9255c to
e8f0aae
Compare
|
|


Summary
FusedLocationProviderwrapping Google'sFusedLocationProviderClientwith cancellation supportLocationOrchestratorto coordinate config/permission checks and EventBus postingrequestLocationUpdateOnceandstopLocationUpdatesinLocationServicesImplwith coroutine job managementModuleLocation.initialize()to create provider → orchestrator → services chainACCESS_COARSE_LOCATIONandACCESS_FINE_LOCATIONpermissions in the module manifestThe SDK does not request permissions — the host app is responsible for runtime permission requests.
requestLocationUpdateOncechecks permission status, fetches a single locationvia
FusedLocationProviderClient.getCurrentLocation, and publishes the result through EventBus. Only one request is active at a time; calling again cancels the previous one.Note
Medium Risk
Introduces new runtime location access via Google Play Services and adds a new coroutine scope surface (
locationScope), which could affect threading/lifecycle behavior and permission expectations if misused.Overview
Adds SDK-managed one-shot location capture to the Location module:
LocationServices.requestLocationUpdate()now triggers a coroutine-driven request via a newLocationOrchestratorandFusedLocationProviderwrapper (with permission/config checks and cancellation), then publishesTrackLocationEventto theEventBus.Extends core
ScopeProviderwith a dedicatedlocationScopeand wires it throughModuleLocation.initialize(), addsACCESS_COARSE_LOCATIONto the module manifest, updates tests for concurrency timing robustness, and adds unit tests for coordinate validation.Written by Cursor Bugbot for commit e8f0aae. This will update automatically on new commits. Configure here.