Skip to content

feat: add support for geofencing#763

Open
mrehan27 wants to merge 47 commits into
mainfrom
feature/geofence-on-device
Open

feat: add support for geofencing#763
mrehan27 wants to merge 47 commits into
mainfrom
feature/geofence-on-device

Conversation

@mrehan27

@mrehan27 mrehan27 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds support for the upcoming geofence feature, merging feature/geofence-on-device into main.

Highlights

  • On-device geofence monitoring — registers nearby geofences and tracks enter/exit transitions through the OS.
  • Movement-based tiered refresh — re-ranks and re-fetches nearby geofences as the user moves (EXIT-triggered), within the OS geofence budget.
  • Reliable delivery — transitions delivered via WorkManager (survives process death) with a foreground-flush fallback, arbitrated through a shared pending-delivery store; duplicate transitions suppressed by a cooldown filter.
  • Lifecycle resilience — restores geofences after reboot, re-fetches on login, and snapshots userId per transition to prevent cross-user attribution.
  • New :geofence Gradle module with the on-device implementation split out of :location.

Changes

Includes previously approved/merged PRs (oldest first):


Note

High Risk
Large new surface area touching location permissions, background delivery, user identity attribution, and dual-channel event delivery; misconfiguration or races could drop transitions or attribute them to the wrong user.

Overview
Introduces a new :geofence module (ModuleGeofence) for on-device GMS geofencing: fetch nearby fences from POST /geofences/nearest, register the nearest set plus a movement-trigger circle, tiered local re-rank vs remote refresh, boot restore, and sign-out reset. OS enter/exit transitions are persisted and delivered at least once via WorkManager (direct HTTP when userId is snapshotted) and a foreground flush into the analytics pipeline, coordinated by shared PendingDeliveryFlusher / claimSendRestore / sendRemoveOnSuccess on PendingDeliveryStore (including atomic appendAll for geoset fan-out).

Core / datapipelines gain LocationAcquired and GeofenceTransitionEvent on the event bus, SecureUserStore (encrypted userId for workers/receivers), geofenceScope, injectable Clock, Iso8601TimestampFormatter (replaces datapipelines’ SegmentInstantFormatter), and HTTP client GET + query params. CustomerIO persists identity on identify, clears secure store on reset, and maps geofence events to Geofence Transition tracks with pinned userId and transition-time timestamps.

CI adds location and geofence to lint/unit-test matrices; tests and a manual exactly-once plan document the delivery paths.

Reviewed by Cursor Bugbot for commit b1e91c0. Bugbot is set up for automated code reviews on this repo. Configure here.

mrehan27 and others added 30 commits May 8, 2026 15:09
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#706)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…unknown transition types (#707)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…che (#708)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…710)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…711)

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…716)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mrehan27 and others added 4 commits June 24, 2026 01:41
…ion (#750)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mrehan27 mrehan27 self-assigned this Jun 30, 2026
@mrehan27 mrehan27 requested a review from a team as a code owner June 30, 2026 12:36
@github-actions

Copy link
Copy Markdown

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.


transitionId = UUID.randomUUID().toString(),
geofenceName = androidComponent.geofenceRegionStore.getCachedRegionName(geofenceId)
)
androidComponent.pendingGeofenceDeliveryStore.append(entry)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cooldown before durable enqueue

Low Severity

After GeofenceCooldownFilter.tryAcquire succeeds, the cooldown timestamp is persisted before the transition is appended to the pending delivery store. If the disk write fails silently, the OS may fire again within the cooldown window but the SDK will suppress it, with no pending row to flush later.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ecaf91e. Configure here.

@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.91326% with 331 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.76%. Comparing base (b5cf24d) to head (b1e91c0).
⚠️ Report is 25 commits behind head on main.

