Skip to content

chore: add SDK-managed one-shot location (requestLocationUpdateOnce)#657

Merged
Shahroz16 merged 1 commit intofeat/real-time-locationfrom
feat/location-one-shot
Feb 18, 2026
Merged

chore: add SDK-managed one-shot location (requestLocationUpdateOnce)#657
Shahroz16 merged 1 commit intofeat/real-time-locationfrom
feat/location-one-shot

Conversation

@Shahroz16
Copy link
Contributor

@Shahroz16 Shahroz16 commented Feb 16, 2026

Summary

  • Add internal FusedLocationProvider wrapping Google's FusedLocationProviderClient with cancellation support
  • Add internal LocationOrchestrator to coordinate config/permission checks and EventBus posting
  • Wire requestLocationUpdateOnce and stopLocationUpdates in LocationServicesImpl with coroutine job management
  • Wire ModuleLocation.initialize() to create provider → orchestrator → services chain
  • Declare ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION permissions in the module manifest

The SDK does not request permissions — the host app is responsible for runtime permission requests. requestLocationUpdateOnce checks permission status, fetches a single location
via 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 new LocationOrchestrator and FusedLocationProvider wrapper (with permission/config checks and cancellation), then publishes TrackLocationEvent to the EventBus.

Extends core ScopeProvider with a dedicated locationScope and wires it through ModuleLocation.initialize(), adds ACCESS_COARSE_LOCATION to 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.

@Shahroz16 Shahroz16 requested a review from a team as a code owner February 16, 2026 11:52
@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.


@Shahroz16 Shahroz16 self-assigned this Feb 16, 2026
@codecov
Copy link

codecov bot commented Feb 16, 2026

Codecov Report

❌ Patch coverage is 0% with 1 line in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (feat/real-time-location@79bb2c3). Learn more about missing BASE report.

Files with missing lines Patch % Lines
.../kotlin/io/customer/sdk/core/util/ScopeProvider.kt 0.00% 1 Missing ⚠️
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.
📢 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

github-actions bot commented Feb 16, 2026

📏 SDK Binary Size Comparison Report

Module Last Recorded Size Current Size Change in Size
core 30.63 KB 30.39 KB ⬇️ -0.24KB
datapipelines 39.17 KB 39.17 KB ✅ No Change
messagingpush 30.25 KB 30.25 KB ✅ No Change
messaginginapp 106.69 KB 106.69 KB ✅ No Change
tracking-migration 22.89 KB 22.89 KB ✅ No Change

@github-actions
Copy link

  • java_layout: feat/location-one-shot (1771242827)

@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771242827)

@github-actions
Copy link

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

@Shahroz16 Shahroz16 force-pushed the feat/location-one-shot branch from 2a698db to c710f0e Compare February 16, 2026 13:03
@github-actions
Copy link

  • java_layout: feat/location-one-shot (1771247037)

@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771247036)

@Shahroz16 Shahroz16 force-pushed the feat/location-one-shot branch from c710f0e to 03eb67c Compare February 16, 2026 16:40
@github-actions
Copy link

  • java_layout: feat/location-one-shot (1771260112)

cause = exception
)
)
}
Copy link

Choose a reason for hiding this comment

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

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.

Fix in Cursor Fix in Web

@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771260107)

@Shahroz16 Shahroz16 force-pushed the feat/location-manual-api branch from cc37ac3 to 2fba264 Compare February 16, 2026 18:12
@Shahroz16 Shahroz16 force-pushed the feat/location-one-shot branch from 03eb67c to c690302 Compare February 16, 2026 18:12
@github-actions
Copy link

  • java_layout: feat/location-one-shot (1771265962)

@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771265950)

@Shahroz16 Shahroz16 force-pushed the feat/location-one-shot branch from c690302 to 060c288 Compare February 16, 2026 18:49
@github-actions
Copy link

  • java_layout: feat/location-one-shot (1771267811)

@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771267807)

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

  • java_layout: feat/location-one-shot (1771322670)

@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771322650)

@Shahroz16 Shahroz16 force-pushed the feat/location-one-shot branch from fe5c170 to 3a4bb53 Compare February 17, 2026 10:29
@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771324226)

@github-actions
Copy link

  • java_layout: feat/location-one-shot (1771324221)

Base automatically changed from feat/location-manual-api to feat/real-time-location February 17, 2026 10:50
@Shahroz16 Shahroz16 force-pushed the feat/location-one-shot branch from 3a4bb53 to b679060 Compare February 17, 2026 10:53
@github-actions
Copy link

  • java_layout: feat/location-one-shot (1771325684)

@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771325683)

@Shahroz16 Shahroz16 force-pushed the feat/location-one-shot branch from b679060 to 6806b70 Compare February 17, 2026 11:13
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 1 potential issue.

if (!authStatus.isAuthorized) {
logger.debug("Location permission not granted ($authStatus), ignoring request.")
return
}
Copy link

Choose a reason for hiding this comment

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

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)

Fix in Cursor Fix in Web

@github-actions
Copy link

  • java_layout: feat/location-one-shot (1771327058)

@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771327058)

@Shahroz16 Shahroz16 force-pushed the feat/location-one-shot branch from 6806b70 to 6902cf1 Compare February 17, 2026 11:44
@github-actions
Copy link

  • java_layout: feat/location-one-shot (1771328929)

@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771328941)

@Shahroz16 Shahroz16 force-pushed the feat/location-one-shot branch from 6902cf1 to 5590940 Compare February 18, 2026 13:15
val logger = SDKComponent.logger
val eventBus = SDKComponent.eventBus
val context = SDKComponent.android().applicationContext
val dispatchers = SDKComponent.dispatchersProvider
Copy link
Contributor

Choose a reason for hiding this comment

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

Some of this could be utilized as SDKComponent extension instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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())
Copy link
Contributor

Choose a reason for hiding this comment

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

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" />
Copy link
Contributor

Choose a reason for hiding this comment

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

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it will only be added for the users who adds the module

@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771420742)

@github-actions
Copy link

  • java_layout: feat/location-one-shot (1771420854)

@Shahroz16 Shahroz16 force-pushed the feat/location-one-shot branch 2 times, most recently from 6772e13 to ce9255c Compare February 18, 2026 13:58
@github-actions
Copy link

  • java_layout: feat/location-one-shot (1771423011)

@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771423002)

@github-actions
Copy link

  • java_layout: feat/location-one-shot (1771423648)

@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771423636)

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>
@Shahroz16 Shahroz16 force-pushed the feat/location-one-shot branch from ce9255c to e8f0aae Compare February 18, 2026 14:34
@github-actions
Copy link

  • java_layout: feat/location-one-shot (1771425641)

@github-actions
Copy link

  • kotlin_compose: feat/location-one-shot (1771425596)

@Shahroz16 Shahroz16 merged commit 42f29b8 into feat/real-time-location Feb 18, 2026
37 checks passed
@Shahroz16 Shahroz16 deleted the feat/location-one-shot branch February 18, 2026 14:58
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