@josuelmm/cordova-background-geolocation 3.1.0 → 3.2.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 (38) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/HISTORY.md +6 -0
  3. package/README.md +62 -9
  4. package/RELEASE.MD +15 -4
  5. package/android/CDVBackgroundGeolocation/src/main/java/com/marianhello/bgloc/cordova/ConfigMapper.java +8 -0
  6. package/android/CDVBackgroundGeolocation/src/main/java/com/tenforwardconsulting/bgloc/cordova/BackgroundGeolocationPlugin.java +57 -1
  7. package/android/common/src/main/java/com/marianhello/bgloc/BackgroundGeolocationFacade.java +26 -0
  8. package/android/common/src/main/java/com/marianhello/bgloc/Config.java +44 -0
  9. package/android/common/src/main/java/com/marianhello/bgloc/PostLocationTask.java +13 -0
  10. package/android/common/src/main/java/com/marianhello/bgloc/data/SessionLocationDAO.java +18 -0
  11. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationContract.java +5 -1
  12. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationDAO.java +13 -1
  13. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java +13 -1
  14. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionContract.java +74 -0
  15. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionLocationDAO.java +169 -0
  16. package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +132 -4
  17. package/angular/background-geolocation.service.ts +28 -0
  18. package/angular/dist/background-geolocation.service.d.ts +4 -0
  19. package/angular/dist/esm2022/background-geolocation.service.mjs +13 -1
  20. package/angular/dist/fesm2022/josuelmm-cordova-background-geolocation.mjs +12 -0
  21. package/angular/dist/fesm2022/josuelmm-cordova-background-geolocation.mjs.map +1 -1
  22. package/angular/dist/public-api.d.ts +1 -1
  23. package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.h +4 -0
  24. package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.m +41 -1
  25. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.h +4 -0
  26. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +21 -0
  27. package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.m +12 -3
  28. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +5 -0
  29. package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.h +29 -0
  30. package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.m +31 -0
  31. package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.h +25 -0
  32. package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.m +153 -0
  33. package/package.json +7 -3
  34. package/plugin.xml +8 -1
  35. package/www/BackgroundGeolocation.d.ts +60 -0
  36. package/www/BackgroundGeolocation.js +24 -0
  37. package/www/cordova-channel-stub.js +27 -0
  38. package/www/cordova-exec-stub.js +15 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,60 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.2.0](https://github.com/josuelmm/cordova-background-geolocation/tree/3.2.0) (2026-02-28)
4
+
5
+ ### Added
6
+
7
+ - **Session API for route/recording** — Lets the app keep a full copy of all locations for the *current recording session* in the plugin, independent of the sync queue. When the user reopens the app without internet, the app can restore the entire route from the plugin (no need to rely on `localStorage` for points).
8
+ - **`startSession(success?, fail?)`** — Call when the user starts a route (e.g. "Start" button). Clears the session table and from then on every new location is also stored in the session table. Session data is **not** removed when locations are synced to the server.
9
+ - **`getSessionLocations(success?, fail?)`** — Returns all locations currently in the session table, ordered by time. Same format as `Location` (latitude, longitude, time, speed, altitude, bearing, accuracy, etc.). Use when reopening without internet to rebuild the track.
10
+ - **`clearSession(success?, fail?)`** — Call when the route is finished and sync has succeeded. Clears the session table so the next `startSession()` starts clean.
11
+ - **`getSessionLocationsCount(success?, fail?)`** — Returns the number of locations in the session (e.g. to show "X points" without loading all).
12
+ - **Android:** New table `location_session` (DB version 20), `SessionLocationDAO`, and persistence from `PostLocationTask` when session is active. Session state stored in `SharedPreferences`.
13
+ - **iOS:** New table `location_session` (DB version 5), `MAURSessionLocationDAO` (singleton), and persistence from `MAURPostLocationTask` when session is active. Session state stored in `NSUserDefaults`.
14
+ - **JS / TypeScript:** The four session methods are available on the global plugin and in the `.d.ts`. **Angular:** `BackgroundGeolocationService` exposes `startSession`, `getSessionLocations`, `clearSession`, `getSessionLocationsCount`.
15
+
16
+ ### Documentation
17
+
18
+ - **README.md** — New section *"New in 3.2.0"* describing the session API and typical flow (start route → startSession; reopen without internet → getSessionLocations; finish route → clearSession).
19
+ - **docs/api.md** — Quick reference and full sections for `startSession`, `getSessionLocations`, `clearSession`, `getSessionLocationsCount`.
20
+ - **docs/angular.md** — Session methods added to the methods table.
21
+ - **docs/index.md** — Mention of session/route restore without internet.
22
+ - **CHANGELOG.md** — This entry. **HISTORY.md** — 3.2.0 session methods. **RELEASE.MD** — Version example updated.
23
+
24
+ ### Changed
25
+
26
+ - Version bump to 3.2.0.
27
+
28
+ [Full Changelog](https://github.com/josuelmm/cordova-background-geolocation/compare/3.1.1...3.2.0)
29
+
30
+ ---
31
+
32
+ ## [3.1.1](https://github.com/josuelmm/cordova-background-geolocation/tree/3.1.1) (2026-02-27)
33
+
34
+ ### Added
35
+
36
+ - **Browser / `ng serve` support** — The plugin uses `cordova/exec` and `cordova/channel`, which only exist in the Cordova runtime. To allow Angular (and other webpack-based) builds to succeed when running `ng serve` or building for browser, the package now ships:
37
+ - **Stub modules:** `www/cordova-exec-stub.js` and `www/cordova-channel-stub.js`. When running in the browser they avoid crashes; when running inside Cordova they delegate to the real `cordova.exec` and `cordova/channel`.
38
+ - **`browser` field in `package.json`** so bundlers (e.g. webpack) resolve `cordova/exec` and `cordova/channel` to these stubs. No app-side webpack config is required in normal setups.
39
+
40
+ ### Fixed
41
+
42
+ - **Angular types on Windows** — The emitted `.d.ts` in `angular/dist/` used `from '../www/BackgroundGeolocation'`, which resolves (relative to `dist/`) to `angular/www/`, a folder that does not exist in the published package. Some apps worked around this by creating a junction `angular/www` → `www`. The build now runs a post-step (`scripts/fix-angular-dts-paths.js`) that rewrites these paths to `../../www/BackgroundGeolocation` in the emitted declarations, so types resolve to the package root `www/` and the junction is no longer needed.
43
+
44
+ ### Documentation
45
+
46
+ - **docs/angular.md** — New section *"Build (ng serve / browser)"*: explains why the stubs exist, that webpack uses the `browser` field, and how to add resolve aliases in the app if a bundler does not respect it (e.g. "Can't resolve 'cordova/exec'"). Note that from 3.1.1 the junction workaround for types is unnecessary.
47
+
48
+ - **README.md** — Angular section and "New in" updated to mention 3.1.1 and browser/ng serve compatibility.
49
+
50
+ ### Changed
51
+
52
+ - Version bump to 3.1.1.
53
+
54
+ [Full Changelog](https://github.com/josuelmm/cordova-background-geolocation/compare/3.1.0...3.1.1)
55
+
56
+ ---
57
+
3
58
  ## [3.1.0](https://github.com/josuelmm/cordova-background-geolocation/tree/3.1.0) (2026-02-21)
4
59
 
5
60
  ### Added
package/HISTORY.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  **for cordova-plugin-background-geolocation**
4
4
 
5
+ ## [3.2.0] - 2026-02-28
6
+
7
+ ### Added
8
+
9
+ - **Session API (route/recording):** `startSession()`, `getSessionLocations()`, `clearSession()`, `getSessionLocationsCount()`. Separate session table (Android DB v20, iOS DB v5) so the app can restore the full route when reopening without internet. Session is cleared on `startSession()` and `clearSession()`; not cleared when sync succeeds.
10
+
5
11
  ## [3.1.0] - 2019-09-24
6
12
 
7
13
  ### Fixed
package/README.md CHANGED
@@ -95,6 +95,22 @@ If your app’s merged manifest ends up with a `foregroundServiceType` other tha
95
95
  <string name="plugin_bgloc_content_authority">site.seelight.client.bgloc</string>
96
96
  ```
97
97
 
98
+ **Notification labels (showTime / showDistance):** If you use `showTime: true` or `showDistance: true`, the notification shows a line for elapsed time and one for distance. **By default the labels are in English** ("Time" and "Distance"). If you want Spanish or another language, add these optional strings in your app so the plugin uses them; if you don’t define them, English is used.
99
+
100
+ Example — English (default, optional in `res/values/strings.xml`):
101
+
102
+ ```xml
103
+ <string name="plugin_bgloc_notification_time_label">Time</string>
104
+ <string name="plugin_bgloc_notification_distance_label">Distance</string>
105
+ ```
106
+
107
+ Example — Spanish: add in `res/values-es/strings.xml` (or your locale folder):
108
+
109
+ ```xml
110
+ <string name="plugin_bgloc_notification_time_label">Tiempo</string>
111
+ <string name="plugin_bgloc_notification_distance_label">Distancia</string>
112
+ ```
113
+
98
114
  This makes your app enforce the correct foreground service type and defines the strings the plugin needs for the sync account.
99
115
 
100
116
  ---
@@ -252,7 +268,18 @@ BackgroundGeolocation.stop();
252
268
 
253
269
  ### 5. Sync queue (syncUrl): pending count, force sync, clear queue
254
270
 
255
- When you use `syncUrl`, locations that fail to post to `url` (or that are only queued for sync) are sent in batch to `syncUrl`. You can:
271
+ When you use `syncUrl`, locations that fail to post to `url` (or that are only queued for sync) are sent in batch to `syncUrl`.
272
+
273
+ **How sync sends data (Content-Type):** It depends on the `Content-Type` you set in `httpHeaders`. Many people assume “one request per location”; that is only true for form encoding.
274
+
275
+ | Content-Type | Sync to `syncUrl` |
276
+ |--------------|-------------------|
277
+ | **`application/json`** (default) | **One POST** with a JSON **array** of all locations in the batch. |
278
+ | **`application/x-www-form-urlencoded`** | **One POST per location** (same flat `key=value&...` as real-time to `url`). Same endpoint can handle both. |
279
+
280
+ So: with **JSON** you get one request per batch (e.g. 100 locations in one body). With **form-urlencoded** you get one request per location (one record per POST). For headers, retries, `postTemplate` and full behaviour see [HTTP posting](docs/http_posting.md) and [API](docs/api.md).
281
+
282
+ You can:
256
283
 
257
284
  - **Get pending count** — `getPendingSyncCount()` returns how many locations are waiting to be synced.
258
285
  - **Force sync now** — `forceSync()` sends all pending locations immediately (ignores `syncThreshold`). No-op if `sync: false`.
@@ -277,6 +304,8 @@ BackgroundGeolocation.clearSync().then(function () {
277
304
  });
278
305
  ```
279
306
 
307
+ More on sync (headers, retries, postTemplate): [HTTP posting](docs/http_posting.md). Full options and methods: [API](docs/api.md).
308
+
280
309
  ### 6. Other methods (summary)
281
310
 
282
311
  | Method | Description |
@@ -287,11 +316,15 @@ BackgroundGeolocation.clearSync().then(function () {
287
316
  | `deleteLocation(id, success, fail)` | Delete one location by id. |
288
317
  | `deleteAllLocations(success, fail)` | Delete all stored locations. |
289
318
  | `getCurrentLocation(success, fail, options)` | One-shot location (e.g. timeout, maximumAge). |
290
- | `getPluginVersion(success, fail)` | Plugin version string (e.g. "3.1.0"). |
319
+ | `getPluginVersion(success, fail)` | Plugin version string (e.g. "3.2.0"). |
291
320
  | `checkStatus(success, fail)` | Service status (isRunning, authorization, etc.). |
292
321
  | `showAppSettings()` / `openSettings()` | Open app settings. |
293
322
  | `showLocationSettings()` | Open system location settings. |
294
323
  | `getLogEntries(limit, fromId, minLevel, success, fail)` | Debug log entries. |
324
+ | `startSession(success, fail)` | Start session: clear session table and store all new locations until `clearSession()`. |
325
+ | `getSessionLocations(success, fail)` | All locations in current session (restore route when reopening without internet). |
326
+ | `clearSession(success, fail)` | Clear session table (call when route finished and sync OK). |
327
+ | `getSessionLocationsCount(success, fail)` | Number of locations in current session. |
295
328
 
296
329
  All methods return a **Promise** if you omit the `success` / `fail` callbacks.
297
330
 
@@ -314,6 +347,19 @@ Subscribe with `BackgroundGeolocation.on(eventName, callback)`. Unsubscribe with
314
347
 
315
348
  Full event payloads and options: [Events](docs/events.md). Full API (all options, all methods): [API](docs/api.md).
316
349
 
350
+ ### New in 3.2.0
351
+
352
+ - **Session API for route/recording** — Store all locations for the *current route* in the plugin, independent of sync. When the user reopens the app without internet, you can restore the full track from the plugin (no need for `localStorage` for points).
353
+ - **`startSession()`** — Call when the user starts a route. Clears the session table; from then on every location is also saved in the session table (and not removed when synced).
354
+ - **`getSessionLocations()`** — Returns all session locations (same format as `Location`: latitude, longitude, time, speed, altitude, bearing, accuracy). Use to rebuild the track after reopening without internet.
355
+ - **`clearSession()`** — Call when the route is finished and sync succeeded. Clears the session table.
356
+ - **`getSessionLocationsCount()`** — Returns how many points are in the session (e.g. to show "X points" in the UI).
357
+ - Typical flow: **Start route** → `startSession()` then `start()`. **Reopen without internet** → `getSessionLocations()` and redraw the route. **Finish route and sync OK** → `clearSession()`.
358
+
359
+ ### New in 3.1.1
360
+
361
+ - **Browser / `ng serve` builds** — The plugin can now be bundled by webpack without "Can't resolve 'cordova/exec'" or "Can't resolve 'cordova/channel'". The package ships stub modules and a `browser` field so `ng serve` and browser builds succeed; on device/emulator the stubs delegate to the real Cordova API. See [docs/angular.md](docs/angular.md#build-ng-serve--browser).
362
+
317
363
  ### New in 3.1.0
318
364
 
319
365
  - **`getPendingSyncCount()`** — Number of locations pending to be synced. Use with `forceSync()` for “X pending” UI.
@@ -370,6 +416,10 @@ export class MyService {
370
416
 
371
417
  **You must import `BackgroundGeolocationModule`** in your `AppModule` (or feature module) so the service is provided and AOT builds work. Then inject `BackgroundGeolocationService` as in the example above. See [docs/angular.md](docs/angular.md) for the full snippet.
372
418
 
419
+ **`ng serve` / browser:** From 3.1.1 the plugin includes browser stubs so `ng serve` and web builds complete without "Can't resolve 'cordova/exec'" — see [docs/angular.md](docs/angular.md#build-ng-serve--browser).
420
+
421
+ **Lazy-loaded pages:** If you see **NG0202** or *"dependency at index N is invalid"* when opening a page that injects this service, use an app-defined token and inject by that token (the plugin token can be undefined in the lazy chunk). See [docs/angular.md](docs/angular.md) (Lazy-loaded modules).
422
+
373
423
  **Migrating from @awesome-cordova-plugins/background-geolocation:** there you inject a class named `BackgroundGeolocation`. In this package, `BackgroundGeolocation` is the **global plugin object**, not an injectable class. Use `BackgroundGeolocationService` instead (same API). See [docs/angular.md](docs/angular.md) for details.
374
424
 
375
425
  ### Summary
@@ -394,13 +444,16 @@ No extra wrapper (e.g. Awesome Cordova Plugins) is required.
394
444
 
395
445
  ## Documentation and changelog
396
446
 
397
- - **[Documentation](https://josuelmm.github.io/cordova-background-geolocation/)** — Full docs (API, options, examples).
398
- - **[API reference](docs/api.md)** — All `configure` options, every method (`configure`, `start`, `stop`, `getPendingSyncCount`, `forceSync`, `clearSync`, `getConfig`, `getLocations`, etc.), TypeScript types.
399
- - **[HTTP posting](docs/http_posting.md)** `url` vs `syncUrl`, headers, Content-Type (JSON vs form-urlencoded), sync batch behaviour, `getPendingSyncCount` / `forceSync` / `clearSync`.
400
- - **[Events](docs/events.md)** — All events (`location`, `error`, `stationary`, `activity`, `http_authorization`, etc.) and payloads.
401
- - **[Angular / Ionic](docs/angular.md)** Injectable service, module, same API.
402
- - **[Example](docs/example.md)** Full example with events and sync.
403
- - **[CHANGELOG](CHANGELOG.md)** Version history.
447
+ This README is the main entry point. For more detail, edge cases and examples use the docs below (and the [online documentation](https://josuelmm.github.io/cordova-background-geolocation/)).
448
+
449
+ | Doc | What you’ll find |
450
+ |-----|------------------|
451
+ | **[API reference](docs/api.md)** | Every `configure` option, every method (`configure`, `start`, `stop`, `getPendingSyncCount`, `forceSync`, `clearSync`, `startSession`, `getSessionLocations`, `clearSession`, `getSessionLocationsCount`, `getConfig`, `getLocations`, etc.), TypeScript types. |
452
+ | **[HTTP posting](docs/http_posting.md)** | `url` vs `syncUrl`, Content-Type (JSON = one POST with array; form-urlencoded = one POST per location), headers, retries, `postTemplate`, sync behaviour. |
453
+ | **[Events](docs/events.md)** | All events (`location`, `error`, `stationary`, `activity`, `http_authorization`, etc.) and payloads. |
454
+ | **[Angular / Ionic](docs/angular.md)** | Injectable service, module, lazy-loaded modules and token “must be defined”, `ng serve` / browser build. |
455
+ | **[Example](docs/example.md)** | Full example with events and sync. |
456
+ | **[CHANGELOG](CHANGELOG.md)** | Version history. |
404
457
 
405
458
  This project is based on [@mauron85/cordova-plugin-background-geolocation](https://github.com/mauron85/cordova-plugin-background-geolocation) and the original by [christocracy](https://github.com/christocracy). Maintained at [josuelmm/cordova-background-geolocation](https://github.com/josuelmm/cordova-background-geolocation). Issues and PRs welcome.
406
459
 
package/RELEASE.MD CHANGED
@@ -1,5 +1,16 @@
1
1
  In order to release a version the following actions are needed:
2
- 1. Update the version in the package.json, lock and plugin.xml files
3
- 2. Go to [releases and create a new release](https://github.com/josuelmm/cordova-background-geolocation/releases/new) in GitHub
4
- 3. Make sure to create a tag too with the version name (e.g. v3.1.0)
5
- 4. Click create. Github actions will publish the package to npm
2
+
3
+ 1. **Version and docs**
4
+ - Set the new version (e.g. `3.2.0`) in:
5
+ - `package.json` (`version` field)
6
+ - `plugin.xml` (root `<plugin>` attribute `version`)
7
+ - Add a release entry in `CHANGELOG.md` (date, Added/Fixed/Changed/Documentation).
8
+ - Update `README.md` if needed (e.g. "New in X.Y.Z", example version strings).
9
+
10
+ 2. **Lock file**
11
+ - Run `npm install` so `package-lock.json` stays in sync with `package.json`.
12
+
13
+ 3. **Git tag and release**
14
+ - Go to [Releases → Create a new release](https://github.com/josuelmm/cordova-background-geolocation/releases/new).
15
+ - Create a tag with the version name (e.g. `v3.2.0`).
16
+ - Publish the release. GitHub Actions will publish the package to npm.
@@ -116,6 +116,12 @@ public class ConfigMapper {
116
116
  if (jObject.has("enableWatchdog")) {
117
117
  config.setEnableWatchdog(jObject.getBoolean("enableWatchdog"));
118
118
  }
119
+ if (jObject.has("showTime")) {
120
+ config.setShowTime(jObject.getBoolean("showTime"));
121
+ }
122
+ if (jObject.has("showDistance")) {
123
+ config.setShowDistance(jObject.getBoolean("showDistance"));
124
+ }
119
125
 
120
126
  return config;
121
127
  }
@@ -151,6 +157,8 @@ public class ConfigMapper {
151
157
  json.put("httpHeaders", new JSONObject(config.getHttpHeaders()));
152
158
  json.put("maxLocations", config.getMaxLocations());
153
159
  json.put("enableWatchdog", Boolean.TRUE.equals(config.getEnableWatchdog()));
160
+ json.put("showTime", Boolean.TRUE.equals(config.getShowTime()));
161
+ json.put("showDistance", Boolean.TRUE.equals(config.getShowDistance()));
154
162
  LocationTemplate tpl = config.getTemplate();
155
163
  Object template = JSONObject.NULL;
156
164
  if (tpl instanceof HashMapLocationTemplate) {
@@ -73,10 +73,14 @@ public class BackgroundGeolocationPlugin extends CordovaPlugin implements Plugin
73
73
  public static final String ACTION_FORCE_SYNC = "forceSync";
74
74
  public static final String ACTION_CLEAR_SYNC = "clearSync";
75
75
  public static final String ACTION_GET_PENDING_SYNC_COUNT = "getPendingSyncCount";
76
+ public static final String ACTION_START_SESSION = "startSession";
77
+ public static final String ACTION_GET_SESSION_LOCATIONS = "getSessionLocations";
78
+ public static final String ACTION_CLEAR_SESSION = "clearSession";
79
+ public static final String ACTION_GET_SESSION_LOCATIONS_COUNT = "getSessionLocationsCount";
76
80
  public static final String ACTION_GET_PLUGIN_VERSION = "getPluginVersion";
77
81
 
78
82
  /** Plugin version; keep in sync with plugin.xml. */
79
- public static final String PLUGIN_VERSION = "3.1.0";
83
+ public static final String PLUGIN_VERSION = "3.2.0";
80
84
 
81
85
  private BackgroundGeolocationFacade facade;
82
86
 
@@ -399,6 +403,49 @@ public class BackgroundGeolocationPlugin extends CordovaPlugin implements Plugin
399
403
  }
400
404
  });
401
405
  return true;
406
+ } else if (ACTION_START_SESSION.equals(action)) {
407
+ runOnWebViewThread(new Runnable() {
408
+ @Override
409
+ public void run() {
410
+ facade.startSession();
411
+ callbackContext.success();
412
+ }
413
+ });
414
+ return true;
415
+ } else if (ACTION_GET_SESSION_LOCATIONS.equals(action)) {
416
+ runOnWebViewThread(new Runnable() {
417
+ @Override
418
+ public void run() {
419
+ try {
420
+ callbackContext.success(getSessionLocations());
421
+ } catch (JSONException e) {
422
+ callbackContext.sendPluginResult(ErrorPluginResult.from("getSessionLocations failed", e, PluginException.JSON_ERROR));
423
+ }
424
+ }
425
+ });
426
+ return true;
427
+ } else if (ACTION_CLEAR_SESSION.equals(action)) {
428
+ runOnWebViewThread(new Runnable() {
429
+ @Override
430
+ public void run() {
431
+ facade.clearSession();
432
+ callbackContext.success();
433
+ }
434
+ });
435
+ return true;
436
+ } else if (ACTION_GET_SESSION_LOCATIONS_COUNT.equals(action)) {
437
+ runOnWebViewThread(new Runnable() {
438
+ @Override
439
+ public void run() {
440
+ try {
441
+ int count = facade.getSessionLocationsCount();
442
+ callbackContext.success(count);
443
+ } catch (Exception e) {
444
+ callbackContext.sendPluginResult(ErrorPluginResult.from("getSessionLocationsCount failed", e, PluginException.SERVICE_ERROR));
445
+ }
446
+ }
447
+ });
448
+ return true;
402
449
  } else if (ACTION_GET_PLUGIN_VERSION.equals(action)) {
403
450
  callbackContext.success(PLUGIN_VERSION);
404
451
  return true;
@@ -557,6 +604,15 @@ public class BackgroundGeolocationPlugin extends CordovaPlugin implements Plugin
557
604
  return jsonLocationsArray;
558
605
  }
559
606
 
607
+ private JSONArray getSessionLocations() throws JSONException {
608
+ JSONArray jsonLocationsArray = new JSONArray();
609
+ Collection<BackgroundLocation> locations = facade.getSessionLocations();
610
+ for (BackgroundLocation location : locations) {
611
+ jsonLocationsArray.put(location.toJSONObjectWithId());
612
+ }
613
+ return jsonLocationsArray;
614
+ }
615
+
560
616
  private JSONArray getLogs(Integer limit, int offset, String minLevel) throws Exception {
561
617
  JSONArray jsonLogsArray = new JSONArray();
562
618
  Collection<LogEntry> logEntries = facade.getLogEntries(limit, offset, minLevel);
@@ -26,11 +26,13 @@ import com.marianhello.bgloc.data.BackgroundLocation;
26
26
  import com.marianhello.bgloc.data.ConfigurationDAO;
27
27
  import com.marianhello.bgloc.data.DAOFactory;
28
28
  import com.marianhello.bgloc.data.LocationDAO;
29
+ import com.marianhello.bgloc.data.SessionLocationDAO;
29
30
  import com.marianhello.bgloc.provider.LocationProvider;
30
31
  import com.marianhello.bgloc.service.LocationService;
31
32
  import com.marianhello.bgloc.service.LocationServiceImpl;
32
33
  import com.marianhello.bgloc.service.LocationServiceProxy;
33
34
  import com.marianhello.bgloc.data.LocationTransform;
35
+ import com.marianhello.bgloc.data.sqlite.SQLiteSessionLocationDAO;
34
36
  import com.marianhello.bgloc.sync.AccountHelper;
35
37
  import com.marianhello.bgloc.sync.NotificationHelper;
36
38
  import com.marianhello.bgloc.sync.SyncService;
@@ -294,6 +296,30 @@ public class BackgroundGeolocationFacade {
294
296
  return dao.getValidLocationsAndDelete();
295
297
  }
296
298
 
299
+ /** Clear session table and start storing all new locations in session. Call when user starts a route. */
300
+ public void startSession() {
301
+ SessionLocationDAO dao = new SQLiteSessionLocationDAO(getContext());
302
+ dao.startSession();
303
+ }
304
+
305
+ /** Return all locations stored in the current session (ordered by time). */
306
+ public Collection<BackgroundLocation> getSessionLocations() {
307
+ SessionLocationDAO dao = new SQLiteSessionLocationDAO(getContext());
308
+ return dao.getSessionLocations();
309
+ }
310
+
311
+ /** Clear session table and stop storing. Call when route is finished and sync OK. */
312
+ public void clearSession() {
313
+ SessionLocationDAO dao = new SQLiteSessionLocationDAO(getContext());
314
+ dao.clearSession();
315
+ }
316
+
317
+ /** Number of locations in the current session. */
318
+ public int getSessionLocationsCount() {
319
+ SessionLocationDAO dao = new SQLiteSessionLocationDAO(getContext());
320
+ return dao.getSessionLocationsCount();
321
+ }
322
+
297
323
  public BackgroundLocation getStationaryLocation() {
298
324
  return mStationaryLocation;
299
325
  }
@@ -69,6 +69,8 @@ public class Config implements Parcelable
69
69
  private Integer maxLocations;
70
70
  private LocationTemplate template;
71
71
  private Boolean enableWatchdog;
72
+ private Boolean showTime;
73
+ private Boolean showDistance;
72
74
 
73
75
  public Config () {
74
76
  }
@@ -104,6 +106,8 @@ public class Config implements Parcelable
104
106
  this.httpHeaders = CloneHelper.deepCopy(config.httpHeaders);
105
107
  this.maxLocations = config.maxLocations;
106
108
  this.enableWatchdog = config.enableWatchdog;
109
+ this.showTime = config.showTime;
110
+ this.showDistance = config.showDistance;
107
111
  if (config.template instanceof AbstractLocationTemplate) {
108
112
  this.template = ((AbstractLocationTemplate)config.template).clone();
109
113
  }
@@ -138,6 +142,8 @@ public class Config implements Parcelable
138
142
  setSyncEnabled((Boolean) in.readValue(null));
139
143
  setMaxLocations(in.readInt());
140
144
  setEnableWatchdog((Boolean) in.readValue(null));
145
+ setShowTime((Boolean) in.readValue(null));
146
+ setShowDistance((Boolean) in.readValue(null));
141
147
  Bundle bundle = in.readBundle();
142
148
  setHttpHeaders((HashMap<String, String>) bundle.getSerializable("httpHeaders"));
143
149
  setTemplate((LocationTemplate) bundle.getSerializable(AbstractLocationTemplate.BUNDLE_KEY));
@@ -175,6 +181,8 @@ public class Config implements Parcelable
175
181
  config.maxLocations = 10000;
176
182
  config.template = null;
177
183
  config.enableWatchdog = false;
184
+ config.showTime = false;
185
+ config.showDistance = false;
178
186
 
179
187
  return config;
180
188
  }
@@ -213,6 +221,8 @@ public class Config implements Parcelable
213
221
  out.writeValue(getSyncEnabled());
214
222
  out.writeInt(getMaxLocations());
215
223
  out.writeValue(getEnableWatchdog());
224
+ out.writeValue(getShowTime());
225
+ out.writeValue(getShowDistance());
216
226
  Bundle bundle = new Bundle();
217
227
  bundle.putSerializable("httpHeaders", getHttpHeaders());
218
228
  bundle.putSerializable(AbstractLocationTemplate.BUNDLE_KEY, (AbstractLocationTemplate) getTemplate());
@@ -609,6 +619,32 @@ public class Config implements Parcelable
609
619
  this.enableWatchdog = enableWatchdog;
610
620
  }
611
621
 
622
+ public boolean hasShowTime() {
623
+ return showTime != null;
624
+ }
625
+
626
+ @Nullable
627
+ public Boolean getShowTime() {
628
+ return showTime;
629
+ }
630
+
631
+ public void setShowTime(Boolean showTime) {
632
+ this.showTime = showTime;
633
+ }
634
+
635
+ public boolean hasShowDistance() {
636
+ return showDistance != null;
637
+ }
638
+
639
+ @Nullable
640
+ public Boolean getShowDistance() {
641
+ return showDistance;
642
+ }
643
+
644
+ public void setShowDistance(Boolean showDistance) {
645
+ this.showDistance = showDistance;
646
+ }
647
+
612
648
  @Override
613
649
  public String toString () {
614
650
  return new StringBuffer()
@@ -637,6 +673,8 @@ public class Config implements Parcelable
637
673
  .append(" httpHeaders=").append(getHttpHeaders().toString())
638
674
  .append(" maxLocations=").append(getMaxLocations())
639
675
  .append(" postTemplate=").append(hasTemplate() ? getTemplate().toString() : null)
676
+ .append(" showTime=").append(getShowTime())
677
+ .append(" showDistance=").append(getShowDistance())
640
678
  .append("]")
641
679
  .toString();
642
680
  }
@@ -744,6 +782,12 @@ public class Config implements Parcelable
744
782
  if (config2.hasTemplate()) {
745
783
  merger.setTemplate(config2.getTemplate());
746
784
  }
785
+ if (config2.hasShowTime()) {
786
+ merger.setShowTime(config2.getShowTime());
787
+ }
788
+ if (config2.hasShowDistance()) {
789
+ merger.setShowDistance(config2.getShowDistance());
790
+ }
747
791
 
748
792
  return merger;
749
793
  }
@@ -2,6 +2,7 @@ package com.marianhello.bgloc;
2
2
 
3
3
  import com.marianhello.bgloc.data.BackgroundLocation;
4
4
  import com.marianhello.bgloc.data.LocationDAO;
5
+ import com.marianhello.bgloc.data.SessionLocationDAO;
5
6
  import com.marianhello.logging.LoggerManager;
6
7
 
7
8
  import org.json.JSONArray;
@@ -29,6 +30,7 @@ import java.util.concurrent.TimeUnit;
29
30
  */
30
31
  public class PostLocationTask {
31
32
  private final LocationDAO mLocationDAO;
33
+ private final SessionLocationDAO mSessionDAO;
32
34
  private final PostLocationTaskListener mTaskListener;
33
35
  private final ConnectivityListener mConnectivityListener;
34
36
 
@@ -48,10 +50,17 @@ public class PostLocationTask {
48
50
 
49
51
  public PostLocationTask(LocationDAO dao, PostLocationTaskListener taskListener,
50
52
  ConnectivityListener connectivityListener) {
53
+ this(dao, null, taskListener, connectivityListener);
54
+ }
55
+
56
+ public PostLocationTask(LocationDAO dao, SessionLocationDAO sessionDAO,
57
+ PostLocationTaskListener taskListener,
58
+ ConnectivityListener connectivityListener) {
51
59
  logger = LoggerManager.getLogger(PostLocationTask.class);
52
60
  logger.info("Creating PostLocationTask");
53
61
 
54
62
  mLocationDAO = dao;
63
+ mSessionDAO = sessionDAO;
55
64
  mTaskListener = taskListener;
56
65
  mConnectivityListener = connectivityListener;
57
66
 
@@ -84,6 +93,10 @@ public class PostLocationTask {
84
93
  long locationId = mLocationDAO.persistLocation(location);
85
94
  location.setLocationId(locationId);
86
95
 
96
+ if (mSessionDAO != null && mSessionDAO.isSessionActive()) {
97
+ mSessionDAO.persistSessionLocation(location);
98
+ }
99
+
87
100
  try {
88
101
  mExecutor.execute(new Runnable() {
89
102
  @Override
@@ -0,0 +1,18 @@
1
+ package com.marianhello.bgloc.data;
2
+
3
+ import java.util.Collection;
4
+
5
+ /**
6
+ * DAO for the current recording session locations.
7
+ * Session is independent of sync: locations are kept until startSession() or clearSession().
8
+ */
9
+ public interface SessionLocationDAO {
10
+ /** Clear session table and set session active. Call when user starts a route. */
11
+ void startSession();
12
+ /** Clear session table and set session inactive. Call when route is finished and sync OK. */
13
+ void clearSession();
14
+ boolean isSessionActive();
15
+ void persistSessionLocation(BackgroundLocation location);
16
+ Collection<BackgroundLocation> getSessionLocations();
17
+ int getSessionLocationsCount();
18
+ }
@@ -45,6 +45,8 @@ public final class SQLiteConfigurationContract {
45
45
  public static final String COLUMN_NAME_HEADERS = "http_headers";
46
46
  public static final String COLUMN_NAME_MAX_LOCATIONS = "max_locations";
47
47
  public static final String COLUMN_NAME_TEMPLATE = "template";
48
+ public static final String COLUMN_NAME_SHOW_TIME = "show_time";
49
+ public static final String COLUMN_NAME_SHOW_DISTANCE = "show_distance";
48
50
 
49
51
  public static final String SQL_CREATE_CONFIG_TABLE =
50
52
  "CREATE TABLE " + ConfigurationEntry.TABLE_NAME + " (" +
@@ -77,7 +79,9 @@ public final class SQLiteConfigurationContract {
77
79
  ConfigurationEntry.COLUMN_NAME_SYNC_ENABLED + INTEGER_TYPE + COMMA_SEP +
78
80
  ConfigurationEntry.COLUMN_NAME_HEADERS + TEXT_TYPE + COMMA_SEP +
79
81
  ConfigurationEntry.COLUMN_NAME_MAX_LOCATIONS + INTEGER_TYPE + COMMA_SEP +
80
- ConfigurationEntry.COLUMN_NAME_TEMPLATE + TEXT_TYPE +
82
+ ConfigurationEntry.COLUMN_NAME_TEMPLATE + TEXT_TYPE + COMMA_SEP +
83
+ ConfigurationEntry.COLUMN_NAME_SHOW_TIME + INTEGER_TYPE + COMMA_SEP +
84
+ ConfigurationEntry.COLUMN_NAME_SHOW_DISTANCE + INTEGER_TYPE +
81
85
  " )";
82
86
 
83
87
  public static final String SQL_DROP_CONFIG_TABLE =
@@ -61,7 +61,9 @@ public class SQLiteConfigurationDAO implements ConfigurationDAO {
61
61
  ConfigurationEntry.COLUMN_NAME_SYNC_ENABLED,
62
62
  ConfigurationEntry.COLUMN_NAME_HEADERS,
63
63
  ConfigurationEntry.COLUMN_NAME_MAX_LOCATIONS,
64
- ConfigurationEntry.COLUMN_NAME_TEMPLATE
64
+ ConfigurationEntry.COLUMN_NAME_TEMPLATE,
65
+ ConfigurationEntry.COLUMN_NAME_SHOW_TIME,
66
+ ConfigurationEntry.COLUMN_NAME_SHOW_DISTANCE
65
67
  };
66
68
 
67
69
  String whereClause = null;
@@ -142,6 +144,14 @@ public class SQLiteConfigurationDAO implements ConfigurationDAO {
142
144
  config.setHttpHeaders(new JSONObject(c.getString(c.getColumnIndex(ConfigurationEntry.COLUMN_NAME_HEADERS))));
143
145
  config.setMaxLocations(c.getInt(c.getColumnIndex(ConfigurationEntry.COLUMN_NAME_MAX_LOCATIONS)));
144
146
  config.setTemplate(LocationTemplateFactory.fromJSONString(c.getString(c.getColumnIndex(ConfigurationEntry.COLUMN_NAME_TEMPLATE))));
147
+ int idxShowTime = c.getColumnIndex(ConfigurationEntry.COLUMN_NAME_SHOW_TIME);
148
+ if (idxShowTime >= 0 && !c.isNull(idxShowTime)) {
149
+ config.setShowTime(c.getInt(idxShowTime) == 1);
150
+ }
151
+ int idxShowDistance = c.getColumnIndex(ConfigurationEntry.COLUMN_NAME_SHOW_DISTANCE);
152
+ if (idxShowDistance >= 0 && !c.isNull(idxShowDistance)) {
153
+ config.setShowDistance(c.getInt(idxShowDistance) == 1);
154
+ }
145
155
 
146
156
  return config;
147
157
  }
@@ -178,6 +188,8 @@ public class SQLiteConfigurationDAO implements ConfigurationDAO {
178
188
  values.put(ConfigurationEntry.COLUMN_NAME_HEADERS, new JSONObject(config.getHttpHeaders()).toString());
179
189
  values.put(ConfigurationEntry.COLUMN_NAME_MAX_LOCATIONS, config.getMaxLocations());
180
190
  values.put(ConfigurationEntry.COLUMN_NAME_TEMPLATE, config.hasTemplate() ? config.getTemplate().toString() : null);
191
+ values.put(ConfigurationEntry.COLUMN_NAME_SHOW_TIME, Boolean.TRUE.equals(config.getShowTime()) ? 1 : 0);
192
+ values.put(ConfigurationEntry.COLUMN_NAME_SHOW_DISTANCE, Boolean.TRUE.equals(config.getShowDistance()) ? 1 : 0);
181
193
 
182
194
  return values;
183
195
  }
@@ -8,6 +8,7 @@ import android.util.Log;
8
8
 
9
9
  import com.marianhello.bgloc.data.sqlite.SQLiteLocationContract.LocationEntry;
10
10
  import com.marianhello.bgloc.data.sqlite.SQLiteConfigurationContract.ConfigurationEntry;
11
+ import com.marianhello.bgloc.data.sqlite.SQLiteSessionContract.SessionEntry;
11
12
 
12
13
  import java.util.ArrayList;
13
14
 
@@ -21,7 +22,7 @@ import static com.marianhello.bgloc.data.sqlite.SQLiteLocationContract.LocationE
21
22
  public class SQLiteOpenHelper extends android.database.sqlite.SQLiteOpenHelper {
22
23
  private static final String TAG = SQLiteOpenHelper.class.getName();
23
24
  public static final String SQLITE_DATABASE_NAME = "cordova_bg_geolocation.db";
24
- public static final int DATABASE_VERSION = 18;
25
+ public static final int DATABASE_VERSION = 20;
25
26
 
26
27
  public static final String TEXT_TYPE = " TEXT";
27
28
  public static final String INTEGER_TYPE = " INTEGER";
@@ -66,6 +67,8 @@ public class SQLiteOpenHelper extends android.database.sqlite.SQLiteOpenHelper {
66
67
  execAndLogSql(db, SQL_CREATE_CONFIG_TABLE);
67
68
  execAndLogSql(db, SQL_CREATE_LOCATION_TABLE_TIME_IDX);
68
69
  execAndLogSql(db, SQL_CREATE_LOCATION_TABLE_BATCH_ID_IDX);
70
+ execAndLogSql(db, SessionEntry.SQL_CREATE_SESSION_TABLE);
71
+ execAndLogSql(db, SessionEntry.SQL_CREATE_SESSION_TABLE_TIME_IDX);
69
72
  }
70
73
 
71
74
  @Override
@@ -132,6 +135,14 @@ public class SQLiteOpenHelper extends android.database.sqlite.SQLiteOpenHelper {
132
135
  case 17:
133
136
  alterSql.add("ALTER TABLE " + ConfigurationEntry.TABLE_NAME +
134
137
  " ADD COLUMN " + ConfigurationEntry.COLUMN_NAME_SYNC_ENABLED + INTEGER_TYPE);
138
+ case 18:
139
+ alterSql.add("ALTER TABLE " + ConfigurationEntry.TABLE_NAME +
140
+ " ADD COLUMN " + ConfigurationEntry.COLUMN_NAME_SHOW_TIME + INTEGER_TYPE);
141
+ alterSql.add("ALTER TABLE " + ConfigurationEntry.TABLE_NAME +
142
+ " ADD COLUMN " + ConfigurationEntry.COLUMN_NAME_SHOW_DISTANCE + INTEGER_TYPE);
143
+ case 19:
144
+ alterSql.add(SessionEntry.SQL_CREATE_SESSION_TABLE);
145
+ alterSql.add(SessionEntry.SQL_CREATE_SESSION_TABLE_TIME_IDX);
135
146
 
136
147
  break; // DO NOT FORGET TO MOVE DOWN BREAK ON DB UPGRADE!!!
137
148
  default:
@@ -149,6 +160,7 @@ public class SQLiteOpenHelper extends android.database.sqlite.SQLiteOpenHelper {
149
160
  // we don't support db downgrade yet, instead we drop table and start over
150
161
  execAndLogSql(db, SQL_DROP_LOCATION_TABLE);
151
162
  execAndLogSql(db, SQL_DROP_CONFIG_TABLE);
163
+ execAndLogSql(db, SessionEntry.SQL_DROP_SESSION_TABLE);
152
164
  onCreate(db);
153
165
  }
154
166