Files with missing lines Patch % Lines
...main/kotlin/io/customer/geofence/ModuleGeofence.kt 12.72% 48 Missing ⚠️
.../kotlin/io/customer/geofence/di/DIGraphGeofence.kt 25.42% 44 Missing ⚠️
.../io/customer/geofence/GeofenceBroadcastReceiver.kt 67.44% 23 Missing and 5 partials ⚠️
...ain/kotlin/io/customer/geofence/GeofenceManager.kt 74.50% 12 Missing and 14 partials ⚠️
...otlin/io/customer/geofence/GeofenceBootReceiver.kt 19.23% 19 Missing and 2 partials ⚠️
...main/kotlin/io/customer/geofence/GeofenceLogger.kt 51.16% 21 Missing ⚠️
...in/io/customer/geofence/api/GeofenceApiResponse.kt 77.52% 19 Missing and 1 partial ⚠️
...main/kotlin/io/customer/sdk/communication/Event.kt 0.00% 13 Missing ⚠️
.../kotlin/io/customer/geofence/GeofenceRepository.kt 92.39% 0 Missing and 13 partials ⚠️
...tlin/io/customer/sdk/data/store/SecureUserStore.kt 0.00% 12 Missing ⚠️
... and 20 more
Additional details and impacted files
@@             Coverage Diff              @@
##               main     #763      +/-   ##
============================================
- Coverage     69.07%   67.76%   -1.31%     
- Complexity      838     1127     +289     
============================================
  Files           149      206      +57     
  Lines          4601     6400    +1799     
  Branches        628      888     +260     
============================================
+ Hits           3178     4337    +1159     
- Misses         1189     1746     +557     
- Partials        234      317      +83     

☔ View full report in Codecov by Harness.
📢 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
Copy Markdown
  • java_layout: feature/geofence-on-device (1782823065)

@github-actions

Copy link
Copy Markdown
  • kotlin_compose: feature/geofence-on-device (1782823060)

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown

📏 SDK Binary Size Comparison Report

Module Last Recorded Size Current Size Change in Size
core 33.61 KB 43.85 KB ⬆️ +10.24KB
datapipelines 42.43 KB 43.17 KB ⬆️ +0.74KB
messagingpush 31.67 KB 31.66 KB ⬇️ -0.01KB
messaginginapp 123.88 KB 123.88 KB ✅ No Change
tracking-migration 22.89 KB 22.89 KB ✅ No Change
location null 18.66 KB ⬆️ +18.66KB
geofence null 69.52 KB ⬆️ +69.52KB

@github-actions

Copy link
Copy Markdown

Build available to test
Version: feature-geofence-on-device-SNAPSHOT
Repository: https://central.sonatype.com/repository/maven-snapshots/

…sts (#764)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread geofence/src/main/kotlin/io/customer/geofence/GeofenceRepository.kt
/** Null when the fired geofence isn't in the cached region set. */
val geofenceName: String? = null
) : PendingDeliveryStore.PendingDeliveryEntry {
override val key: String get() = "${geofenceId}_${transition.name}_$timestamp"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pending delivery key collisions

Low Severity

PendingGeofenceDelivery.key uses geofence id, transition, and second-granularity timestamp only, while transitionId is excluded. Two pending rows with the same key share one WorkManager unique work (KEEP), and PendingDeliveryStore.claim/remove drop every row with that key—so a second distinct transition in the same second can be merged away and never delivered.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 624e065. Configure here.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
  • java_layout: feature/geofence-on-device (1782986928)

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
  • kotlin_compose: feature/geofence-on-device (1782986929)

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
  • java_layout: feature/geofence-on-device (1782989349)

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
  • kotlin_compose: feature/geofence-on-device (1782989358)

…768)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
  • java_layout: feature/geofence-on-device (1783086353)

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
  • kotlin_compose: feature/geofence-on-device (1783086360)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

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.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 39a89fb. Configure here.

} catch (ce: CancellationException) {
throw ce
} catch (ex: Exception) {
callbacks.onEntryFailed(entry, ex)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Flush drops failed publish

Medium Severity

The geofence foreground flush removes a pending transition from the store before publishing; if publish throws, the entry is gone and neither the analytics pipeline nor WorkManager can deliver that transition again.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 39a89fb. Configure here.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
  • java_layout: feature/geofence-on-device (1783696355)

@github-actions

Copy link
Copy Markdown
  • kotlin_compose: feature/geofence-on-device (1783696370)

@github-actions

Copy link
Copy Markdown
  • java_layout: feature/geofence-on-device (1783696824)

@github-actions

Copy link
Copy Markdown
  • kotlin_compose: feature/geofence-on-device (1783696824)

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