@bglocation/capacitor 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CapacitorBackgroundLocation.podspec +19 -0
  2. package/LICENSE.md +97 -0
  3. package/Package.swift +44 -0
  4. package/README.md +264 -0
  5. package/android/build.gradle +74 -0
  6. package/android/src/main/AndroidManifest.xml +37 -0
  7. package/android/src/main/kotlin/dev/bglocation/BackgroundLocationPlugin.kt +684 -0
  8. package/android/src/main/kotlin/dev/bglocation/core/Models.kt +76 -0
  9. package/android/src/main/kotlin/dev/bglocation/core/battery/BGLBatteryHelper.kt +127 -0
  10. package/android/src/main/kotlin/dev/bglocation/core/boot/BGLBootCompletedReceiver.kt +32 -0
  11. package/android/src/main/kotlin/dev/bglocation/core/config/BGLConfigParser.kt +114 -0
  12. package/android/src/main/kotlin/dev/bglocation/core/config/BGLVersion.kt +6 -0
  13. package/android/src/main/kotlin/dev/bglocation/core/debug/BGLDebugLogger.kt +174 -0
  14. package/android/src/main/kotlin/dev/bglocation/core/geofence/BGLGeofenceBroadcastReceiver.kt +93 -0
  15. package/android/src/main/kotlin/dev/bglocation/core/geofence/BGLGeofenceManager.kt +310 -0
  16. package/android/src/main/kotlin/dev/bglocation/core/http/BGLHttpSender.kt +187 -0
  17. package/android/src/main/kotlin/dev/bglocation/core/http/BGLLocationBuffer.kt +152 -0
  18. package/android/src/main/kotlin/dev/bglocation/core/license/BGLBuildConfig.kt +16 -0
  19. package/android/src/main/kotlin/dev/bglocation/core/license/BGLLicenseEnforcer.kt +137 -0
  20. package/android/src/main/kotlin/dev/bglocation/core/license/BGLLicenseValidator.kt +134 -0
  21. package/android/src/main/kotlin/dev/bglocation/core/license/BGLTrialTimer.kt +176 -0
  22. package/android/src/main/kotlin/dev/bglocation/core/location/BGLAdaptiveFilter.kt +94 -0
  23. package/android/src/main/kotlin/dev/bglocation/core/location/BGLHeartbeatTimer.kt +38 -0
  24. package/android/src/main/kotlin/dev/bglocation/core/location/BGLLocationForegroundService.kt +289 -0
  25. package/android/src/main/kotlin/dev/bglocation/core/location/BGLLocationHelpers.kt +72 -0
  26. package/android/src/main/kotlin/dev/bglocation/core/location/BGLPermissionManager.kt +99 -0
  27. package/android/src/main/kotlin/dev/bglocation/core/notification/BGLNotificationHelper.kt +77 -0
  28. package/dist/esm/definitions.d.ts +390 -0
  29. package/dist/esm/definitions.js +3 -0
  30. package/dist/esm/definitions.js.map +1 -0
  31. package/dist/esm/index.d.ts +4 -0
  32. package/dist/esm/index.js +26 -0
  33. package/dist/esm/index.js.map +1 -0
  34. package/dist/esm/web.d.ts +47 -0
  35. package/dist/esm/web.js +231 -0
  36. package/dist/esm/web.js.map +1 -0
  37. package/dist/esm/web.test.d.ts +1 -0
  38. package/dist/esm/web.test.js +940 -0
  39. package/dist/esm/web.test.js.map +1 -0
  40. package/dist/plugin.cjs.js +267 -0
  41. package/dist/plugin.cjs.js.map +1 -0
  42. package/dist/plugin.js +270 -0
  43. package/dist/plugin.js.map +1 -0
  44. package/ios/Sources/BGLocationCore/Config/BGLConfigParser.swift +88 -0
  45. package/ios/Sources/BGLocationCore/Config/BGLVersion.swift +6 -0
  46. package/ios/Sources/BGLocationCore/Debug/BGLDebugLogger.swift +201 -0
  47. package/ios/Sources/BGLocationCore/Geofence/BGLGeofenceManager.swift +538 -0
  48. package/ios/Sources/BGLocationCore/Http/BGLHttpSender.swift +227 -0
  49. package/ios/Sources/BGLocationCore/Http/BGLLocationBuffer.swift +198 -0
  50. package/ios/Sources/BGLocationCore/License/BGLBuildConfig.swift +11 -0
  51. package/ios/Sources/BGLocationCore/License/BGLLicenseEnforcer.swift +134 -0
  52. package/ios/Sources/BGLocationCore/License/BGLLicenseValidator.swift +163 -0
  53. package/ios/Sources/BGLocationCore/License/BGLTrialTimer.swift +168 -0
  54. package/ios/Sources/BGLocationCore/Location/BGLAdaptiveFilter.swift +91 -0
  55. package/ios/Sources/BGLocationCore/Location/BGLHeartbeatTimer.swift +50 -0
  56. package/ios/Sources/BGLocationCore/Location/BGLLocationData.swift +48 -0
  57. package/ios/Sources/BGLocationCore/Location/BGLLocationHelpers.swift +42 -0
  58. package/ios/Sources/BGLocationCore/Location/BGLLocationManager.swift +268 -0
  59. package/ios/Sources/BGLocationCore/Location/BGLPermissionManager.swift +33 -0
  60. package/ios/Sources/BackgroundLocationPlugin/BackgroundLocationPlugin.swift +657 -0
  61. package/package.json +75 -0
