@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.
- package/CapacitorBackgroundLocation.podspec +19 -0
- package/LICENSE.md +97 -0
- package/Package.swift +44 -0
- package/README.md +264 -0
- package/android/build.gradle +74 -0
- package/android/src/main/AndroidManifest.xml +37 -0
- package/android/src/main/kotlin/dev/bglocation/BackgroundLocationPlugin.kt +684 -0
- package/android/src/main/kotlin/dev/bglocation/core/Models.kt +76 -0
- package/android/src/main/kotlin/dev/bglocation/core/battery/BGLBatteryHelper.kt +127 -0
- package/android/src/main/kotlin/dev/bglocation/core/boot/BGLBootCompletedReceiver.kt +32 -0
- package/android/src/main/kotlin/dev/bglocation/core/config/BGLConfigParser.kt +114 -0
- package/android/src/main/kotlin/dev/bglocation/core/config/BGLVersion.kt +6 -0
- package/android/src/main/kotlin/dev/bglocation/core/debug/BGLDebugLogger.kt +174 -0
- package/android/src/main/kotlin/dev/bglocation/core/geofence/BGLGeofenceBroadcastReceiver.kt +93 -0
- package/android/src/main/kotlin/dev/bglocation/core/geofence/BGLGeofenceManager.kt +310 -0
- package/android/src/main/kotlin/dev/bglocation/core/http/BGLHttpSender.kt +187 -0
- package/android/src/main/kotlin/dev/bglocation/core/http/BGLLocationBuffer.kt +152 -0
- package/android/src/main/kotlin/dev/bglocation/core/license/BGLBuildConfig.kt +16 -0
- package/android/src/main/kotlin/dev/bglocation/core/license/BGLLicenseEnforcer.kt +137 -0
- package/android/src/main/kotlin/dev/bglocation/core/license/BGLLicenseValidator.kt +134 -0
- package/android/src/main/kotlin/dev/bglocation/core/license/BGLTrialTimer.kt +176 -0
- package/android/src/main/kotlin/dev/bglocation/core/location/BGLAdaptiveFilter.kt +94 -0
- package/android/src/main/kotlin/dev/bglocation/core/location/BGLHeartbeatTimer.kt +38 -0
- package/android/src/main/kotlin/dev/bglocation/core/location/BGLLocationForegroundService.kt +289 -0
- package/android/src/main/kotlin/dev/bglocation/core/location/BGLLocationHelpers.kt +72 -0
- package/android/src/main/kotlin/dev/bglocation/core/location/BGLPermissionManager.kt +99 -0
- package/android/src/main/kotlin/dev/bglocation/core/notification/BGLNotificationHelper.kt +77 -0
- package/dist/esm/definitions.d.ts +390 -0
- package/dist/esm/definitions.js +3 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +26 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +47 -0
- package/dist/esm/web.js +231 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/esm/web.test.d.ts +1 -0
- package/dist/esm/web.test.js +940 -0
- package/dist/esm/web.test.js.map +1 -0
- package/dist/plugin.cjs.js +267 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +270 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/BGLocationCore/Config/BGLConfigParser.swift +88 -0
- package/ios/Sources/BGLocationCore/Config/BGLVersion.swift +6 -0
- package/ios/Sources/BGLocationCore/Debug/BGLDebugLogger.swift +201 -0
- package/ios/Sources/BGLocationCore/Geofence/BGLGeofenceManager.swift +538 -0
- package/ios/Sources/BGLocationCore/Http/BGLHttpSender.swift +227 -0
- package/ios/Sources/BGLocationCore/Http/BGLLocationBuffer.swift +198 -0
- package/ios/Sources/BGLocationCore/License/BGLBuildConfig.swift +11 -0
- package/ios/Sources/BGLocationCore/License/BGLLicenseEnforcer.swift +134 -0
- package/ios/Sources/BGLocationCore/License/BGLLicenseValidator.swift +163 -0
- package/ios/Sources/BGLocationCore/License/BGLTrialTimer.swift +168 -0
- package/ios/Sources/BGLocationCore/Location/BGLAdaptiveFilter.swift +91 -0
- package/ios/Sources/BGLocationCore/Location/BGLHeartbeatTimer.swift +50 -0
- package/ios/Sources/BGLocationCore/Location/BGLLocationData.swift +48 -0
- package/ios/Sources/BGLocationCore/Location/BGLLocationHelpers.swift +42 -0
- package/ios/Sources/BGLocationCore/Location/BGLLocationManager.swift +268 -0
- package/ios/Sources/BGLocationCore/Location/BGLPermissionManager.swift +33 -0
- package/ios/Sources/BackgroundLocationPlugin/BackgroundLocationPlugin.swift +657 -0
- 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>
|