@bricks-soft/capacitor-location-tracking 0.0.0-stage → 8.0.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 (168) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2759 -2
  3. package/android/build.gradle +106 -0
  4. package/android/consumer-rules.pro +42 -0
  5. package/android/src/main/AndroidManifest.xml +52 -0
  6. package/android/src/main/java/com/brickssoft/locationtracking/LocationTrackingPlugin.kt +431 -0
  7. package/android/src/main/java/com/brickssoft/locationtracking/api/LocationTrackingListener.kt +52 -0
  8. package/android/src/main/java/com/brickssoft/locationtracking/api/LocationTrackingNative.kt +267 -0
  9. package/android/src/main/java/com/brickssoft/locationtracking/api/NativeListeners.kt +270 -0
  10. package/android/src/main/java/com/brickssoft/locationtracking/api/NativeThread.kt +57 -0
  11. package/android/src/main/java/com/brickssoft/locationtracking/bridge/BridgeServices.kt +64 -0
  12. package/android/src/main/java/com/brickssoft/locationtracking/bridge/CallResponder.kt +41 -0
  13. package/android/src/main/java/com/brickssoft/locationtracking/bridge/OptionParsers.kt +281 -0
  14. package/android/src/main/java/com/brickssoft/locationtracking/bridge/PluginHandlers.kt +541 -0
  15. package/android/src/main/java/com/brickssoft/locationtracking/bridge/PluginPermissionHost.kt +76 -0
  16. package/android/src/main/java/com/brickssoft/locationtracking/bridge/WatchRegistry.kt +84 -0
  17. package/android/src/main/java/com/brickssoft/locationtracking/config/Config.kt +231 -0
  18. package/android/src/main/java/com/brickssoft/locationtracking/config/ConfigJson.kt +574 -0
  19. package/android/src/main/java/com/brickssoft/locationtracking/config/ConfigStore.kt +31 -0
  20. package/android/src/main/java/com/brickssoft/locationtracking/config/ConfigValidator.kt +170 -0
  21. package/android/src/main/java/com/brickssoft/locationtracking/config/RuntimeStateJson.kt +73 -0
  22. package/android/src/main/java/com/brickssoft/locationtracking/config/SharedPrefsConfigStore.kt +135 -0
  23. package/android/src/main/java/com/brickssoft/locationtracking/config/State.kt +45 -0
  24. package/android/src/main/java/com/brickssoft/locationtracking/core/AppDispatchers.kt +21 -0
  25. package/android/src/main/java/com/brickssoft/locationtracking/core/Clock.kt +41 -0
  26. package/android/src/main/java/com/brickssoft/locationtracking/core/Components.kt +217 -0
  27. package/android/src/main/java/com/brickssoft/locationtracking/core/Constants.kt +78 -0
  28. package/android/src/main/java/com/brickssoft/locationtracking/core/Errors.kt +27 -0
  29. package/android/src/main/java/com/brickssoft/locationtracking/core/EventBus.kt +39 -0
  30. package/android/src/main/java/com/brickssoft/locationtracking/core/ForceStopProbe.kt +63 -0
  31. package/android/src/main/java/com/brickssoft/locationtracking/core/Iso8601.kt +63 -0
  32. package/android/src/main/java/com/brickssoft/locationtracking/core/Logger.kt +68 -0
  33. package/android/src/main/java/com/brickssoft/locationtracking/core/RecordHooks.kt +47 -0
  34. package/android/src/main/java/com/brickssoft/locationtracking/core/TrackingEvent.kt +64 -0
  35. package/android/src/main/java/com/brickssoft/locationtracking/data/SqlSupport.kt +79 -0
  36. package/android/src/main/java/com/brickssoft/locationtracking/data/SqliteGeofenceStore.kt +144 -0
  37. package/android/src/main/java/com/brickssoft/locationtracking/data/SqliteLocationStore.kt +215 -0
  38. package/android/src/main/java/com/brickssoft/locationtracking/data/Stores.kt +45 -0
  39. package/android/src/main/java/com/brickssoft/locationtracking/data/TrackingDatabase.kt +111 -0
  40. package/android/src/main/java/com/brickssoft/locationtracking/device/DefaultDeviceInfoProvider.kt +104 -0
  41. package/android/src/main/java/com/brickssoft/locationtracking/device/DefaultDeviceMonitor.kt +335 -0
  42. package/android/src/main/java/com/brickssoft/locationtracking/device/DeviceMonitor.kt +36 -0
  43. package/android/src/main/java/com/brickssoft/locationtracking/device/DeviceStateMapping.kt +69 -0
  44. package/android/src/main/java/com/brickssoft/locationtracking/engine/DefaultTrackingEngine.kt +1281 -0
  45. package/android/src/main/java/com/brickssoft/locationtracking/engine/ElasticDistance.kt +68 -0
  46. package/android/src/main/java/com/brickssoft/locationtracking/engine/LocationRequests.kt +74 -0
  47. package/android/src/main/java/com/brickssoft/locationtracking/engine/MotionStateMachine.kt +291 -0
  48. package/android/src/main/java/com/brickssoft/locationtracking/engine/StationaryRegion.kt +41 -0
  49. package/android/src/main/java/com/brickssoft/locationtracking/engine/TrackingEngine.kt +60 -0
  50. package/android/src/main/java/com/brickssoft/locationtracking/engine/TrackingTimers.kt +90 -0
  51. package/android/src/main/java/com/brickssoft/locationtracking/geofence/DefaultGeofenceManager.kt +899 -0
  52. package/android/src/main/java/com/brickssoft/locationtracking/geofence/DwellTracker.kt +80 -0
  53. package/android/src/main/java/com/brickssoft/locationtracking/geofence/GeofenceManager.kt +33 -0
  54. package/android/src/main/java/com/brickssoft/locationtracking/geofence/PolygonMath.kt +194 -0
  55. package/android/src/main/java/com/brickssoft/locationtracking/heartbeat/DefaultHeartbeatScheduler.kt +713 -0
  56. package/android/src/main/java/com/brickssoft/locationtracking/heartbeat/HeartbeatAlarmReceiver.kt +69 -0
  57. package/android/src/main/java/com/brickssoft/locationtracking/heartbeat/HeartbeatAlarms.kt +100 -0
  58. package/android/src/main/java/com/brickssoft/locationtracking/heartbeat/HeartbeatScheduler.kt +25 -0
  59. package/android/src/main/java/com/brickssoft/locationtracking/heartbeat/HeartbeatWindow.kt +179 -0
  60. package/android/src/main/java/com/brickssoft/locationtracking/http/AuthorizationManager.kt +213 -0
  61. package/android/src/main/java/com/brickssoft/locationtracking/http/BodyBuilder.kt +73 -0
  62. package/android/src/main/java/com/brickssoft/locationtracking/http/HttpSupport.kt +118 -0
  63. package/android/src/main/java/com/brickssoft/locationtracking/http/HttpSyncer.kt +18 -0
  64. package/android/src/main/java/com/brickssoft/locationtracking/http/OkHttpSyncer.kt +551 -0
  65. package/android/src/main/java/com/brickssoft/locationtracking/http/StrictJson.kt +135 -0
  66. package/android/src/main/java/com/brickssoft/locationtracking/http/SyncPolicy.kt +109 -0
  67. package/android/src/main/java/com/brickssoft/locationtracking/http/TemplateRenderer.kt +143 -0
  68. package/android/src/main/java/com/brickssoft/locationtracking/logging/FileLogger.kt +461 -0
  69. package/android/src/main/java/com/brickssoft/locationtracking/logging/LogFileProvider.kt +9 -0
  70. package/android/src/main/java/com/brickssoft/locationtracking/logging/LogFormatter.kt +148 -0
  71. package/android/src/main/java/com/brickssoft/locationtracking/logging/LogStore.kt +30 -0
  72. package/android/src/main/java/com/brickssoft/locationtracking/model/DeviceModels.kt +176 -0
  73. package/android/src/main/java/com/brickssoft/locationtracking/model/GeofenceSpec.kt +25 -0
  74. package/android/src/main/java/com/brickssoft/locationtracking/model/ModelJson.kt +550 -0
  75. package/android/src/main/java/com/brickssoft/locationtracking/model/Record.kt +106 -0
  76. package/android/src/main/java/com/brickssoft/locationtracking/model/TrackedLocation.kt +53 -0
  77. package/android/src/main/java/com/brickssoft/locationtracking/permission/BackgroundRationaleDialog.kt +85 -0
  78. package/android/src/main/java/com/brickssoft/locationtracking/permission/CurrentActivityTracker.kt +72 -0
  79. package/android/src/main/java/com/brickssoft/locationtracking/permission/DefaultPermissionManager.kt +333 -0
  80. package/android/src/main/java/com/brickssoft/locationtracking/permission/PermissionManager.kt +54 -0
  81. package/android/src/main/java/com/brickssoft/locationtracking/position/DefaultPositionService.kt +277 -0
  82. package/android/src/main/java/com/brickssoft/locationtracking/position/PositionService.kt +39 -0
  83. package/android/src/main/java/com/brickssoft/locationtracking/position/PositionWatch.kt +115 -0
  84. package/android/src/main/java/com/brickssoft/locationtracking/processing/DefaultLocationProcessor.kt +93 -0
  85. package/android/src/main/java/com/brickssoft/locationtracking/processing/DefaultOdometer.kt +99 -0
  86. package/android/src/main/java/com/brickssoft/locationtracking/processing/DefaultRecordFactory.kt +166 -0
  87. package/android/src/main/java/com/brickssoft/locationtracking/processing/Geo.kt +56 -0
  88. package/android/src/main/java/com/brickssoft/locationtracking/processing/KalmanFilter.kt +72 -0
  89. package/android/src/main/java/com/brickssoft/locationtracking/processing/Processing.kt +48 -0
  90. package/android/src/main/java/com/brickssoft/locationtracking/provider/Backends.kt +187 -0
  91. package/android/src/main/java/com/brickssoft/locationtracking/provider/DefaultProviderFactory.kt +169 -0
  92. package/android/src/main/java/com/brickssoft/locationtracking/provider/ProviderPackaging.kt +135 -0
  93. package/android/src/main/java/com/brickssoft/locationtracking/provider/android/AndroidActivityBackend.kt +25 -0
  94. package/android/src/main/java/com/brickssoft/locationtracking/provider/android/AndroidGeofenceBackend.kt +181 -0
  95. package/android/src/main/java/com/brickssoft/locationtracking/provider/android/AndroidGeofenceReceiver.kt +115 -0
  96. package/android/src/main/java/com/brickssoft/locationtracking/provider/android/AndroidLocationBackend.kt +265 -0
  97. package/android/src/main/java/com/brickssoft/locationtracking/provider/android/AndroidProviderBundle.kt +31 -0
  98. package/android/src/main/java/com/brickssoft/locationtracking/provider/android/LocationProviders.kt +98 -0
  99. package/android/src/main/java/com/brickssoft/locationtracking/provider/gms/GmsActivityBackend.kt +80 -0
  100. package/android/src/main/java/com/brickssoft/locationtracking/provider/gms/GmsActivityReceiver.kt +62 -0
  101. package/android/src/main/java/com/brickssoft/locationtracking/provider/gms/GmsErrors.kt +72 -0
  102. package/android/src/main/java/com/brickssoft/locationtracking/provider/gms/GmsGeofenceBackend.kt +152 -0
  103. package/android/src/main/java/com/brickssoft/locationtracking/provider/gms/GmsGeofenceReceiver.kt +78 -0
  104. package/android/src/main/java/com/brickssoft/locationtracking/provider/gms/GmsLocationBackend.kt +176 -0
  105. package/android/src/main/java/com/brickssoft/locationtracking/provider/gms/GmsProviderBundle.kt +62 -0
  106. package/android/src/main/java/com/brickssoft/locationtracking/provider/gms/GmsReceiverSupport.kt +88 -0
  107. package/android/src/main/java/com/brickssoft/locationtracking/provider/gms/GmsTasks.kt +55 -0
  108. package/android/src/main/java/com/brickssoft/locationtracking/provider/hms/HmsActivityBackend.kt +71 -0
  109. package/android/src/main/java/com/brickssoft/locationtracking/provider/hms/HmsActivityReceiver.kt +57 -0
  110. package/android/src/main/java/com/brickssoft/locationtracking/provider/hms/HmsErrors.kt +76 -0
  111. package/android/src/main/java/com/brickssoft/locationtracking/provider/hms/HmsGeofenceBackend.kt +116 -0
  112. package/android/src/main/java/com/brickssoft/locationtracking/provider/hms/HmsGeofenceReceiver.kt +67 -0
  113. package/android/src/main/java/com/brickssoft/locationtracking/provider/hms/HmsIntents.kt +83 -0
  114. package/android/src/main/java/com/brickssoft/locationtracking/provider/hms/HmsLocationBackend.kt +197 -0
  115. package/android/src/main/java/com/brickssoft/locationtracking/provider/hms/HmsProviderBundle.kt +55 -0
  116. package/android/src/main/java/com/brickssoft/locationtracking/provider/hms/HmsTasks.kt +44 -0
  117. package/android/src/main/java/com/brickssoft/locationtracking/record/RecordSink.kt +95 -0
  118. package/android/src/main/java/com/brickssoft/locationtracking/service/BootCountStore.kt +44 -0
  119. package/android/src/main/java/com/brickssoft/locationtracking/service/BootReceiver.kt +175 -0
  120. package/android/src/main/java/com/brickssoft/locationtracking/service/DefaultServiceController.kt +196 -0
  121. package/android/src/main/java/com/brickssoft/locationtracking/service/LocationTrackingService.kt +319 -0
  122. package/android/src/main/java/com/brickssoft/locationtracking/service/NotificationActionReceiver.kt +42 -0
  123. package/android/src/main/java/com/brickssoft/locationtracking/service/NotificationFactory.kt +338 -0
  124. package/android/src/main/java/com/brickssoft/locationtracking/service/ServiceCommands.kt +204 -0
  125. package/android/src/main/java/com/brickssoft/locationtracking/service/ServiceController.kt +22 -0
  126. package/android/src/main/java/com/brickssoft/locationtracking/service/ServiceDeps.kt +61 -0
  127. package/android/src/main/java/com/brickssoft/locationtracking/service/WhileInUseStartCheck.kt +115 -0
  128. package/android/src/main/java/com/brickssoft/locationtracking/settings/DefaultDeviceSettings.kt +79 -0
  129. package/android/src/main/java/com/brickssoft/locationtracking/settings/DeviceSettings.kt +21 -0
  130. package/android/src/main/java/com/brickssoft/locationtracking/settings/OemPowerManagers.kt +198 -0
  131. package/android/src/main/res/drawable/lt_ic_notification.xml +11 -0
  132. package/android/src/main/res/values/lt_strings.xml +11 -0
  133. package/android/src/main/res/xml/lt_file_paths.xml +5 -0
  134. package/dist/esm/definitions.d.ts +719 -0
  135. package/dist/esm/definitions.js +2 -0
  136. package/dist/esm/definitions.js.map +1 -0
  137. package/dist/esm/events.d.ts +50 -0
  138. package/dist/esm/events.js +52 -0
  139. package/dist/esm/events.js.map +1 -0
  140. package/dist/esm/index.d.ts +3 -0
  141. package/dist/esm/index.js +5 -0
  142. package/dist/esm/index.js.map +1 -0
  143. package/dist/esm/plugin.d.ts +3 -0
  144. package/dist/esm/plugin.js +10 -0
  145. package/dist/esm/plugin.js.map +1 -0
  146. package/dist/esm/web/config.d.ts +19 -0
  147. package/dist/esm/web/config.js +243 -0
  148. package/dist/esm/web/config.js.map +1 -0
  149. package/dist/esm/web/device.d.ts +9 -0
  150. package/dist/esm/web/device.js +90 -0
  151. package/dist/esm/web/device.js.map +1 -0
  152. package/dist/esm/web/errors.d.ts +14 -0
  153. package/dist/esm/web/errors.js +19 -0
  154. package/dist/esm/web/errors.js.map +1 -0
  155. package/dist/esm/web/geolocation.d.ts +26 -0
  156. package/dist/esm/web/geolocation.js +117 -0
  157. package/dist/esm/web/geolocation.js.map +1 -0
  158. package/dist/esm/web/location.d.ts +7 -0
  159. package/dist/esm/web/location.js +70 -0
  160. package/dist/esm/web/location.js.map +1 -0
  161. package/dist/esm/web.d.ts +164 -0
  162. package/dist/esm/web.js +328 -0
  163. package/dist/esm/web.js.map +1 -0
  164. package/dist/plugin.cjs.js +945 -0
  165. package/dist/plugin.cjs.js.map +1 -0
  166. package/dist/plugin.js +948 -0
  167. package/dist/plugin.js.map +1 -0
  168. package/package.json +59 -4
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PermissionState, PluginListenerHandle } from '@capacitor/core';\n\nexport type CallbackID = string;\nexport type DesiredAccuracy = 'high' | 'balanced' | 'low' | 'passive';\nexport type LocationProviderSetting = 'auto' | 'gms' | 'hms' | 'android';\nexport type LocationBackend = 'gms' | 'hms' | 'android' | 'web';\nexport type TrackingMode = 'location' | 'geofences';\nexport type LogLevel = 'off' | 'error' | 'warn' | 'info' | 'debug' | 'verbose';\nexport type ActivityType = 'still' | 'on_foot' | 'walking' | 'running' | 'on_bicycle' | 'in_vehicle' | 'unknown';\nexport type RecordEvent =\n | 'location'\n | 'motionchange'\n | 'current_position'\n | 'watch_position'\n | 'heartbeat'\n | 'geofence'\n | 'tracking_start'\n | 'tracking_stop'\n | 'providerchange';\nexport type GeofenceAction = 'ENTER' | 'EXIT' | 'DWELL';\nexport type HttpMethod = 'POST' | 'PUT' | 'PATCH';\nexport type NotificationPriority = 'min' | 'low' | 'default' | 'high' | 'max';\nexport type PermissionType = 'location' | 'backgroundLocation' | 'activityRecognition' | 'notifications';\nexport type ConnectivityType = 'wifi' | 'cellular' | 'ethernet' | 'other' | 'none';\nexport type HeartbeatStrategy = 'exact' | 'listener_with_backup' | 'idle_paced' | 'disabled';\n/** Rejection `code` of every failed promise. */\nexport type ErrorCode =\n | 'NOT_READY'\n | 'PERMISSION_DENIED'\n | 'LOCATION_DISABLED'\n | 'TIMEOUT'\n | 'UNAVAILABLE'\n | 'INVALID_ARGUMENT'\n | 'NOT_FOUND'\n | 'NO_URL'\n | 'HTTP_ERROR'\n | 'NETWORK_ERROR'\n | 'TOO_MANY_GEOFENCES'\n | 'NO_ACTIVITY'\n | 'IO_ERROR'\n | 'UNIMPLEMENTED'\n | 'INTERNAL';\n\n/* ================= CONFIG (all optional; defaults shown) ================= */\n\nexport interface LocationFilterConfig {\n /** @default false */\n useKalman?: boolean;\n /**\n * Reject fixes with accuracy worse than this (m).\n * @default 100\n */\n trackingAccuracyThreshold?: number;\n /**\n * Reject fixes implying speed above this (m/s); 0 = off.\n * @default 80\n */\n maxImpliedSpeed?: number;\n /**\n * Odometer ignores fixes worse than this (m).\n * @default 20\n */\n odometerAccuracyThreshold?: number;\n /** @default false */\n allowIdenticalLocations?: boolean;\n /**\n * Drop mock fixes entirely (otherwise they are kept with mock:true).\n * @default false\n */\n rejectMockLocations?: boolean;\n}\n\nexport interface GeolocationConfig {\n /** @default 'high' */\n desiredAccuracy?: DesiredAccuracy;\n /**\n * meters\n * @default 10\n */\n distanceFilter?: number;\n /**\n * ms\n * @default 1000\n */\n locationUpdateInterval?: number;\n /**\n * ms\n * @default 500\n */\n fastestLocationUpdateInterval?: number;\n /** @default false */\n disableElasticity?: boolean;\n /** @default 1 */\n elasticityMultiplier?: number;\n /**\n * meters\n * @default 25\n */\n stationaryRadius?: number;\n /**\n * minutes\n * @default 5\n */\n stopTimeout?: number;\n /**\n * minutes, 0 = off\n * @default 0\n */\n stopAfterElapsedMinutes?: number;\n /** @default false */\n stopOnStationary?: boolean;\n /**\n * default getCurrentPosition timeout, ms\n * @default 30000\n */\n locationTimeout?: number;\n filter?: LocationFilterConfig;\n}\n\nexport interface ActivityConfig {\n /** @default false */\n disableMotionActivityUpdates?: boolean;\n /**\n * ms\n * @default 10000\n */\n activityRecognitionInterval?: number;\n /**\n * 0-100\n * @default 75\n */\n minimumActivityRecognitionConfidence?: number;\n /**\n * ms\n * @default 0\n */\n motionTriggerDelay?: number;\n /** @default false */\n disableStopDetection?: boolean;\n}\n\nexport interface HeartbeatConfig {\n /** @default true */\n enabled?: boolean;\n /**\n * seconds, min 60\n * @default 180\n */\n minInterval?: number;\n /**\n * seconds, >= minInterval\n * @default 300\n */\n maxInterval?: number;\n}\n\nexport interface AuthorizationConfig {\n /** @default 'JWT' */\n strategy?: 'JWT';\n accessToken?: string;\n refreshToken?: string;\n refreshUrl?: string;\n /**\n * String values may contain `{refreshToken}`.\n * @default {}\n */\n refreshPayload?: Record<string, string>;\n refreshHeaders?: Record<string, string>;\n /** @default 'json' */\n refreshPayloadEncoding?: 'json' | 'form';\n /**\n * access-token expiry, epoch ms; -1 = unknown\n * @default -1\n */\n expires?: number;\n}\n\nexport interface HttpConfig {\n /** No url => nothing is uploaded (records stay queued). */\n url?: string | null;\n /** @default 'POST' */\n method?: HttpMethod;\n /** @default {} */\n headers?: Record<string, string>;\n /**\n * merged into the ROOT of every request body\n * @default {}\n */\n params?: Record<string, unknown>;\n /** @default true */\n autoSync?: boolean;\n /**\n * upload when queue >= threshold; 0 = every record\n * @default 0\n */\n autoSyncThreshold?: number;\n /**\n * s; upload normal records once the oldest queued one is this old; 0 = off\n * @default 0\n */\n syncInterval?: number;\n /** @default false */\n batchSync?: boolean;\n /** @default 100 */\n maxBatchSize?: number;\n /** @default false */\n disableAutoSyncOnCellular?: boolean;\n /**\n * '.' = no wrapping\n * @default 'location'\n */\n rootProperty?: string;\n /** JSON text with `<%= name %>` placeholders */\n locationTemplate?: string | null;\n /** used for event 'geofence'; falls back to locationTemplate */\n geofenceTemplate?: string | null;\n /**\n * ms\n * @default 60000\n */\n timeout?: number;\n authorization?: AuthorizationConfig | null;\n}\n\nexport interface PersistenceConfig {\n /** @default 7 */\n maxDaysToPersist?: number;\n /**\n * -1 = unlimited\n * @default -1\n */\n maxRecordsToPersist?: number;\n /**\n * merged into `extras` of every record at creation time\n * @default {}\n */\n extras?: Record<string, unknown>;\n}\n\nexport interface AppConfig {\n /** @default true */\n stopOnTerminate?: boolean;\n /** @default false */\n startOnBoot?: boolean;\n}\n\nexport interface NotificationActionButton {\n id: string;\n label: string;\n}\n\nexport interface NotificationConfig {\n /** @default app label */\n title?: string;\n /** @default 'Location tracking is active' */\n text?: string;\n /**\n * 'drawable/name' | 'mipmap/name'\n * @default plugin icon 'drawable/lt_ic_notification'\n */\n smallIcon?: string;\n largeIcon?: string;\n /** '#RRGGBB' */\n color?: string;\n /** @default 'default' */\n priority?: NotificationPriority;\n /** @default 'location_tracking' */\n channelId?: string;\n /** @default 'Location tracking' */\n channelName?: string;\n /** max 3; tap => 'notificationaction' event */\n actions?: NotificationActionButton[];\n /** notification that resumes tracking when Android refuses to restore it from the background (Android 14+) */\n resume?: ResumeNotificationConfig;\n}\n\n/**\n * Posted when Android refuses to restore tracking from the background (after a reboot, an app update or a process\n * restart), typically on Android 14+ without \"Allow all the time\". A tap resumes the session\n * (`tracking_start` reason `resume_notification`). Uses the tracking notification's small icon and color.\n */\nexport interface ResumeNotificationConfig {\n /** @default false */\n enabled?: boolean;\n /** @default app label */\n title?: string;\n /** @default 'Location tracking is paused. Tap to resume.' */\n text?: string;\n /** @default 'Paused location tracking' */\n channelName?: string;\n}\n\nexport interface GeofenceConfig {\n /** @default true */\n initialTriggerEntry?: boolean;\n}\n\nexport interface LoggerConfig {\n /** @default 'info' */\n logLevel?: LogLevel;\n /** @default 3 */\n logMaxDays?: number;\n}\n\nexport interface BackgroundPermissionRationale {\n /** defaults from plugin string resources */\n title?: string;\n message?: string;\n positiveAction?: string;\n negativeAction?: string;\n}\n\nexport interface Config {\n geolocation?: GeolocationConfig;\n activity?: ActivityConfig;\n heartbeat?: HeartbeatConfig;\n http?: HttpConfig;\n persistence?: PersistenceConfig;\n app?: AppConfig;\n notification?: NotificationConfig;\n geofence?: GeofenceConfig;\n logger?: LoggerConfig;\n backgroundPermissionRationale?: BackgroundPermissionRationale;\n /**\n * Android only\n * @default 'auto'\n */\n locationProvider?: LocationProviderSetting;\n}\n\nexport interface ReadyOptions {\n config?: Config;\n /**\n * true: config = defaults + given config (every launch). false: given config applied only on the very first\n * ready(); afterwards persisted config wins.\n * @default true\n */\n reset?: boolean;\n}\n\nexport interface State {\n enabled: boolean;\n trackingMode: TrackingMode;\n isMoving: boolean;\n odometer: number;\n backend: LocationBackend;\n lastRecordAt: string | null;\n /** fully populated with defaults */\n config: Config;\n}\n\n/* ================= RECORDS (snake_case = identical to wire format) ================= */\n\nexport interface Coords {\n latitude: number;\n longitude: number;\n accuracy: number;\n altitude: number | null;\n altitude_accuracy: number | null;\n speed: number | null;\n speed_accuracy: number | null;\n heading: number | null;\n heading_accuracy: number | null;\n}\n\nexport interface ProviderState {\n enabled: boolean;\n gps: boolean;\n network: boolean;\n permission: 'always' | 'when_in_use' | 'denied';\n accuracy: 'precise' | 'approximate' | 'none';\n backend: LocationBackend;\n}\n\nexport interface Location {\n uuid: string;\n event: RecordEvent;\n /** fix time (ISO-8601 UTC ms); null if no location ever known */\n timestamp: string | null;\n /** record creation time */\n recorded_at: string;\n /** only present in HTTP bodies */\n sent_at?: string;\n elapsed_realtime_ms: number;\n /** -1 if unavailable */\n boot_count: number;\n is_moving: boolean;\n odometer: number;\n mock: boolean;\n coords: Coords | null;\n activity: { type: ActivityType; confidence: number };\n /** level 0..1, -1 unknown */\n battery: { level: number; is_charging: boolean };\n backend: LocationBackend | null;\n extras?: Record<string, unknown>;\n /** event 'geofence' only */\n geofence?: { identifier: string; action: GeofenceAction; extras?: Record<string, unknown> };\n /** provider state when the record was created; the new state for event 'providerchange' */\n provider?: ProviderState;\n /**\n * tracking_start: start|start_geofences|boot|restore|package_replaced|resume_notification;\n * tracking_stop: stop|stop_on_stationary|stop_after_elapsed|terminate|permission_denied|service_start_failed|reboot|package_replaced\n */\n reason?: string;\n /** event 'heartbeat' only, optional: how the heartbeat is scheduled */\n heartbeat?: HeartbeatMeta;\n}\n\n/** Scheduling metadata of a `heartbeat` record (snake_case = wire format). */\nexport interface HeartbeatMeta {\n strategy: 'exact' | 'listener_with_backup' | 'idle_paced';\n /** heartbeat.minInterval (s) when the heartbeat was created */\n min_interval: number;\n /** heartbeat.maxInterval (s) when the heartbeat was created */\n max_interval: number;\n /** when the next heartbeat is expected (ISO-8601 UTC ms); null if unknown */\n next_at: string | null;\n /** the app is exempt from battery optimization */\n battery_exempt: boolean;\n /** the device was in deep Doze */\n device_idle: boolean;\n}\n\nexport type LocationRecord = Location;\n\nexport interface InsertLocationInput {\n coords: Pick<Coords, 'latitude' | 'longitude'> & Partial<Coords>;\n timestamp?: string;\n event?: RecordEvent;\n is_moving?: boolean;\n extras?: Record<string, unknown>;\n}\n\n/* ================= GEOFENCES ================= */\n\n/**\n * Either (latitude, longitude, radius) = circle, or vertices = polygon. For polygons, getGeofences() also returns\n * the computed enclosing circle.\n */\nexport interface Geofence {\n identifier: string;\n latitude?: number;\n longitude?: number;\n /** m */\n radius?: number;\n /** [[lat,lng], ...] >= 3 points */\n vertices?: [number, number][];\n /** @default true */\n notifyOnEntry?: boolean;\n /** @default true */\n notifyOnExit?: boolean;\n /** @default false */\n notifyOnDwell?: boolean;\n /**\n * ms\n * @default 30000\n */\n loiteringDelay?: number;\n extras?: Record<string, unknown>;\n}\n\n/* ================= EVENTS ================= */\n\nexport interface MotionChangeEvent {\n isMoving: boolean;\n location: Location;\n}\nexport interface ActivityChangeEvent {\n activity: ActivityType;\n confidence: number;\n}\nexport interface HeartbeatEvent {\n location: Location;\n}\nexport interface GeofenceEvent {\n identifier: string;\n action: GeofenceAction;\n location: Location;\n extras?: Record<string, unknown>;\n}\nexport interface GeofencesChangeEvent {\n on: Geofence[];\n off: string[];\n}\nexport interface HttpEvent {\n success: boolean;\n status: number;\n responseText: string;\n uuids: string[];\n}\nexport interface ConnectivityChangeEvent {\n connected: boolean;\n type: ConnectivityType;\n}\nexport interface PowerSaveChangeEvent {\n isPowerSaveMode: boolean;\n}\nexport interface EnabledChangeEvent {\n enabled: boolean;\n}\nexport interface NotificationActionEvent {\n id: string;\n}\nexport interface AuthorizationEvent {\n success: boolean;\n status: number;\n error?: string;\n response?: Record<string, unknown>;\n}\nexport interface LocationTrackingEventMap {\n location: Location;\n motionchange: MotionChangeEvent;\n activitychange: ActivityChangeEvent;\n providerchange: ProviderState;\n heartbeat: HeartbeatEvent;\n geofence: GeofenceEvent;\n geofenceschange: GeofencesChangeEvent;\n http: HttpEvent;\n connectivitychange: ConnectivityChangeEvent;\n powersavechange: PowerSaveChangeEvent;\n enabledchange: EnabledChangeEvent;\n notificationaction: NotificationActionEvent;\n authorization: AuthorizationEvent;\n}\nexport type LocationTrackingEventName = keyof LocationTrackingEventMap;\n\n/* ================= MISC ================= */\n\nexport interface CurrentPositionOptions {\n /** @default 3 */\n samples?: number;\n /**\n * ms\n * @default geolocation.locationTimeout\n */\n timeout?: number;\n /**\n * ms\n * @default 0\n */\n maximumAge?: number;\n /** @default 'high' */\n desiredAccuracy?: DesiredAccuracy;\n /** @default true */\n persist?: boolean;\n extras?: Record<string, unknown>;\n}\n\nexport interface WatchPositionOptions {\n /**\n * ms\n * @default 1000\n */\n interval?: number;\n /** @default 'high' */\n desiredAccuracy?: DesiredAccuracy;\n /** @default false */\n persist?: boolean;\n extras?: Record<string, unknown>;\n}\n\nexport type WatchPositionCallback = (location: Location | null, error?: { code: ErrorCode; message: string }) => void;\n\nexport interface HeartbeatStatus {\n enabled: boolean;\n minInterval: number;\n maxInterval: number;\n lastRecordAt: string | null;\n lastHeartbeatAt: string | null;\n nextHeartbeatAt: string | null;\n strategy: HeartbeatStrategy;\n canScheduleExactAlarms: boolean;\n isIgnoringBatteryOptimizations: boolean;\n isDeviceIdleMode: boolean;\n isPowerSaveMode: boolean;\n pendingHeartbeats: number;\n}\n\nexport interface BatteryOptimizationStatus {\n isIgnoringBatteryOptimizations: boolean;\n canScheduleExactAlarms: boolean;\n isDeviceIdleMode: boolean;\n}\n\nexport interface PowerManagerInfo {\n manufacturer: string;\n available: boolean;\n}\n\nexport interface DeviceInfo {\n platform: 'android' | 'web';\n manufacturer: string;\n model: string;\n brand: string;\n osVersion: string;\n sdkInt: number;\n pluginVersion: string;\n gmsAvailable: boolean;\n hmsAvailable: boolean;\n backend: LocationBackend;\n packagedProviders: string[];\n}\n\nexport interface Sensors {\n accelerometer: boolean;\n gyroscope: boolean;\n magnetometer: boolean;\n significantMotion: boolean;\n stepCounter: boolean;\n stepDetector: boolean;\n barometer: boolean;\n}\n\nexport interface PermissionStatus {\n location: PermissionState;\n backgroundLocation: PermissionState;\n activityRecognition: PermissionState;\n notifications: PermissionState;\n}\n\nexport interface LogQuery {\n start?: number;\n end?: number;\n level?: LogLevel;\n limit?: number;\n order?: 'asc' | 'desc';\n}\n\nexport interface LocationTrackingPlugin {\n /** Loads persisted config and state. Must resolve before most other methods (see NOT_READY rule). */\n ready(options?: ReadyOptions): Promise<State>;\n /** deep merge; arrays replaced; null resets key to default */\n setConfig(options: { config: Config }): Promise<State>;\n /** Resets the config to defaults, then applies the given config. */\n reset(options?: { config?: Config }): Promise<State>;\n getState(): Promise<State>;\n /** Starts location tracking (mode 'location'). */\n start(): Promise<State>;\n /** Starts geofence-only tracking (mode 'geofences'). */\n startGeofences(): Promise<State>;\n stop(): Promise<State>;\n /** Forces the motion state to moving or stationary. */\n changePace(options: { isMoving: boolean }): Promise<void>;\n\n getCurrentPosition(options?: CurrentPositionOptions): Promise<Location>;\n watchPosition(options: WatchPositionOptions, callback: WatchPositionCallback): Promise<CallbackID>;\n clearWatch(options: { id: CallbackID }): Promise<void>;\n\n getOdometer(): Promise<{ odometer: number }>;\n setOdometer(options: { odometer: number }): Promise<{ odometer: number }>;\n resetOdometer(): Promise<{ odometer: number }>;\n\n getLocations(options?: { limit?: number }): Promise<{ locations: Location[] }>;\n getCount(): Promise<{ count: number }>;\n insertLocation(options: { location: InsertLocationInput }): Promise<{ uuid: string }>;\n destroyLocations(): Promise<{ count: number }>;\n destroyLocation(options: { uuid: string }): Promise<{ deleted: boolean }>;\n /** uploads whole queue now; resolves with uploaded records */\n sync(): Promise<{ locations: Location[] }>;\n\n addGeofence(options: { geofence: Geofence }): Promise<void>;\n addGeofences(options: { geofences: Geofence[] }): Promise<void>;\n removeGeofence(options: { identifier: string }): Promise<void>;\n /** identifiers omitted (or null) = remove all; an empty array removes nothing */\n removeGeofences(options?: { identifiers?: string[] }): Promise<void>;\n getGeofences(): Promise<{ geofences: Geofence[] }>;\n getGeofence(options: { identifier: string }): Promise<{ geofence: Geofence | null }>;\n geofenceExists(options: { identifier: string }): Promise<{ exists: boolean }>;\n\n getHeartbeatStatus(): Promise<HeartbeatStatus>;\n\n getProviderState(): Promise<ProviderState>;\n isPowerSaveMode(): Promise<{ isPowerSaveMode: boolean }>;\n getBatteryOptimizationStatus(): Promise<BatteryOptimizationStatus>;\n openBatteryOptimizationSettings(): Promise<{ opened: boolean }>;\n getPowerManagerInfo(): Promise<PowerManagerInfo>;\n openPowerManagerSettings(): Promise<{ opened: boolean }>;\n openLocationSettings(): Promise<{ opened: boolean }>;\n openAppSettings(): Promise<{ opened: boolean }>;\n getDeviceInfo(): Promise<DeviceInfo>;\n getSensors(): Promise<Sensors>;\n\n checkPermissions(): Promise<PermissionStatus>;\n /** default all four, order: location, notifications, activityRecognition, backgroundLocation */\n requestPermissions(options?: { permissions?: PermissionType[] }): Promise<PermissionStatus>;\n\n log(options: { level: Exclude<LogLevel, 'off'>; message: string }): Promise<void>;\n getLog(options?: LogQuery): Promise<{ log: string }>;\n destroyLog(): Promise<void>;\n uploadLog(options: {\n url: string;\n headers?: Record<string, string>;\n params?: Record<string, unknown>;\n }): Promise<{ success: boolean; status: number }>;\n emailLog(options: { email: string; subject?: string }): Promise<void>;\n\n addListener(eventName: 'location', listenerFunc: (e: Location) => void): Promise<PluginListenerHandle>;\n addListener(eventName: 'motionchange', listenerFunc: (e: MotionChangeEvent) => void): Promise<PluginListenerHandle>;\n addListener(\n eventName: 'activitychange',\n listenerFunc: (e: ActivityChangeEvent) => void,\n ): Promise<PluginListenerHandle>;\n addListener(eventName: 'providerchange', listenerFunc: (e: ProviderState) => void): Promise<PluginListenerHandle>;\n addListener(eventName: 'heartbeat', listenerFunc: (e: HeartbeatEvent) => void): Promise<PluginListenerHandle>;\n addListener(eventName: 'geofence', listenerFunc: (e: GeofenceEvent) => void): Promise<PluginListenerHandle>;\n addListener(\n eventName: 'geofenceschange',\n listenerFunc: (e: GeofencesChangeEvent) => void,\n ): Promise<PluginListenerHandle>;\n addListener(eventName: 'http', listenerFunc: (e: HttpEvent) => void): Promise<PluginListenerHandle>;\n addListener(\n eventName: 'connectivitychange',\n listenerFunc: (e: ConnectivityChangeEvent) => void,\n ): Promise<PluginListenerHandle>;\n addListener(\n eventName: 'powersavechange',\n listenerFunc: (e: PowerSaveChangeEvent) => void,\n ): Promise<PluginListenerHandle>;\n addListener(eventName: 'enabledchange', listenerFunc: (e: EnabledChangeEvent) => void): Promise<PluginListenerHandle>;\n addListener(\n eventName: 'notificationaction',\n listenerFunc: (e: NotificationActionEvent) => void,\n ): Promise<PluginListenerHandle>;\n addListener(eventName: 'authorization', listenerFunc: (e: AuthorizationEvent) => void): Promise<PluginListenerHandle>;\n removeAllListeners(): Promise<void>;\n}\n"]}
@@ -0,0 +1,50 @@
1
+ import type { PluginListenerHandle } from '@capacitor/core';
2
+ import type { ActivityChangeEvent, AuthorizationEvent, ConnectivityChangeEvent, EnabledChangeEvent, GeofenceEvent, GeofencesChangeEvent, HeartbeatEvent, HttpEvent, Location, LocationTrackingEventMap, LocationTrackingEventName, MotionChangeEvent, NotificationActionEvent, PowerSaveChangeEvent, ProviderState } from './definitions';
3
+ /** Names of every event emitted by the plugin. */
4
+ export declare const Events: {
5
+ readonly LOCATION: "location";
6
+ readonly MOTIONCHANGE: "motionchange";
7
+ readonly ACTIVITYCHANGE: "activitychange";
8
+ readonly PROVIDERCHANGE: "providerchange";
9
+ readonly HEARTBEAT: "heartbeat";
10
+ readonly GEOFENCE: "geofence";
11
+ readonly GEOFENCESCHANGE: "geofenceschange";
12
+ readonly HTTP: "http";
13
+ readonly CONNECTIVITYCHANGE: "connectivitychange";
14
+ readonly POWERSAVECHANGE: "powersavechange";
15
+ readonly ENABLEDCHANGE: "enabledchange";
16
+ readonly NOTIFICATIONACTION: "notificationaction";
17
+ readonly AUTHORIZATION: "authorization";
18
+ };
19
+ /**
20
+ * Subscribes to any plugin event with a typed payload. Call `remove()` on the resolved handle to unsubscribe.
21
+ * The helpers below are standalone functions on purpose: Capacitor's plugin proxy turns unknown properties of
22
+ * `LocationTracking` into native calls, so nothing is attached to it.
23
+ */
24
+ export declare function on<K extends LocationTrackingEventName>(name: K, callback: (event: LocationTrackingEventMap[K]) => void): Promise<PluginListenerHandle>;
25
+ /** Every recorded location, including persisted `current_position` / `watch_position` fixes and motion changes. */
26
+ export declare const onLocation: (cb: (e: Location) => void) => Promise<PluginListenerHandle>;
27
+ /** Moving / stationary transitions, with the fix at the moment of the change. */
28
+ export declare const onMotionChange: (cb: (e: MotionChangeEvent) => void) => Promise<PluginListenerHandle>;
29
+ /** Detected activity (still, walking, in_vehicle, ...). */
30
+ export declare const onActivityChange: (cb: (e: ActivityChangeEvent) => void) => Promise<PluginListenerHandle>;
31
+ /** Location services or permission changes. */
32
+ export declare const onProviderChange: (cb: (e: ProviderState) => void) => Promise<PluginListenerHandle>;
33
+ /** Heartbeat records, created when no record was made for `heartbeat.minInterval` seconds while tracking. */
34
+ export declare const onHeartbeat: (cb: (e: HeartbeatEvent) => void) => Promise<PluginListenerHandle>;
35
+ /** Geofence ENTER / EXIT / DWELL transitions. */
36
+ export declare const onGeofence: (cb: (e: GeofenceEvent) => void) => Promise<PluginListenerHandle>;
37
+ /** Geofences added to or removed from monitoring. */
38
+ export declare const onGeofencesChange: (cb: (e: GeofencesChangeEvent) => void) => Promise<PluginListenerHandle>;
39
+ /** Result of every HTTP upload request. */
40
+ export declare const onHttp: (cb: (e: HttpEvent) => void) => Promise<PluginListenerHandle>;
41
+ /** Network connectivity changes. */
42
+ export declare const onConnectivityChange: (cb: (e: ConnectivityChangeEvent) => void) => Promise<PluginListenerHandle>;
43
+ /** Battery-saver mode changes. */
44
+ export declare const onPowerSaveChange: (cb: (e: PowerSaveChangeEvent) => void) => Promise<PluginListenerHandle>;
45
+ /** Tracking enabled / disabled. */
46
+ export declare const onEnabledChange: (cb: (e: EnabledChangeEvent) => void) => Promise<PluginListenerHandle>;
47
+ /** Taps on the foreground notification's action buttons. */
48
+ export declare const onNotificationAction: (cb: (e: NotificationActionEvent) => void) => Promise<PluginListenerHandle>;
49
+ /** JWT refresh results. */
50
+ export declare const onAuthorization: (cb: (e: AuthorizationEvent) => void) => Promise<PluginListenerHandle>;
@@ -0,0 +1,52 @@
1
+ import { LocationTracking } from './plugin';
2
+ /** Names of every event emitted by the plugin. */
3
+ export const Events = {
4
+ LOCATION: 'location',
5
+ MOTIONCHANGE: 'motionchange',
6
+ ACTIVITYCHANGE: 'activitychange',
7
+ PROVIDERCHANGE: 'providerchange',
8
+ HEARTBEAT: 'heartbeat',
9
+ GEOFENCE: 'geofence',
10
+ GEOFENCESCHANGE: 'geofenceschange',
11
+ HTTP: 'http',
12
+ CONNECTIVITYCHANGE: 'connectivitychange',
13
+ POWERSAVECHANGE: 'powersavechange',
14
+ ENABLEDCHANGE: 'enabledchange',
15
+ NOTIFICATIONACTION: 'notificationaction',
16
+ AUTHORIZATION: 'authorization',
17
+ };
18
+ /**
19
+ * Subscribes to any plugin event with a typed payload. Call `remove()` on the resolved handle to unsubscribe.
20
+ * The helpers below are standalone functions on purpose: Capacitor's plugin proxy turns unknown properties of
21
+ * `LocationTracking` into native calls, so nothing is attached to it.
22
+ */
23
+ export function on(name, callback) {
24
+ return LocationTracking.addListener(name, callback);
25
+ }
26
+ /** Every recorded location, including persisted `current_position` / `watch_position` fixes and motion changes. */
27
+ export const onLocation = (cb) => on('location', cb);
28
+ /** Moving / stationary transitions, with the fix at the moment of the change. */
29
+ export const onMotionChange = (cb) => on('motionchange', cb);
30
+ /** Detected activity (still, walking, in_vehicle, ...). */
31
+ export const onActivityChange = (cb) => on('activitychange', cb);
32
+ /** Location services or permission changes. */
33
+ export const onProviderChange = (cb) => on('providerchange', cb);
34
+ /** Heartbeat records, created when no record was made for `heartbeat.minInterval` seconds while tracking. */
35
+ export const onHeartbeat = (cb) => on('heartbeat', cb);
36
+ /** Geofence ENTER / EXIT / DWELL transitions. */
37
+ export const onGeofence = (cb) => on('geofence', cb);
38
+ /** Geofences added to or removed from monitoring. */
39
+ export const onGeofencesChange = (cb) => on('geofenceschange', cb);
40
+ /** Result of every HTTP upload request. */
41
+ export const onHttp = (cb) => on('http', cb);
42
+ /** Network connectivity changes. */
43
+ export const onConnectivityChange = (cb) => on('connectivitychange', cb);
44
+ /** Battery-saver mode changes. */
45
+ export const onPowerSaveChange = (cb) => on('powersavechange', cb);
46
+ /** Tracking enabled / disabled. */
47
+ export const onEnabledChange = (cb) => on('enabledchange', cb);
48
+ /** Taps on the foreground notification's action buttons. */
49
+ export const onNotificationAction = (cb) => on('notificationaction', cb);
50
+ /** JWT refresh results. */
51
+ export const onAuthorization = (cb) => on('authorization', cb);
52
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,kDAAkD;AAClD,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,QAAQ,EAAE,UAAU;IACpB,YAAY,EAAE,cAAc;IAC5B,cAAc,EAAE,gBAAgB;IAChC,cAAc,EAAE,gBAAgB;IAChC,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;IACpB,eAAe,EAAE,iBAAiB;IAClC,IAAI,EAAE,MAAM;IACZ,kBAAkB,EAAE,oBAAoB;IACxC,eAAe,EAAE,iBAAiB;IAClC,aAAa,EAAE,eAAe;IAC9B,kBAAkB,EAAE,oBAAoB;IACxC,aAAa,EAAE,eAAe;CACtB,CAAC;AAMX;;;;GAIG;AACH,MAAM,UAAU,EAAE,CAChB,IAAO,EACP,QAAsD;IAEtD,OAAQ,gBAAqD,CAAC,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC5F,CAAC;AAED,mHAAmH;AACnH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,EAAyB,EAAiC,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAC3G,iFAAiF;AACjF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAAkC,EAAiC,EAAE,CAClG,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;AACzB,2DAA2D;AAC3D,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,EAAoC,EAAiC,EAAE,CACtG,EAAE,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;AAC3B,+CAA+C;AAC/C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,EAA8B,EAAiC,EAAE,CAChG,EAAE,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;AAC3B,6GAA6G;AAC7G,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,EAA+B,EAAiC,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACnH,iDAAiD;AACjD,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,EAA8B,EAAiC,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAChH,qDAAqD;AACrD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,EAAqC,EAAiC,EAAE,CACxG,EAAE,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;AAC5B,2CAA2C;AAC3C,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EAA0B,EAAiC,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACpG,oCAAoC;AACpC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,EAAwC,EAAiC,EAAE,CAC9G,EAAE,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;AAC/B,kCAAkC;AAClC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,EAAqC,EAAiC,EAAE,CACxG,EAAE,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;AAC5B,mCAAmC;AACnC,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,EAAmC,EAAiC,EAAE,CACpG,EAAE,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;AAC1B,4DAA4D;AAC5D,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,EAAwC,EAAiC,EAAE,CAC9G,EAAE,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;AAC/B,2BAA2B;AAC3B,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,EAAmC,EAAiC,EAAE,CACpG,EAAE,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC","sourcesContent":["// Owned by Unit 1 (TS wrapper + web).\nimport type { PluginListenerHandle } from '@capacitor/core';\n\nimport type {\n ActivityChangeEvent,\n AuthorizationEvent,\n ConnectivityChangeEvent,\n EnabledChangeEvent,\n GeofenceEvent,\n GeofencesChangeEvent,\n HeartbeatEvent,\n HttpEvent,\n Location,\n LocationTrackingEventMap,\n LocationTrackingEventName,\n MotionChangeEvent,\n NotificationActionEvent,\n PowerSaveChangeEvent,\n ProviderState,\n} from './definitions';\nimport { LocationTracking } from './plugin';\n\n/** Names of every event emitted by the plugin. */\nexport const Events = {\n LOCATION: 'location',\n MOTIONCHANGE: 'motionchange',\n ACTIVITYCHANGE: 'activitychange',\n PROVIDERCHANGE: 'providerchange',\n HEARTBEAT: 'heartbeat',\n GEOFENCE: 'geofence',\n GEOFENCESCHANGE: 'geofenceschange',\n HTTP: 'http',\n CONNECTIVITYCHANGE: 'connectivitychange',\n POWERSAVECHANGE: 'powersavechange',\n ENABLEDCHANGE: 'enabledchange',\n NOTIFICATIONACTION: 'notificationaction',\n AUTHORIZATION: 'authorization',\n} as const;\n\ninterface UntypedListenerTarget {\n addListener(eventName: string, listenerFunc: (event: any) => void): Promise<PluginListenerHandle>;\n}\n\n/**\n * Subscribes to any plugin event with a typed payload. Call `remove()` on the resolved handle to unsubscribe.\n * The helpers below are standalone functions on purpose: Capacitor's plugin proxy turns unknown properties of\n * `LocationTracking` into native calls, so nothing is attached to it.\n */\nexport function on<K extends LocationTrackingEventName>(\n name: K,\n callback: (event: LocationTrackingEventMap[K]) => void,\n): Promise<PluginListenerHandle> {\n return (LocationTracking as unknown as UntypedListenerTarget).addListener(name, callback);\n}\n\n/** Every recorded location, including persisted `current_position` / `watch_position` fixes and motion changes. */\nexport const onLocation = (cb: (e: Location) => void): Promise<PluginListenerHandle> => on('location', cb);\n/** Moving / stationary transitions, with the fix at the moment of the change. */\nexport const onMotionChange = (cb: (e: MotionChangeEvent) => void): Promise<PluginListenerHandle> =>\n on('motionchange', cb);\n/** Detected activity (still, walking, in_vehicle, ...). */\nexport const onActivityChange = (cb: (e: ActivityChangeEvent) => void): Promise<PluginListenerHandle> =>\n on('activitychange', cb);\n/** Location services or permission changes. */\nexport const onProviderChange = (cb: (e: ProviderState) => void): Promise<PluginListenerHandle> =>\n on('providerchange', cb);\n/** Heartbeat records, created when no record was made for `heartbeat.minInterval` seconds while tracking. */\nexport const onHeartbeat = (cb: (e: HeartbeatEvent) => void): Promise<PluginListenerHandle> => on('heartbeat', cb);\n/** Geofence ENTER / EXIT / DWELL transitions. */\nexport const onGeofence = (cb: (e: GeofenceEvent) => void): Promise<PluginListenerHandle> => on('geofence', cb);\n/** Geofences added to or removed from monitoring. */\nexport const onGeofencesChange = (cb: (e: GeofencesChangeEvent) => void): Promise<PluginListenerHandle> =>\n on('geofenceschange', cb);\n/** Result of every HTTP upload request. */\nexport const onHttp = (cb: (e: HttpEvent) => void): Promise<PluginListenerHandle> => on('http', cb);\n/** Network connectivity changes. */\nexport const onConnectivityChange = (cb: (e: ConnectivityChangeEvent) => void): Promise<PluginListenerHandle> =>\n on('connectivitychange', cb);\n/** Battery-saver mode changes. */\nexport const onPowerSaveChange = (cb: (e: PowerSaveChangeEvent) => void): Promise<PluginListenerHandle> =>\n on('powersavechange', cb);\n/** Tracking enabled / disabled. */\nexport const onEnabledChange = (cb: (e: EnabledChangeEvent) => void): Promise<PluginListenerHandle> =>\n on('enabledchange', cb);\n/** Taps on the foreground notification's action buttons. */\nexport const onNotificationAction = (cb: (e: NotificationActionEvent) => void): Promise<PluginListenerHandle> =>\n on('notificationaction', cb);\n/** JWT refresh results. */\nexport const onAuthorization = (cb: (e: AuthorizationEvent) => void): Promise<PluginListenerHandle> =>\n on('authorization', cb);\n"]}
@@ -0,0 +1,3 @@
1
+ export * from './definitions';
2
+ export { LocationTracking } from './plugin';
3
+ export { Events, on, onActivityChange, onAuthorization, onConnectivityChange, onEnabledChange, onGeofence, onGeofencesChange, onHeartbeat, onHttp, onLocation, onMotionChange, onNotificationAction, onPowerSaveChange, onProviderChange, } from './events';
@@ -0,0 +1,5 @@
1
+ // Owned by Unit 1 (TS wrapper + web).
2
+ export * from './definitions';
3
+ export { LocationTracking } from './plugin';
4
+ export { Events, on, onActivityChange, onAuthorization, onConnectivityChange, onEnabledChange, onGeofence, onGeofencesChange, onHeartbeat, onHttp, onLocation, onMotionChange, onNotificationAction, onPowerSaveChange, onProviderChange, } from './events';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EACL,MAAM,EACN,EAAE,EACF,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,MAAM,EACN,UAAU,EACV,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,UAAU,CAAC","sourcesContent":["// Owned by Unit 1 (TS wrapper + web).\nexport * from './definitions';\nexport { LocationTracking } from './plugin';\nexport {\n Events,\n on,\n onActivityChange,\n onAuthorization,\n onConnectivityChange,\n onEnabledChange,\n onGeofence,\n onGeofencesChange,\n onHeartbeat,\n onHttp,\n onLocation,\n onMotionChange,\n onNotificationAction,\n onPowerSaveChange,\n onProviderChange,\n} from './events';\n"]}
@@ -0,0 +1,3 @@
1
+ import type { LocationTrackingPlugin } from './definitions';
2
+ /** The Capacitor plugin object. */
3
+ export declare const LocationTracking: LocationTrackingPlugin;
@@ -0,0 +1,10 @@
1
+ // Owned by Unit 1 (TS wrapper + web).
2
+ import { registerPlugin } from '@capacitor/core';
3
+ // Capacitor calls the web factory again for every call made before the first one resolves, so the (stateful) web
4
+ // implementation is memoized: concurrent first calls such as `onLocation(...)` + `ready(...)` share one instance.
5
+ let webImplementation;
6
+ /** The Capacitor plugin object. */
7
+ export const LocationTracking = registerPlugin('LocationTracking', {
8
+ web: () => webImplementation || (webImplementation = import('./web').then((m) => new m.LocationTrackingWeb())),
9
+ });
10
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,iHAAiH;AACjH,kHAAkH;AAClH,IAAI,iBAA8D,CAAC;AAEnE,mCAAmC;AACnC,MAAM,CAAC,MAAM,gBAAgB,GAAG,cAAc,CAAyB,kBAAkB,EAAE;IACzF,GAAG,EAAE,GAAG,EAAE,CAAC,iBAAiB,IAAI,CAAC,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,mBAAmB,EAAE,CAAC,CAAC;CAC/G,CAAC,CAAC","sourcesContent":["// Owned by Unit 1 (TS wrapper + web).\nimport { registerPlugin } from '@capacitor/core';\n\nimport type { LocationTrackingPlugin } from './definitions';\n\n// Capacitor calls the web factory again for every call made before the first one resolves, so the (stateful) web\n// implementation is memoized: concurrent first calls such as `onLocation(...)` + `ready(...)` share one instance.\nlet webImplementation: Promise<LocationTrackingPlugin> | undefined;\n\n/** The Capacitor plugin object. */\nexport const LocationTracking = registerPlugin<LocationTrackingPlugin>('LocationTracking', {\n web: () => webImplementation || (webImplementation = import('./web').then((m) => new m.LocationTrackingWeb())),\n});\n"]}
@@ -0,0 +1,19 @@
1
+ import type { Config, State } from '../definitions';
2
+ type JsonObject = Record<string, unknown>;
3
+ /**
4
+ * Every documented `@default` of {@link Config}, mirroring the Android `Config` model.
5
+ * Keys without a default (for example `notification.title`, which falls back to the app label) are left out.
6
+ */
7
+ export declare function defaultConfig(): Config;
8
+ export declare function isPlainObject(value: unknown): value is JsonObject;
9
+ /** Deep copy of JSON-like data (plain objects, arrays and primitives). */
10
+ export declare function cloneJson<T>(value: T): T;
11
+ /**
12
+ * Returns `base` deep-merged with `patch`, without changing either.
13
+ * - Sections are merged key by key; arrays, maps (`headers`, `params`, `extras`, ...) and scalars are replaced.
14
+ * - `null` resets a key to its default (`http.authorization` resets to `null`); `undefined` is ignored.
15
+ */
16
+ export declare function mergeConfig(base: Config, patch: Config | undefined): Config;
17
+ /** The web `State`: tracking is never enabled on web, and nothing is recorded or persisted. */
18
+ export declare function webState(config: Config): State;
19
+ export {};
@@ -0,0 +1,243 @@
1
+ /** Never copied: assigning `__proto__` would change the target's prototype instead of adding a key. */
2
+ const PROTO_KEY = '__proto__';
3
+ /** Free-form maps. They are config values, not sections: a new value replaces the old one instead of merging. */
4
+ const MAP_VALUES = [
5
+ 'http.headers',
6
+ 'http.params',
7
+ 'persistence.extras',
8
+ 'http.authorization.refreshPayload',
9
+ 'http.authorization.refreshHeaders',
10
+ ];
11
+ /** Sections whose own default is `null`. `null` resets them to `null`; setting them fills in their field defaults. */
12
+ const NULLABLE_SECTIONS = ['http.authorization'];
13
+ /**
14
+ * Every documented `@default` of {@link Config}, mirroring the Android `Config` model.
15
+ * Keys without a default (for example `notification.title`, which falls back to the app label) are left out.
16
+ */
17
+ export function defaultConfig() {
18
+ return {
19
+ geolocation: {
20
+ desiredAccuracy: 'high',
21
+ distanceFilter: 10,
22
+ locationUpdateInterval: 1000,
23
+ fastestLocationUpdateInterval: 500,
24
+ disableElasticity: false,
25
+ elasticityMultiplier: 1,
26
+ stationaryRadius: 25,
27
+ stopTimeout: 5,
28
+ stopAfterElapsedMinutes: 0,
29
+ stopOnStationary: false,
30
+ locationTimeout: 30000,
31
+ filter: {
32
+ useKalman: false,
33
+ trackingAccuracyThreshold: 100,
34
+ maxImpliedSpeed: 80,
35
+ odometerAccuracyThreshold: 20,
36
+ allowIdenticalLocations: false,
37
+ rejectMockLocations: false,
38
+ },
39
+ },
40
+ activity: {
41
+ disableMotionActivityUpdates: false,
42
+ activityRecognitionInterval: 10000,
43
+ minimumActivityRecognitionConfidence: 75,
44
+ motionTriggerDelay: 0,
45
+ disableStopDetection: false,
46
+ },
47
+ heartbeat: {
48
+ enabled: true,
49
+ minInterval: 180,
50
+ maxInterval: 300,
51
+ },
52
+ http: {
53
+ url: null,
54
+ method: 'POST',
55
+ headers: {},
56
+ params: {},
57
+ autoSync: true,
58
+ autoSyncThreshold: 0,
59
+ syncInterval: 0,
60
+ batchSync: false,
61
+ maxBatchSize: 100,
62
+ disableAutoSyncOnCellular: false,
63
+ rootProperty: 'location',
64
+ locationTemplate: null,
65
+ geofenceTemplate: null,
66
+ timeout: 60000,
67
+ authorization: null,
68
+ },
69
+ persistence: {
70
+ maxDaysToPersist: 7,
71
+ maxRecordsToPersist: -1,
72
+ extras: {},
73
+ },
74
+ app: {
75
+ stopOnTerminate: true,
76
+ startOnBoot: false,
77
+ },
78
+ notification: {
79
+ text: 'Location tracking is active',
80
+ smallIcon: 'drawable/lt_ic_notification',
81
+ priority: 'default',
82
+ channelId: 'location_tracking',
83
+ channelName: 'Location tracking',
84
+ actions: [],
85
+ resume: {
86
+ enabled: false,
87
+ text: 'Location tracking is paused. Tap to resume.',
88
+ channelName: 'Paused location tracking',
89
+ },
90
+ },
91
+ geofence: {
92
+ initialTriggerEntry: true,
93
+ },
94
+ logger: {
95
+ logLevel: 'info',
96
+ logMaxDays: 3,
97
+ },
98
+ backgroundPermissionRationale: {},
99
+ locationProvider: 'auto',
100
+ };
101
+ }
102
+ /** Field defaults of `http.authorization`, applied once it is set (the section itself defaults to `null`). */
103
+ function defaultAuthorization() {
104
+ return {
105
+ strategy: 'JWT',
106
+ refreshPayload: {},
107
+ refreshHeaders: {},
108
+ refreshPayloadEncoding: 'json',
109
+ expires: -1,
110
+ };
111
+ }
112
+ function defaultTrees() {
113
+ const sections = defaultConfig();
114
+ sections.http.authorization = defaultAuthorization();
115
+ return { values: defaultConfig(), sections };
116
+ }
117
+ export function isPlainObject(value) {
118
+ return Object.prototype.toString.call(value) === '[object Object]';
119
+ }
120
+ /** Deep copy of JSON-like data (plain objects, arrays and primitives). */
121
+ export function cloneJson(value) {
122
+ if (Array.isArray(value)) {
123
+ return value.map((item) => cloneJson(item));
124
+ }
125
+ if (isPlainObject(value)) {
126
+ const copy = {};
127
+ for (const key of Object.keys(value)) {
128
+ if (key !== PROTO_KEY) {
129
+ copy[key] = cloneJson(value[key]);
130
+ }
131
+ }
132
+ return copy;
133
+ }
134
+ return value;
135
+ }
136
+ function lookup(tree, path) {
137
+ let node = tree;
138
+ for (const key of path) {
139
+ if (!isPlainObject(node)) {
140
+ return undefined;
141
+ }
142
+ node = node[key];
143
+ }
144
+ return node;
145
+ }
146
+ function hasValue(patch) {
147
+ return Object.keys(patch).some((key) => patch[key] !== undefined && patch[key] !== null);
148
+ }
149
+ function mergeInto(target, patch, path, trees) {
150
+ for (const key of Object.keys(patch)) {
151
+ const value = patch[key];
152
+ if (value === undefined || key === PROTO_KEY) {
153
+ continue;
154
+ }
155
+ const childPath = path.concat(key);
156
+ const dotted = childPath.join('.');
157
+ const sectionDefaults = lookup(trees.sections, childPath);
158
+ const isSection = isPlainObject(sectionDefaults) && MAP_VALUES.indexOf(dotted) < 0;
159
+ if (value === null) {
160
+ // null resets the key to its default (fields of a null section use the section's field defaults);
161
+ // keys without a default are removed.
162
+ const own = lookup(trees.values, childPath);
163
+ const reset = own !== undefined ? own : sectionDefaults;
164
+ if (reset === undefined) {
165
+ delete target[key];
166
+ }
167
+ else {
168
+ target[key] = cloneJson(reset);
169
+ }
170
+ }
171
+ else if (isSection) {
172
+ // A section only accepts an object; anything else is ignored so the section stays fully populated.
173
+ const current = target[key];
174
+ // A null section is only switched on by a patch that sets something.
175
+ const staysNull = NULLABLE_SECTIONS.indexOf(dotted) >= 0 && current === null && !hasValue(value);
176
+ if (isPlainObject(value) && !staysNull) {
177
+ const base = isPlainObject(current) ? current : cloneJson(sectionDefaults);
178
+ mergeInto(base, value, childPath, trees);
179
+ target[key] = base;
180
+ }
181
+ }
182
+ else {
183
+ // Arrays, maps and scalar values are replaced.
184
+ target[key] = cloneJson(value);
185
+ }
186
+ }
187
+ }
188
+ /** The same clamps the Android config validator applies. */
189
+ function clamp(config) {
190
+ const heartbeat = config.heartbeat;
191
+ if (heartbeat) {
192
+ if (typeof heartbeat.minInterval === 'number' && heartbeat.minInterval < 60) {
193
+ heartbeat.minInterval = 60;
194
+ }
195
+ if (typeof heartbeat.minInterval === 'number' &&
196
+ typeof heartbeat.maxInterval === 'number' &&
197
+ heartbeat.maxInterval < heartbeat.minInterval) {
198
+ heartbeat.maxInterval = heartbeat.minInterval;
199
+ }
200
+ }
201
+ const activity = config.activity;
202
+ if (activity && typeof activity.minimumActivityRecognitionConfidence === 'number') {
203
+ activity.minimumActivityRecognitionConfidence = Math.min(100, Math.max(0, activity.minimumActivityRecognitionConfidence));
204
+ }
205
+ const http = config.http;
206
+ if (http && typeof http.maxBatchSize === 'number' && http.maxBatchSize < 1) {
207
+ http.maxBatchSize = 1;
208
+ }
209
+ if (http && typeof http.syncInterval === 'number' && http.syncInterval < 0) {
210
+ http.syncInterval = 0;
211
+ }
212
+ const notification = config.notification;
213
+ if (notification && Array.isArray(notification.actions) && notification.actions.length > 3) {
214
+ notification.actions = notification.actions.slice(0, 3);
215
+ }
216
+ }
217
+ /**
218
+ * Returns `base` deep-merged with `patch`, without changing either.
219
+ * - Sections are merged key by key; arrays, maps (`headers`, `params`, `extras`, ...) and scalars are replaced.
220
+ * - `null` resets a key to its default (`http.authorization` resets to `null`); `undefined` is ignored.
221
+ */
222
+ export function mergeConfig(base, patch) {
223
+ const result = cloneJson(base);
224
+ if (patch) {
225
+ mergeInto(result, patch, [], defaultTrees());
226
+ }
227
+ const config = result;
228
+ clamp(config);
229
+ return config;
230
+ }
231
+ /** The web `State`: tracking is never enabled on web, and nothing is recorded or persisted. */
232
+ export function webState(config) {
233
+ return {
234
+ enabled: false,
235
+ trackingMode: 'location',
236
+ isMoving: false,
237
+ odometer: 0,
238
+ backend: 'web',
239
+ lastRecordAt: null,
240
+ config: cloneJson(config),
241
+ };
242
+ }
243
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/web/config.ts"],"names":[],"mappings":"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","sourcesContent":["// Owned by Unit 1 (TS wrapper + web).\nimport type { AuthorizationConfig, Config, State } from '../definitions';\n\ntype JsonObject = Record<string, unknown>;\n\n/** Never copied: assigning `__proto__` would change the target's prototype instead of adding a key. */\nconst PROTO_KEY = '__proto__';\n\n/** Free-form maps. They are config values, not sections: a new value replaces the old one instead of merging. */\nconst MAP_VALUES = [\n 'http.headers',\n 'http.params',\n 'persistence.extras',\n 'http.authorization.refreshPayload',\n 'http.authorization.refreshHeaders',\n];\n\n/** Sections whose own default is `null`. `null` resets them to `null`; setting them fills in their field defaults. */\nconst NULLABLE_SECTIONS = ['http.authorization'];\n\n/**\n * Every documented `@default` of {@link Config}, mirroring the Android `Config` model.\n * Keys without a default (for example `notification.title`, which falls back to the app label) are left out.\n */\nexport function defaultConfig(): Config {\n return {\n geolocation: {\n desiredAccuracy: 'high',\n distanceFilter: 10,\n locationUpdateInterval: 1000,\n fastestLocationUpdateInterval: 500,\n disableElasticity: false,\n elasticityMultiplier: 1,\n stationaryRadius: 25,\n stopTimeout: 5,\n stopAfterElapsedMinutes: 0,\n stopOnStationary: false,\n locationTimeout: 30000,\n filter: {\n useKalman: false,\n trackingAccuracyThreshold: 100,\n maxImpliedSpeed: 80,\n odometerAccuracyThreshold: 20,\n allowIdenticalLocations: false,\n rejectMockLocations: false,\n },\n },\n activity: {\n disableMotionActivityUpdates: false,\n activityRecognitionInterval: 10000,\n minimumActivityRecognitionConfidence: 75,\n motionTriggerDelay: 0,\n disableStopDetection: false,\n },\n heartbeat: {\n enabled: true,\n minInterval: 180,\n maxInterval: 300,\n },\n http: {\n url: null,\n method: 'POST',\n headers: {},\n params: {},\n autoSync: true,\n autoSyncThreshold: 0,\n syncInterval: 0,\n batchSync: false,\n maxBatchSize: 100,\n disableAutoSyncOnCellular: false,\n rootProperty: 'location',\n locationTemplate: null,\n geofenceTemplate: null,\n timeout: 60000,\n authorization: null,\n },\n persistence: {\n maxDaysToPersist: 7,\n maxRecordsToPersist: -1,\n extras: {},\n },\n app: {\n stopOnTerminate: true,\n startOnBoot: false,\n },\n notification: {\n text: 'Location tracking is active',\n smallIcon: 'drawable/lt_ic_notification',\n priority: 'default',\n channelId: 'location_tracking',\n channelName: 'Location tracking',\n actions: [],\n resume: {\n enabled: false,\n text: 'Location tracking is paused. Tap to resume.',\n channelName: 'Paused location tracking',\n },\n },\n geofence: {\n initialTriggerEntry: true,\n },\n logger: {\n logLevel: 'info',\n logMaxDays: 3,\n },\n backgroundPermissionRationale: {},\n locationProvider: 'auto',\n };\n}\n\n/** Field defaults of `http.authorization`, applied once it is set (the section itself defaults to `null`). */\nfunction defaultAuthorization(): AuthorizationConfig {\n return {\n strategy: 'JWT',\n refreshPayload: {},\n refreshHeaders: {},\n refreshPayloadEncoding: 'json',\n expires: -1,\n };\n}\n\ninterface DefaultTrees {\n /** The real defaults (`http.authorization` is `null`). */\n values: JsonObject;\n /** The defaults with nullable sections filled in, used to find sections and the defaults of their fields. */\n sections: JsonObject;\n}\n\nfunction defaultTrees(): DefaultTrees {\n const sections = defaultConfig() as unknown as JsonObject;\n (sections.http as JsonObject).authorization = defaultAuthorization();\n return { values: defaultConfig() as unknown as JsonObject, sections };\n}\n\nexport function isPlainObject(value: unknown): value is JsonObject {\n return Object.prototype.toString.call(value) === '[object Object]';\n}\n\n/** Deep copy of JSON-like data (plain objects, arrays and primitives). */\nexport function cloneJson<T>(value: T): T {\n if (Array.isArray(value)) {\n return value.map((item) => cloneJson(item)) as unknown as T;\n }\n if (isPlainObject(value)) {\n const copy: JsonObject = {};\n for (const key of Object.keys(value)) {\n if (key !== PROTO_KEY) {\n copy[key] = cloneJson(value[key]);\n }\n }\n return copy as T;\n }\n return value;\n}\n\nfunction lookup(tree: JsonObject, path: string[]): unknown {\n let node: unknown = tree;\n for (const key of path) {\n if (!isPlainObject(node)) {\n return undefined;\n }\n node = node[key];\n }\n return node;\n}\n\nfunction hasValue(patch: JsonObject): boolean {\n return Object.keys(patch).some((key) => patch[key] !== undefined && patch[key] !== null);\n}\n\nfunction mergeInto(target: JsonObject, patch: JsonObject, path: string[], trees: DefaultTrees): void {\n for (const key of Object.keys(patch)) {\n const value = patch[key];\n if (value === undefined || key === PROTO_KEY) {\n continue;\n }\n const childPath = path.concat(key);\n const dotted = childPath.join('.');\n const sectionDefaults = lookup(trees.sections, childPath);\n const isSection = isPlainObject(sectionDefaults) && MAP_VALUES.indexOf(dotted) < 0;\n\n if (value === null) {\n // null resets the key to its default (fields of a null section use the section's field defaults);\n // keys without a default are removed.\n const own = lookup(trees.values, childPath);\n const reset = own !== undefined ? own : sectionDefaults;\n if (reset === undefined) {\n delete target[key];\n } else {\n target[key] = cloneJson(reset);\n }\n } else if (isSection) {\n // A section only accepts an object; anything else is ignored so the section stays fully populated.\n const current = target[key];\n // A null section is only switched on by a patch that sets something.\n const staysNull = NULLABLE_SECTIONS.indexOf(dotted) >= 0 && current === null && !hasValue(value as JsonObject);\n if (isPlainObject(value) && !staysNull) {\n const base = isPlainObject(current) ? current : (cloneJson(sectionDefaults) as JsonObject);\n mergeInto(base, value as JsonObject, childPath, trees);\n target[key] = base;\n }\n } else {\n // Arrays, maps and scalar values are replaced.\n target[key] = cloneJson(value);\n }\n }\n}\n\n/** The same clamps the Android config validator applies. */\nfunction clamp(config: Config): void {\n const heartbeat = config.heartbeat;\n if (heartbeat) {\n if (typeof heartbeat.minInterval === 'number' && heartbeat.minInterval < 60) {\n heartbeat.minInterval = 60;\n }\n if (\n typeof heartbeat.minInterval === 'number' &&\n typeof heartbeat.maxInterval === 'number' &&\n heartbeat.maxInterval < heartbeat.minInterval\n ) {\n heartbeat.maxInterval = heartbeat.minInterval;\n }\n }\n const activity = config.activity;\n if (activity && typeof activity.minimumActivityRecognitionConfidence === 'number') {\n activity.minimumActivityRecognitionConfidence = Math.min(\n 100,\n Math.max(0, activity.minimumActivityRecognitionConfidence),\n );\n }\n const http = config.http;\n if (http && typeof http.maxBatchSize === 'number' && http.maxBatchSize < 1) {\n http.maxBatchSize = 1;\n }\n if (http && typeof http.syncInterval === 'number' && http.syncInterval < 0) {\n http.syncInterval = 0;\n }\n const notification = config.notification;\n if (notification && Array.isArray(notification.actions) && notification.actions.length > 3) {\n notification.actions = notification.actions.slice(0, 3);\n }\n}\n\n/**\n * Returns `base` deep-merged with `patch`, without changing either.\n * - Sections are merged key by key; arrays, maps (`headers`, `params`, `extras`, ...) and scalars are replaced.\n * - `null` resets a key to its default (`http.authorization` resets to `null`); `undefined` is ignored.\n */\nexport function mergeConfig(base: Config, patch: Config | undefined): Config {\n const result = cloneJson(base) as unknown as JsonObject;\n if (patch) {\n mergeInto(result, patch as unknown as JsonObject, [], defaultTrees());\n }\n const config = result as unknown as Config;\n clamp(config);\n return config;\n}\n\n/** The web `State`: tracking is never enabled on web, and nothing is recorded or persisted. */\nexport function webState(config: Config): State {\n return {\n enabled: false,\n trackingMode: 'location',\n isMoving: false,\n odometer: 0,\n backend: 'web',\n lastRecordAt: null,\n config: cloneJson(config),\n };\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import type { DeviceInfo } from '../definitions';
2
+ /** Keep in sync with `package.json` (a test checks this) and `PLUGIN_VERSION` in `android/build.gradle`. */
3
+ export declare const PLUGIN_VERSION = "8.0.0";
4
+ /**
5
+ * Best-effort `DeviceInfo` for a browser, parsed from `navigator.userAgent`.
6
+ * `brand` is the browser, `model` the device when the user agent names one (else `navigator.platform`),
7
+ * `osVersion` includes the OS name, and `sdkInt` is -1 because there is no Android API level.
8
+ */
9
+ export declare function webDeviceInfo(userAgent: string, platform?: string): DeviceInfo;
@@ -0,0 +1,90 @@
1
+ /** Keep in sync with `package.json` (a test checks this) and `PLUGIN_VERSION` in `android/build.gradle`. */
2
+ export const PLUGIN_VERSION = '8.0.0';
3
+ /** Tokens after `Android x` that do not name a device ('K' is Chrome's reduced user agent). */
4
+ const NOT_A_MODEL = ['K', 'U', 'wv', 'Mobile', 'Tablet', 'HarmonyOS'];
5
+ /** The device model among the `;`-separated tokens that follow `Android x` in the user agent's comment. */
6
+ function androidModel(tokens) {
7
+ for (const token of tokens) {
8
+ const model = token.replace(/\s*Build\/.*$/, '').trim();
9
+ const skip = !model ||
10
+ NOT_A_MODEL.indexOf(model) >= 0 ||
11
+ /^[a-z]{2}(?:[-_][a-z]{2})?$/i.test(model) || // locale, e.g. en-us
12
+ /^(?:rv:|HMSCore)/.test(model);
13
+ if (!skip) {
14
+ return model;
15
+ }
16
+ }
17
+ return undefined;
18
+ }
19
+ function parseOs(ua) {
20
+ let match = /\(([^)]*?)Android\s*([\d.]*)([^)]*)\)/.exec(ua);
21
+ if (match) {
22
+ return {
23
+ osVersion: match[2] ? `Android ${match[2]}` : 'Android',
24
+ manufacturer: 'unknown',
25
+ model: androidModel(match[3].split(';')),
26
+ };
27
+ }
28
+ match = /(iPhone|iPad|iPod).*?OS (\d+(?:_\d+)*)/.exec(ua);
29
+ if (match) {
30
+ return { osVersion: `iOS ${match[2].replace(/_/g, '.')}`, manufacturer: 'Apple', model: match[1] };
31
+ }
32
+ match = /Mac OS X (\d+(?:[_.]\d+)*)/.exec(ua);
33
+ if (match) {
34
+ return { osVersion: `macOS ${match[1].replace(/_/g, '.')}`, manufacturer: 'Apple', model: 'Macintosh' };
35
+ }
36
+ match = /Windows NT (\d+(?:\.\d+)*)/.exec(ua);
37
+ if (match) {
38
+ return { osVersion: `Windows ${match[1]}`, manufacturer: 'unknown', model: undefined };
39
+ }
40
+ match = /CrOS \S+ (\d+(?:\.\d+)*)/.exec(ua);
41
+ if (match) {
42
+ return { osVersion: `ChromeOS ${match[1]}`, manufacturer: 'unknown', model: undefined };
43
+ }
44
+ if (/Linux/.test(ua)) {
45
+ return { osVersion: 'Linux', manufacturer: 'unknown', model: undefined };
46
+ }
47
+ return { osVersion: 'unknown', manufacturer: 'unknown', model: undefined };
48
+ }
49
+ /** Browser name and major version, e.g. `Chrome 120`. Order matters: many browsers also claim Chrome or Safari. */
50
+ function parseBrowser(ua) {
51
+ const browsers = [
52
+ ['Edge', /Edg(?:e|A|iOS)?\/(\d+)/],
53
+ ['Opera', /(?:OPR|Opera)\/(\d+)/],
54
+ ['Samsung Internet', /SamsungBrowser\/(\d+)/],
55
+ ['Huawei Browser', /HuaweiBrowser\/(\d+)/],
56
+ ['Firefox', /(?:Firefox|FxiOS)\/(\d+)/],
57
+ ['Chrome', /(?:Chrome|CriOS)\/(\d+)/],
58
+ ['Safari', /Version\/(\d+)[\d.]*.*Safari\//],
59
+ ['Node.js', /Node\.js\/(\d+)/],
60
+ ];
61
+ for (const [name, pattern] of browsers) {
62
+ const match = pattern.exec(ua);
63
+ if (match) {
64
+ return `${name} ${match[1]}`;
65
+ }
66
+ }
67
+ return 'unknown';
68
+ }
69
+ /**
70
+ * Best-effort `DeviceInfo` for a browser, parsed from `navigator.userAgent`.
71
+ * `brand` is the browser, `model` the device when the user agent names one (else `navigator.platform`),
72
+ * `osVersion` includes the OS name, and `sdkInt` is -1 because there is no Android API level.
73
+ */
74
+ export function webDeviceInfo(userAgent, platform) {
75
+ const os = parseOs(userAgent);
76
+ return {
77
+ platform: 'web',
78
+ manufacturer: os.manufacturer,
79
+ model: os.model || platform || 'unknown',
80
+ brand: parseBrowser(userAgent),
81
+ osVersion: os.osVersion,
82
+ sdkInt: -1,
83
+ pluginVersion: PLUGIN_VERSION,
84
+ gmsAvailable: false,
85
+ hmsAvailable: false,
86
+ backend: 'web',
87
+ packagedProviders: [],
88
+ };
89
+ }
90
+ //# sourceMappingURL=device.js.map