@@ -0,0 +1,19 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(File.dirname(__FILE__), 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'CapacitorBackgroundLocation'
7
+ s.version = package['version']
8
+ s.summary = 'Capacitor plugin for continuous background location tracking'
9
+ s.license = { :type => 'Proprietary', :text => 'UNLICENSED — all rights reserved' }
10
+ s.homepage = 'https://bglocation.dev'
11
+ s.author = 'Szymon Walczak'
12
+ s.source = { :git => 'https://gitlab.com/szymonwalczak/capacitor-background-location.git', :tag => s.version.to_s }
13
+ s.source_files = 'ios/Sources/**/*.{swift,h,m}'
14
+
15
+ s.ios.deployment_target = '15.0'
16
+
17
+ s.dependency 'Capacitor'
18
+ s.swift_version = '5.0'
19
+ end
package/LICENSE.md ADDED
@@ -0,0 +1,97 @@
1
+ Elastic License 2.0 (ELv2)
2
+
3
+ URL: https://www.elastic.co/licensing/elastic-license
4
+
5
+ ## Acceptance
6
+
7
+ By using the software, you agree to all of the terms and conditions below.
8
+
9
+ ## Copyright License
10
+
11
+ The licensor grants you a non-exclusive, royalty-free, worldwide,
12
+ non-sublicensable, non-transferable license to use, copy, distribute, make
13
+ available, and prepare derivative works of the software, in each case subject
14
+ to the limitations and conditions below.
15
+
16
+ ## Limitations
17
+
18
+ You may not provide the software to third parties as a hosted or managed
19
+ service, where the service provides users with access to any substantial set
20
+ of the features or functionality of the software.
21
+
22
+ You may not move, change, disable, or circumvent the license key functionality
23
+ in the software, and you may not remove or obscure any functionality in the
24
+ software that is protected by the license key.
25
+
26
+ You may not alter, remove, or obscure any licensing, copyright, or other notices
27
+ of the licensor in the software. Any use of the licensor's trademarks is subject
28
+ to applicable law.
29
+
30
+ ## Patents
31
+
32
+ The licensor grants you a license, under any patent claims the licensor can
33
+ license, or becomes able to license, to make, have made, use, sell, offer for
34
+ sale, import and have imported the software, in each case subject to the
35
+ limitations and conditions in this license. This license does not cover any
36
+ patent claims that you cause to be infringed by modifications or additions to
37
+ the software. If you or your company make any written claim that the software
38
+ infringes or contributes to infringement of any patent, your patent license for
39
+ the software granted under these terms ends immediately. If your company makes
40
+ such a claim, your patent license ends immediately for work on behalf of your
41
+ company.
42
+
43
+ ## Notices
44
+
45
+ You must ensure that anyone who gets a copy of any part of the software from
46
+ you also gets a copy of these terms.
47
+
48
+ If you modify the software, you must include in any modified copies of the
49
+ software prominent notices stating that you have modified the software.
50
+
51
+ ## No Other Rights
52
+
53
+ These terms do not imply any licenses other than those expressly granted in
54
+ these terms.
55
+
56
+ ## Termination
57
+
58
+ If you use the software in violation of these terms, such use is not licensed,
59
+ and your licenses will automatically terminate. If the licensor provides you
60
+ with a notice of your violation, and you cease all violation of this license no
61
+ later than 30 days after you receive that notice, your licenses will be
62
+ reinstated retroactively. However, if you violate these terms after such
63
+ reinstatement, any additional violation of these terms will cause your licenses
64
+ to terminate automatically and permanently.
65
+
66
+ ## No Liability
67
+
68
+ *As far as the law allows, the software comes as is, without any warranty or
69
+ condition, and the licensor will not be liable to you for any damages arising
70
+ out of these terms or the use or nature of the software, under any kind of
71
+ legal claim.*
72
+
73
+ ## Definitions
74
+
75
+ The **licensor** is the entity offering these terms, and the **software** is the
76
+ software the licensor makes available under these terms, including any portion
77
+ of it.
78
+
79
+ **you** refers to the individual or entity agreeing to these terms.
80
+
81
+ **your company** is any legal entity, sole proprietorship, or other kind of
82
+ organization that you work for, plus all organizations that have control over,
83
+ are under the control of, or are under common control with that organization.
84
+ **control** means ownership of substantially all the assets of an entity, or the
85
+ power to direct its management and policies by vote, contract, or otherwise.
86
+ Control can be direct or indirect.
87
+
88
+ **your licenses** are all the licenses granted to you for the software under
89
+ these terms.
90
+
91
+ **use** means anything you do with the software requiring one of your licenses.
92
+
93
+ **trademark** means trademarks, service marks, and similar rights.
94
+
95
+ ---
96
+
97
+ Copyright (c) 2025-2026 Szymon Walczak. All rights reserved.
package/Package.swift ADDED
@@ -0,0 +1,44 @@
1
+ // swift-tools-version: 5.9
2
+
3
+ // SPM package for Capacitor 8 plugin integration and unit testing.
4
+ // BGLocationCore Swift sources are vendored in ios/Sources/BGLocationCore/
5
+ // (no remote fetch or binary framework needed).
6
+
7
+ import PackageDescription
8
+
9
+ let package = Package(
10
+ name: "CapacitorBglocation",
11
+ platforms: [.iOS(.v15)],
12
+ products: [
13
+ .library(
14
+ name: "CapacitorBglocation",
15
+ targets: ["BackgroundLocationPlugin"]
16
+ )
17
+ ],
18
+ dependencies: [
19
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0"),
20
+ ],
21
+ targets: [
22
+ .target(
23
+ name: "BGLocationCore",
24
+ path: "ios/Sources/BGLocationCore"
25
+ ),
26
+ .target(
27
+ name: "BackgroundLocationPlugin",
28
+ dependencies: [
29
+ .product(name: "Capacitor", package: "capacitor-swift-pm"),
30
+ .product(name: "Cordova", package: "capacitor-swift-pm"),
31
+ "BGLocationCore",
32
+ ],
33
+ path: "ios/Sources/BackgroundLocationPlugin"
34
+ ),
35
+ .testTarget(
36
+ name: "BglocationCoreTests",
37
+ dependencies: [
38
+ "BackgroundLocationPlugin",
39
+ "BGLocationCore",
40
+ ],
41
+ path: "ios/Tests"
42
+ ),
43
+ ]
44
+ )
package/README.md ADDED
@@ -0,0 +1,264 @@
1
+ # @bglocation/capacitor
2
+
3
+ Capacitor 8 plugin for continuous background location tracking on iOS and Android with optional native HTTP POST.
4
+
5
+ > **Status**: POC (Proof of Concept)
6
+
7
+ ## Features
8
+
9
+ - Continuous GPS tracking in background (iOS + Android)
10
+ - Configurable `distanceFilter`, `desiredAccuracy`, update intervals
11
+ - **Adaptive distance filter** — `distanceFilter: 'auto'` dynamically adjusts based on speed (EMA smoothing) to maintain ~10s update interval
12
+ - Heartbeat timer — periodic event even when stationary
13
+ - Native HTTP POST — send location to backend from native layer
14
+ - **Offline buffer** — SQLite-backed queue for failed HTTP requests with automatic retry
15
+ - **Configurable notification** — custom title and text for Android foreground service notification (i18n ready)
16
+ - **Debug mode** — verbose logs, `onDebug` events, optional system sounds, dynamic Android notification with GPS counters
17
+ - **OEM battery killer detection** — warns when battery optimization may kill tracking (Android), with dontkillmyapp.com links
18
+ - **Approximate location warning** — detects and warns when user granted only approximate location (iOS 14+)
19
+ - **Significant Location Change fallback** — SLC watchdog auto-restarts GPS after iOS kills the app (iOS)
20
+ - **Mock location detection** — detects mock/test location providers (Android)
21
+ - **Permission rationale** — emits event before requesting background location on Android 11+ for custom rationale UI
22
+ - **Geofencing** — monitor enter/exit/dwell for up to 20 circular regions (iOS + Android), with persistence across app restarts
23
+ - Events: `onLocation`, `onHeartbeat`, `onHttp`, `onProviderChange`, `onDebug`, `onBatteryWarning`, `onAccuracyWarning`, `onMockLocation`, `onPermissionRationale`, `onTrialExpired`, `onGeofence`
24
+ - Web fallback for development/testing in browser
25
+ - 774 unit tests across 3 platforms
26
+
27
+ ## Documentation
28
+
29
+ Detailed documentation is in the [`.wiki/`](.wiki/README.md) directory:
30
+
31
+ | Document | Description |
32
+ |----------|-------------|
33
+ | [Architecture](.wiki/Architecture.md) | System architecture, data flow, platform differences |
34
+ | [API Reference](.wiki/API.md) | Full API: methods, events, interfaces, error codes |
35
+ | [iOS](.wiki/iOS.md) | CLLocationManager, permissions, heartbeat, background modes |
36
+ | [Android](.wiki/Android.md) | Foreground Service, FusedLocation, permissions, binding |
37
+ | [Web](.wiki/Web.md) | Browser fallback implementation |
38
+ | [HTTP](.wiki/HTTP.md) | Native HTTP sender: config, format, timeouts, errors |
39
+ | [Testing](.wiki/Testing.md) | Test strategy, running tests, CI integration |
40
+ | [Geofencing](.wiki/Geofencing.md) | Geofence API, dwell, persistence, platform differences |
41
+ | [Deployment](.wiki/Deployment.md) | Production checklist: license key, platform requirements, troubleshooting |
42
+ | [Known Limitations](.wiki/Known-Limitations.md) | POC limitations and production roadmap |
43
+
44
+ ## Platforms
45
+
46
+ | Platform | Min Version | Implementation |
47
+ |----------|-------------|----------------|
48
+ | iOS | 15.0 | `CLLocationManager` with background modes |
49
+ | Android | API 26 | `FusedLocationProviderClient` + Foreground Service |
50
+ | Web | — | `navigator.geolocation` (dev/testing fallback) |
51
+
52
+ ## Installation
53
+
54
+ ```bash
55
+ npm install @bglocation/capacitor
56
+ npx cap sync
57
+ ```
58
+
59
+ ### iOS
60
+
61
+ Add **all** of the following keys to `Info.plist`:
62
+
63
+ ```xml
64
+ <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
65
+ <string>We need your location to track the tour route</string>
66
+ <key>NSLocationWhenInUseUsageDescription</key>
67
+ <string>We need your location to show your position on the map</string>
68
+ <key>UIBackgroundModes</key>
69
+ <array>
70
+ <string>location</string>
71
+ </array>
72
+ ```
73
+
74
+ > **Important**: Both `NSLocationAlwaysAndWhenInUseUsageDescription` and `NSLocationWhenInUseUsageDescription` are required. Missing either will cause a runtime crash with a privacy violation. The plugin calls `requestAlwaysAuthorization()` automatically when tracking starts if the user hasn't been prompted yet.
75
+ >
76
+ > `allowsBackgroundLocationUpdates = true` is set internally by the plugin during `configure()`, **before** the app enters background — this is required by iOS.
77
+
78
+ ### Android
79
+
80
+ Permissions are declared in the plugin's `AndroidManifest.xml` and merged automatically:
81
+ - `ACCESS_FINE_LOCATION`
82
+ - `ACCESS_COARSE_LOCATION`
83
+ - `ACCESS_BACKGROUND_LOCATION` (Android 10+)
84
+ - `FOREGROUND_SERVICE` / `FOREGROUND_SERVICE_LOCATION`
85
+
86
+ **Your app must request permissions before calling `start()`**. The plugin only *checks* permissions — it does not prompt the user. On Android 10+ (API 29), the user must explicitly grant "Allow all the time" for background location to work. If permissions are not granted, `start()` will reject with an error.
87
+
88
+ > **iOS vs Android difference**: iOS auto-prompts for `Always` authorization via `requestAlwaysAuthorization()`. Android requires the app to handle permission requests explicitly.
89
+
90
+ ## Quick Start
91
+
92
+ ```typescript
93
+ import { BackgroundLocation } from '@bglocation/capacitor';
94
+
95
+ // 1. Request permissions
96
+ const perms = await BackgroundLocation.checkPermissions();
97
+ if (perms.location !== 'granted') {
98
+ await BackgroundLocation.requestPermissions({ permissions: ['location'] });
99
+ }
100
+ if (perms.backgroundLocation !== 'granted') {
101
+ await BackgroundLocation.requestPermissions({ permissions: ['backgroundLocation'] });
102
+ }
103
+
104
+ // 2. Configure
105
+ await BackgroundLocation.configure({
106
+ distanceFilter: 15, // meters
107
+ desiredAccuracy: 'high', // 'high' | 'balanced' | 'low'
108
+ locationUpdateInterval: 5000, // ms (Android only)
109
+ fastestLocationUpdateInterval: 2000, // ms (Android only)
110
+ heartbeatInterval: 15, // seconds
111
+ http: { // optional — native HTTP POST
112
+ url: 'https://api.example.com/location',
113
+ headers: { Authorization: 'Bearer <token>' },
114
+ buffer: { maxSize: 1000 }, // optional — offline buffer for failed requests
115
+ },
116
+ notification: { // optional — Android foreground service notification
117
+ title: 'Tour Active',
118
+ text: 'Tracking your position',
119
+ },
120
+ debug: true, // optional — verbose logging + onDebug events
121
+ debugSounds: true, // optional — system sounds on events (requires debug: true)
122
+ });
123
+
124
+ // 3. Listen for events
125
+ BackgroundLocation.addListener('onLocation', (location) => {
126
+ console.log('Location:', location.latitude, location.longitude);
127
+ });
128
+
129
+ BackgroundLocation.addListener('onHeartbeat', (event) => {
130
+ console.log('Heartbeat:', event.location);
131
+ });
132
+
133
+ BackgroundLocation.addListener('onHttp', (event) => {
134
+ console.log('HTTP:', event.success, event.statusCode, 'buffered:', event.bufferedCount);
135
+ });
136
+
137
+ BackgroundLocation.addListener('onDebug', (event) => {
138
+ console.log('Debug:', event.message);
139
+ });
140
+
141
+ // 4. Start tracking
142
+ await BackgroundLocation.start();
143
+
144
+ // ... later ...
145
+
146
+ // 5. Stop tracking
147
+ await BackgroundLocation.stop();
148
+ await BackgroundLocation.removeAllListeners();
149
+ ```
150
+
151
+ ## API Overview
152
+
153
+ ### Methods
154
+
155
+ | Method | Returns | Description |
156
+ |--------|---------|-------------|
157
+ | `checkPermissions()` | `LocationPermissionStatus` | Check current permission state |
158
+ | `requestPermissions(options?)` | `LocationPermissionStatus` | Request location permissions |
159
+ | `configure(options)` | `ConfigureResult` | Set tracking parameters (call before `start()`). Supports partial reconfiguration — omitted fields are merged with previous config. |
160
+ | `start()` | `LocationState` | Start background tracking |
161
+ | `stop()` | `LocationState` | Stop tracking |
162
+ | `getState()` | `LocationState` | Get current plugin state |
163
+ | `getCurrentPosition(options?)` | `Location` | One-shot position fix |
164
+ | `checkBatteryOptimization()` | `BatteryWarningEvent` | Check OEM battery optimization state (Android) |
165
+ | `requestBatteryOptimization()` | `BatteryWarningEvent` | Open battery optimization settings (Android) |
166
+ | `addGeofence(options)` | `void` | Add a geofence (max 20) |
167
+ | `addGeofences(options)` | `void` | Add multiple geofences atomically |
168
+ | `removeGeofence(options)` | `void` | Remove a geofence by identifier |
169
+ | `removeAllGeofences()` | `void` | Remove all geofences |
170
+ | `getGeofences()` | `{ geofences: Geofence[] }` | Get registered geofences |
171
+ | `removeAllListeners()` | `void` | Remove all listeners and stop tracking |
172
+
173
+ ### Events
174
+
175
+ | Event | Payload | Trigger |
176
+ |-------|---------|---------|
177
+ | `onLocation` | `Location` | Each GPS update (per `distanceFilter`) |
178
+ | `onHeartbeat` | `HeartbeatEvent` | Every `heartbeatInterval` seconds |
179
+ | `onHttp` | `HttpEvent` | After each native HTTP POST response (includes `bufferedCount`) |
180
+ | `onProviderChange` | `ProviderChangeEvent` | GPS/permission status change |
181
+ | `onDebug` | `DebugEvent` | Debug log message (when `debug: true`) |
182
+ | `onBatteryWarning` | `BatteryWarningEvent` | Battery optimization may kill tracking (Android only) |
183
+ | `onAccuracyWarning` | `AccuracyWarningEvent` | Approximate location granted (iOS only) |
184
+ | `onMockLocation` | `MockLocationEvent` | Mock location detected — once per session (Android only) |
185
+ | `onPermissionRationale` | `PermissionRationaleEvent` | Before requesting background location (Android 11+ only) |
186
+ | `onGeofence` | `GeofenceEvent` | Geofence enter/exit/dwell transition |
187
+
188
+ Full API documentation: [`.wiki/API.md`](.wiki/API.md)
189
+
190
+ ## Licensing
191
+
192
+ The plugin uses an offline RSA-2048-SHA256 license system with a **perpetual + update gating** model:
193
+
194
+ - **Full mode**: No restrictions. Requires a valid license key with active updates.
195
+ - **Trial mode**: `debug=true` forced, 30 min tracking limit, 1 h cooldown.
196
+
197
+ ### Update Gating
198
+
199
+ Each plugin build embeds a build timestamp. If the license's update period (`exp`) has ended before the build date, the plugin runs in trial mode:
200
+
201
+ ```
202
+ License exp = 2027-03-16 (1 year from purchase)
203
+
204
+ Plugin v1.2 (built 2026-05) → exp > build → FULL ✅ forever
205
+ Plugin v2.0 (built 2027-06) → exp < build → TRIAL ⚠️ renewal needed
206
+ ```
207
+
208
+ ### License Renewal
209
+
210
+ 1. Sign in to the [License Portal](https://bglocation.dev/portal)
211
+ 2. Expired licenses show a red **Expired** badge
212
+ 3. Click **Renew** to generate a new key with a fresh 1-year update period
213
+ 4. Update `capacitor.config.ts` with the new key
214
+
215
+ See [`.wiki/Licensing.md`](.wiki/Licensing.md) for full details.
216
+
217
+ ## Testing
218
+
219
+ ```bash
220
+ npm test # Web (Vitest) — 164 tests
221
+ npm run test:android # Android (JUnit + MockK) — 343 tests
222
+ npm run test:ios # iOS (XCTest via SPM) — 267 tests
223
+ ```
224
+
225
+ Total: **774 tests** across 3 platforms (+ 116 in test app = **890 total**).
226
+
227
+ See [`.wiki/Testing.md`](.wiki/Testing.md) for details.
228
+
229
+ ## Development
230
+
231
+ ```bash
232
+ npm run build # TypeScript → dist/
233
+ npm run test:watch # Vitest watch mode
234
+ npm run verify # Build + test both native platforms
235
+ npm run verify:ios # Pod install + xcodebuild
236
+ npm run verify:android # Gradle clean build test
237
+ ```
238
+
239
+ ## Debug Sounds
240
+
241
+ When `debug: true` and `debugSounds: true`, the plugin plays system sounds on key events:
242
+
243
+ | Event | iOS | Android |
244
+ |-------|-----|---------|
245
+ | **Start tracking** | Begin Recording (1054) | `TONE_PROP_BEEP` |
246
+ | **Stop tracking** | End Recording (1055) | `TONE_PROP_BEEP2` |
247
+ | **Location fix** | Tink (1052) | `TONE_PROP_ACK` |
248
+ | **Heartbeat** | Tock (1057) | `TONE_SUP_CONFIRM` |
249
+ | **HTTP error** | SMS Alert (1073) | `TONE_SUP_ERROR` |
250
+ | **HTTP success** | — (silent) | — (silent) |
251
+ | **Geofence add** | Key Press Click (1116) | `TONE_PROP_ACK` |
252
+ | **Geofence event** | Key Press Delete (1117) | `TONE_SUP_CONFIRM` |
253
+
254
+ > Web platform does not play sounds — debug events are available via `onDebug` listener and `console.debug`.
255
+
256
+ ## Known Limitations
257
+
258
+ - No motion/activity detection
259
+
260
+ See [`.wiki/Known-Limitations.md`](.wiki/Known-Limitations.md) for full list and production roadmap.
261
+
262
+ ## License
263
+
264
+ ELv2 — all rights reserved.
@@ -0,0 +1,74 @@
1
+ ext {
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
6
+ playServicesLocationVersion = project.hasProperty('playServicesLocationVersion') ? rootProject.ext.playServicesLocationVersion : '21.0.1'
7
+ }
8
+
9
+ buildscript {
10
+ repositories {
11
+ google()
12
+ mavenCentral()
13
+ }
14
+ dependencies {
15
+ classpath 'com.android.tools.build:gradle:8.13.0'
16
+ classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21'
17
+ }
18
+ }
19
+
20
+ apply plugin: 'com.android.library'
21
+ apply plugin: 'kotlin-android'
22
+
23
+ android {
24
+ namespace "dev.bglocation"
25
+ compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
26
+
27
+ defaultConfig {
28
+ minSdk project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 26
29
+ targetSdk project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
30
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
31
+ buildConfigField "long", "PLUGIN_BUILD_EPOCH", "${(long)(System.currentTimeMillis() / 1000)}L"
32
+ }
33
+
34
+ buildFeatures {
35
+ buildConfig true
36
+ }
37
+
38
+ buildTypes {
39
+ release {
40
+ minifyEnabled false
41
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
42
+ }
43
+ }
44
+
45
+ compileOptions {
46
+ sourceCompatibility JavaVersion.VERSION_21
47
+ targetCompatibility JavaVersion.VERSION_21
48
+ }
49
+
50
+ kotlinOptions {
51
+ jvmTarget = '21'
52
+ }
53
+
54
+ testOptions {
55
+ unitTests.returnDefaultValues = true
56
+ }
57
+ }
58
+
59
+ repositories {
60
+ google()
61
+ mavenCentral()
62
+ }
63
+
64
+ dependencies {
65
+ implementation project(':capacitor-android')
66
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
67
+ implementation "com.google.android.gms:play-services-location:$playServicesLocationVersion"
68
+
69
+ testImplementation "junit:junit:$junitVersion"
70
+ testImplementation "io.mockk:mockk:1.13.16"
71
+ testImplementation "org.json:json:20231013"
72
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
73
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
74
+ }
@@ -0,0 +1,37 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
+
4
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
5
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
6
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
7
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
8
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
9
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
10
+ <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
11
+ <uses-permission android:name="android.permission.INTERNET" />
12
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
13
+
14
+ <application>
15
+ <service
16
+ android:name="dev.bglocation.core.location.BGLLocationForegroundService"
17
+ android:foregroundServiceType="location"
18
+ android:exported="false" />
19
+
20
+ <receiver
21
+ android:name="dev.bglocation.core.geofence.BGLGeofenceBroadcastReceiver"
22
+ android:exported="false">
23
+ <intent-filter>
24
+ <action android:name="dev.bglocation.ACTION_GEOFENCE_EVENT" />
25
+ </intent-filter>
26
+ </receiver>
27
+
28
+ <receiver
29
+ android:name="dev.bglocation.core.boot.BGLBootCompletedReceiver"
30
+ android:exported="true">
31
+ <intent-filter>
32
+ <action android:name="android.intent.action.BOOT_COMPLETED" />
33
+ </intent-filter>
34
+ </receiver>
35
+ </application>
36
+
37
+ </manifest>