@josuelmm/capacitor-background-geolocation 1.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 (207) hide show
  1. package/JosuelmmCapacitorBackgroundGeolocation.podspec +34 -0
  2. package/LICENSE +17 -0
  3. package/NOTICE.md +32 -0
  4. package/Package.swift +45 -0
  5. package/README.md +402 -0
  6. package/android/build.gradle +79 -0
  7. package/android/proguard-rules.pro +1 -0
  8. package/android/src/main/AndroidManifest.xml +83 -0
  9. package/android/src/main/java/com/evgenii/jsevaluator/HandlerWrapper.java +18 -0
  10. package/android/src/main/java/com/evgenii/jsevaluator/JavaScriptInterface.java +22 -0
  11. package/android/src/main/java/com/evgenii/jsevaluator/JsEvaluator.java +133 -0
  12. package/android/src/main/java/com/evgenii/jsevaluator/JsFunctionCallFormatter.java +37 -0
  13. package/android/src/main/java/com/evgenii/jsevaluator/WebViewWrapper.java +71 -0
  14. package/android/src/main/java/com/evgenii/jsevaluator/interfaces/CallJavaResultInterface.java +8 -0
  15. package/android/src/main/java/com/evgenii/jsevaluator/interfaces/HandlerWrapperInterface.java +5 -0
  16. package/android/src/main/java/com/evgenii/jsevaluator/interfaces/JsCallback.java +10 -0
  17. package/android/src/main/java/com/evgenii/jsevaluator/interfaces/JsEvaluatorInterface.java +18 -0
  18. package/android/src/main/java/com/evgenii/jsevaluator/interfaces/WebViewWrapperInterface.java +14 -0
  19. package/android/src/main/java/com/josuelmm/capacitor/backgroundgeolocation/BackgroundGeolocationPlugin.java +898 -0
  20. package/android/src/main/java/com/josuelmm/capacitor/backgroundgeolocation/ConfigMapper.java +303 -0
  21. package/android/src/main/java/com/josuelmm/capacitor/backgroundgeolocation/HeadlessTaskRegistry.java +34 -0
  22. package/android/src/main/java/com/josuelmm/capacitor/backgroundgeolocation/JsEvaluatorTaskRunner.java +63 -0
  23. package/android/src/main/java/com/marianhello/bgloc/BackgroundGeolocationFacade.java +699 -0
  24. package/android/src/main/java/com/marianhello/bgloc/BootCompletedReceiver.java +103 -0
  25. package/android/src/main/java/com/marianhello/bgloc/Config.java +1155 -0
  26. package/android/src/main/java/com/marianhello/bgloc/ConnectivityListener.java +5 -0
  27. package/android/src/main/java/com/marianhello/bgloc/HttpPostService.java +362 -0
  28. package/android/src/main/java/com/marianhello/bgloc/LocationManager.java +138 -0
  29. package/android/src/main/java/com/marianhello/bgloc/PluginDelegate.java +45 -0
  30. package/android/src/main/java/com/marianhello/bgloc/PluginException.java +38 -0
  31. package/android/src/main/java/com/marianhello/bgloc/PostLocationTask.java +238 -0
  32. package/android/src/main/java/com/marianhello/bgloc/ResourceResolver.java +55 -0
  33. package/android/src/main/java/com/marianhello/bgloc/data/AbstractLocationTemplate.java +69 -0
  34. package/android/src/main/java/com/marianhello/bgloc/data/ArrayListLocationTemplate.java +88 -0
  35. package/android/src/main/java/com/marianhello/bgloc/data/BackgroundActivity.java +108 -0
  36. package/android/src/main/java/com/marianhello/bgloc/data/BackgroundLocation.java +1088 -0
  37. package/android/src/main/java/com/marianhello/bgloc/data/ConfigJsonMapper.java +211 -0
  38. package/android/src/main/java/com/marianhello/bgloc/data/ConfigurationDAO.java +13 -0
  39. package/android/src/main/java/com/marianhello/bgloc/data/DAOFactory.java +17 -0
  40. package/android/src/main/java/com/marianhello/bgloc/data/HashMapLocationTemplate.java +82 -0
  41. package/android/src/main/java/com/marianhello/bgloc/data/LocationDAO.java +27 -0
  42. package/android/src/main/java/com/marianhello/bgloc/data/LocationTemplate.java +12 -0
  43. package/android/src/main/java/com/marianhello/bgloc/data/LocationTemplateFactory.java +71 -0
  44. package/android/src/main/java/com/marianhello/bgloc/data/LocationTransform.java +19 -0
  45. package/android/src/main/java/com/marianhello/bgloc/data/SessionLocationDAO.java +18 -0
  46. package/android/src/main/java/com/marianhello/bgloc/data/provider/ContentProviderLocationDAO.java +406 -0
  47. package/android/src/main/java/com/marianhello/bgloc/data/provider/LocationContentProvider.java +321 -0
  48. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationContract.java +94 -0
  49. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationDAO.java +227 -0
  50. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationContract.java +122 -0
  51. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationDAO.java +550 -0
  52. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java +189 -0
  53. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionContract.java +74 -0
  54. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionLocationDAO.java +169 -0
  55. package/android/src/main/java/com/marianhello/bgloc/driving/DrivingEventsDetector.java +265 -0
  56. package/android/src/main/java/com/marianhello/bgloc/headless/AbstractTaskRunner.java +15 -0
  57. package/android/src/main/java/com/marianhello/bgloc/headless/ActivityTask.java +48 -0
  58. package/android/src/main/java/com/marianhello/bgloc/headless/JsCallback.java +10 -0
  59. package/android/src/main/java/com/marianhello/bgloc/headless/LocationTask.java +60 -0
  60. package/android/src/main/java/com/marianhello/bgloc/headless/StationaryTask.java +25 -0
  61. package/android/src/main/java/com/marianhello/bgloc/headless/Task.java +8 -0
  62. package/android/src/main/java/com/marianhello/bgloc/headless/TaskRunner.java +5 -0
  63. package/android/src/main/java/com/marianhello/bgloc/headless/TaskRunnerFactory.java +8 -0
  64. package/android/src/main/java/com/marianhello/bgloc/http/UrlTemplateResolver.java +115 -0
  65. package/android/src/main/java/com/marianhello/bgloc/oem/BatteryOemHelper.java +214 -0
  66. package/android/src/main/java/com/marianhello/bgloc/provider/AbstractLocationProvider.java +218 -0
  67. package/android/src/main/java/com/marianhello/bgloc/provider/ActivityRecognitionLocationProvider.java +385 -0
  68. package/android/src/main/java/com/marianhello/bgloc/provider/DistanceFilterLocationProvider.java +685 -0
  69. package/android/src/main/java/com/marianhello/bgloc/provider/LocationProvider.java +32 -0
  70. package/android/src/main/java/com/marianhello/bgloc/provider/LocationProviderFactory.java +47 -0
  71. package/android/src/main/java/com/marianhello/bgloc/provider/ProviderDelegate.java +12 -0
  72. package/android/src/main/java/com/marianhello/bgloc/provider/RawLocationProvider.java +175 -0
  73. package/android/src/main/java/com/marianhello/bgloc/sensor/SensorFusionDetector.java +199 -0
  74. package/android/src/main/java/com/marianhello/bgloc/service/LocationService.java +16 -0
  75. package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +1531 -0
  76. package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceInfo.java +6 -0
  77. package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceInfoImpl.java +41 -0
  78. package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceIntentBuilder.java +203 -0
  79. package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java +156 -0
  80. package/android/src/main/java/com/marianhello/bgloc/sync/AccountHelper.java +39 -0
  81. package/android/src/main/java/com/marianhello/bgloc/sync/Authenticator.java +68 -0
  82. package/android/src/main/java/com/marianhello/bgloc/sync/AuthenticatorService.java +28 -0
  83. package/android/src/main/java/com/marianhello/bgloc/sync/BatchManager.java +311 -0
  84. package/android/src/main/java/com/marianhello/bgloc/sync/NotificationHelper.java +148 -0
  85. package/android/src/main/java/com/marianhello/bgloc/sync/SyncAdapter.java +301 -0
  86. package/android/src/main/java/com/marianhello/bgloc/sync/SyncService.java +68 -0
  87. package/android/src/main/java/com/marianhello/logging/DBLogReader.java +208 -0
  88. package/android/src/main/java/com/marianhello/logging/LogEntry.java +99 -0
  89. package/android/src/main/java/com/marianhello/logging/LoggerManager.java +70 -0
  90. package/android/src/main/java/com/marianhello/logging/UncaughtExceptionLogger.java +36 -0
  91. package/android/src/main/java/com/marianhello/utils/CloneHelper.java +22 -0
  92. package/android/src/main/java/com/marianhello/utils/Convert.java +56 -0
  93. package/android/src/main/java/com/marianhello/utils/TextUtils.java +72 -0
  94. package/android/src/main/java/com/marianhello/utils/ToneGenerator.java +68 -0
  95. package/android/src/main/java/org/apache/commons/io/Charsets.java +153 -0
  96. package/android/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java +344 -0
  97. package/android/src/main/java/org/chromium/content/browser/ThreadUtils.java +134 -0
  98. package/android/src/main/java/ru/andremoniy/sqlbuilder/SqlExpression.java +398 -0
  99. package/android/src/main/java/ru/andremoniy/sqlbuilder/SqlSelectStatement.java +671 -0
  100. package/android/src/main/java/ru/andremoniy/sqlbuilder/SqlStatement.java +29 -0
  101. package/android/src/main/java/ru/andremoniy/utils/TextUtils.java +61 -0
  102. package/android/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  103. package/android/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  104. package/android/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  105. package/android/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  106. package/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  107. package/android/src/main/res/values/strings.xml +15 -0
  108. package/android/src/main/res/xml/authenticator.xml +7 -0
  109. package/android/src/main/res/xml/syncadapter.xml +9 -0
  110. package/dist/esm/definitions.d.ts +1052 -0
  111. package/dist/esm/definitions.js +142 -0
  112. package/dist/esm/definitions.js.map +1 -0
  113. package/dist/esm/index.d.ts +8 -0
  114. package/dist/esm/index.js +23 -0
  115. package/dist/esm/index.js.map +1 -0
  116. package/dist/esm/web.d.ts +92 -0
  117. package/dist/esm/web.js +242 -0
  118. package/dist/esm/web.js.map +1 -0
  119. package/dist/plugin.cjs.js +415 -0
  120. package/dist/plugin.cjs.js.map +1 -0
  121. package/dist/plugin.js +418 -0
  122. package/dist/plugin.js.map +1 -0
  123. package/ios/Sources/BackgroundGeolocationPlugin/BackgroundGeolocationPlugin-Bridging-Header.h +18 -0
  124. package/ios/Sources/BackgroundGeolocationPlugin/BackgroundGeolocationPlugin.m +52 -0
  125. package/ios/Sources/BackgroundGeolocationPlugin/BackgroundGeolocationPlugin.swift +750 -0
  126. package/ios/Tests/BackgroundGeolocationPluginTests/BackgroundGeolocationPluginTests.swift +12 -0
  127. package/ios/common/BackgroundGeolocation/CocoaLumberjack.h +1945 -0
  128. package/ios/common/BackgroundGeolocation/CocoaLumberjack.m +5255 -0
  129. package/ios/common/BackgroundGeolocation/FMDB.h +2357 -0
  130. package/ios/common/BackgroundGeolocation/FMDB.m +2672 -0
  131. package/ios/common/BackgroundGeolocation/FMDBLogger.h +42 -0
  132. package/ios/common/BackgroundGeolocation/FMDBLogger.m +264 -0
  133. package/ios/common/BackgroundGeolocation/INTULocationManager/INTUHeadingRequest.h +41 -0
  134. package/ios/common/BackgroundGeolocation/INTULocationManager/INTUHeadingRequest.m +68 -0
  135. package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationManager+Internal.h +33 -0
  136. package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationManager.h +178 -0
  137. package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationManager.m +1025 -0
  138. package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationRequest.h +103 -0
  139. package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationRequest.m +238 -0
  140. package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationRequestDefines.h +163 -0
  141. package/ios/common/BackgroundGeolocation/INTULocationManager/INTURequestIDGenerator.h +39 -0
  142. package/ios/common/BackgroundGeolocation/INTULocationManager/INTURequestIDGenerator.m +37 -0
  143. package/ios/common/BackgroundGeolocation/MAURAbstractLocationProvider.h +51 -0
  144. package/ios/common/BackgroundGeolocation/MAURAbstractLocationProvider.m +53 -0
  145. package/ios/common/BackgroundGeolocation/MAURActivity.h +23 -0
  146. package/ios/common/BackgroundGeolocation/MAURActivity.m +52 -0
  147. package/ios/common/BackgroundGeolocation/MAURActivityLocationProvider.h +18 -0
  148. package/ios/common/BackgroundGeolocation/MAURActivityLocationProvider.m +340 -0
  149. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.h +88 -0
  150. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +1193 -0
  151. package/ios/common/BackgroundGeolocation/MAURBackgroundSync.h +46 -0
  152. package/ios/common/BackgroundGeolocation/MAURBackgroundSync.m +283 -0
  153. package/ios/common/BackgroundGeolocation/MAURBackgroundTaskManager.h +25 -0
  154. package/ios/common/BackgroundGeolocation/MAURBackgroundTaskManager.m +105 -0
  155. package/ios/common/BackgroundGeolocation/MAURConfig.h +99 -0
  156. package/ios/common/BackgroundGeolocation/MAURConfig.m +636 -0
  157. package/ios/common/BackgroundGeolocation/MAURConfigurationContract.h +53 -0
  158. package/ios/common/BackgroundGeolocation/MAURConfigurationContract.m +54 -0
  159. package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.h +20 -0
  160. package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.m +550 -0
  161. package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.h +17 -0
  162. package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.m +124 -0
  163. package/ios/common/BackgroundGeolocation/MAURLocation.h +73 -0
  164. package/ios/common/BackgroundGeolocation/MAURLocation.m +392 -0
  165. package/ios/common/BackgroundGeolocation/MAURLocationContract.h +38 -0
  166. package/ios/common/BackgroundGeolocation/MAURLocationContract.m +39 -0
  167. package/ios/common/BackgroundGeolocation/MAURLocationManager.h +53 -0
  168. package/ios/common/BackgroundGeolocation/MAURLocationManager.m +305 -0
  169. package/ios/common/BackgroundGeolocation/MAURLogReader.h +26 -0
  170. package/ios/common/BackgroundGeolocation/MAURLogReader.m +122 -0
  171. package/ios/common/BackgroundGeolocation/MAURLogging.h +19 -0
  172. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.h +53 -0
  173. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +367 -0
  174. package/ios/common/BackgroundGeolocation/MAURProviderDelegate.h +52 -0
  175. package/ios/common/BackgroundGeolocation/MAURRawLocationProvider.h +18 -0
  176. package/ios/common/BackgroundGeolocation/MAURRawLocationProvider.m +138 -0
  177. package/ios/common/BackgroundGeolocation/MAURSQLiteConfigurationDAO.h +26 -0
  178. package/ios/common/BackgroundGeolocation/MAURSQLiteConfigurationDAO.m +335 -0
  179. package/ios/common/BackgroundGeolocation/MAURSQLiteHelper.h +57 -0
  180. package/ios/common/BackgroundGeolocation/MAURSQLiteHelper.m +93 -0
  181. package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.h +52 -0
  182. package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.m +520 -0
  183. package/ios/common/BackgroundGeolocation/MAURSQLiteOpenHelper.h +32 -0
  184. package/ios/common/BackgroundGeolocation/MAURSQLiteOpenHelper.m +276 -0
  185. package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.h +41 -0
  186. package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.m +137 -0
  187. package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.h +29 -0
  188. package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.m +31 -0
  189. package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.h +25 -0
  190. package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.m +153 -0
  191. package/ios/common/BackgroundGeolocation/MAURUncaughtExceptionLogger.h +20 -0
  192. package/ios/common/BackgroundGeolocation/MAURUncaughtExceptionLogger.m +62 -0
  193. package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.h +31 -0
  194. package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.m +107 -0
  195. package/ios/common/BackgroundGeolocation/Reachability.h +102 -0
  196. package/ios/common/BackgroundGeolocation/Reachability.m +475 -0
  197. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/README.md +170 -0
  198. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/ext/NSString+ZIMString.h +55 -0
  199. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/ext/NSString+ZIMString.m +47 -0
  200. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlDataManipulationCommand.h +27 -0
  201. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlExpression.h +250 -0
  202. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlExpression.m +259 -0
  203. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlSelectStatement.h +360 -0
  204. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlSelectStatement.m +427 -0
  205. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlStatement.h +37 -0
  206. package/ios/common/BackgroundGeolocation/module.modulemap +16 -0
  207. package/package.json +82 -0
@@ -0,0 +1,898 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 JosueLMM
3
+ //
4
+ // Capacitor 8+ bridge for the upstream `com.marianhello.bgloc` native core.
5
+ // The Cordova entry point (com.tenforwardconsulting.bgloc.cordova.BackgroundGeolocationPlugin)
6
+ // is the reference for behavior; this file mirrors its method set with Capacitor idioms.
7
+
8
+ package com.josuelmm.capacitor.backgroundgeolocation;
9
+
10
+ import android.Manifest;
11
+ import android.content.Context;
12
+ import android.content.pm.PackageManager;
13
+ import android.os.Build;
14
+ import android.os.PowerManager;
15
+
16
+ import com.getcapacitor.JSObject;
17
+ import com.getcapacitor.PermissionState;
18
+ import com.getcapacitor.Plugin;
19
+ import com.getcapacitor.PluginCall;
20
+ import com.getcapacitor.PluginMethod;
21
+ import com.getcapacitor.annotation.CapacitorPlugin;
22
+ import com.getcapacitor.annotation.Permission;
23
+ import com.getcapacitor.annotation.PermissionCallback;
24
+
25
+ import com.marianhello.bgloc.BackgroundGeolocationFacade;
26
+ import com.marianhello.bgloc.Config;
27
+ import com.marianhello.bgloc.PluginDelegate;
28
+ import com.marianhello.bgloc.PluginException;
29
+ import com.marianhello.bgloc.data.BackgroundActivity;
30
+ import com.marianhello.bgloc.data.BackgroundLocation;
31
+ import com.marianhello.bgloc.oem.BatteryOemHelper;
32
+ import com.marianhello.logging.LogEntry;
33
+ import com.marianhello.logging.LoggerManager;
34
+
35
+ import org.json.JSONArray;
36
+ import org.json.JSONException;
37
+ import org.json.JSONObject;
38
+
39
+ import java.util.Collection;
40
+ import java.util.concurrent.atomic.AtomicInteger;
41
+
42
+ @CapacitorPlugin(
43
+ name = "BackgroundGeolocation",
44
+ permissions = {
45
+ @Permission(
46
+ alias = "location",
47
+ strings = {
48
+ Manifest.permission.ACCESS_FINE_LOCATION,
49
+ Manifest.permission.ACCESS_COARSE_LOCATION
50
+ }
51
+ ),
52
+ @Permission(
53
+ alias = "backgroundLocation",
54
+ strings = { "android.permission.ACCESS_BACKGROUND_LOCATION" }
55
+ ),
56
+ @Permission(
57
+ alias = "activity",
58
+ strings = { "android.permission.ACTIVITY_RECOGNITION" }
59
+ ),
60
+ @Permission(
61
+ alias = "notifications",
62
+ strings = { "android.permission.POST_NOTIFICATIONS" }
63
+ )
64
+ }
65
+ )
66
+ public class BackgroundGeolocationPlugin extends Plugin implements PluginDelegate {
67
+
68
+ private static final String LOCATION_EVENT = "location";
69
+ private static final String STATIONARY_EVENT = "stationary";
70
+ private static final String ACTIVITY_EVENT = "activity";
71
+ private static final String FOREGROUND_EVENT = "foreground";
72
+ private static final String BACKGROUND_EVENT = "background";
73
+ private static final String AUTHORIZATION_EVENT = "authorization";
74
+ private static final String START_EVENT = "start";
75
+ private static final String STOP_EVENT = "stop";
76
+ private static final String ABORT_REQUESTED_EVENT = "abort_requested";
77
+ private static final String HTTP_AUTHORIZATION_EVENT = "http_authorization";
78
+ private static final String ERROR_EVENT = "error";
79
+
80
+ public static final String PLUGIN_VERSION = "1.0.0";
81
+
82
+ private BackgroundGeolocationFacade facade;
83
+ private org.slf4j.Logger logger;
84
+ private final AtomicInteger taskCounter = new AtomicInteger(0);
85
+
86
+ @Override
87
+ public void load() {
88
+ super.load();
89
+ logger = LoggerManager.getLogger(BackgroundGeolocationPlugin.class);
90
+ facade = new BackgroundGeolocationFacade(getAppContext(), this);
91
+ facade.resume();
92
+ }
93
+
94
+ @Override
95
+ protected void handleOnPause() {
96
+ if (facade != null) facade.pause();
97
+ notifyListeners(BACKGROUND_EVENT, new JSObject());
98
+ super.handleOnPause();
99
+ }
100
+
101
+ @Override
102
+ protected void handleOnResume() {
103
+ if (facade != null) facade.resume();
104
+ notifyListeners(FOREGROUND_EVENT, new JSObject());
105
+ super.handleOnResume();
106
+ }
107
+
108
+ @Override
109
+ protected void handleOnDestroy() {
110
+ if (facade != null) facade.destroy();
111
+ super.handleOnDestroy();
112
+ }
113
+
114
+ private Context getAppContext() {
115
+ return getBridge().getActivity().getApplicationContext();
116
+ }
117
+
118
+ private void rejectWith(PluginCall call, String message, Exception cause, int code) {
119
+ JSObject details = new JSObject();
120
+ details.put("code", code);
121
+ if (cause != null && cause.getMessage() != null) {
122
+ details.put("cause", cause.getMessage());
123
+ }
124
+ call.reject(message, String.valueOf(code), cause, details);
125
+ }
126
+
127
+ private void rejectWith(PluginCall call, PluginException e) {
128
+ JSObject details = new JSObject();
129
+ details.put("code", e.getCode());
130
+ call.reject(e.getMessage() != null ? e.getMessage() : "Plugin error",
131
+ String.valueOf(e.getCode()), e, details);
132
+ }
133
+
134
+ @PluginMethod
135
+ public void configure(PluginCall call) {
136
+ try {
137
+ JSObject data = call.getData();
138
+ Config config = ConfigMapper.fromJSONObject(data);
139
+ facade.configure(config);
140
+ call.resolve();
141
+ } catch (JSONException e) {
142
+ rejectWith(call, "Configuration error", e, PluginException.JSON_ERROR);
143
+ } catch (PluginException e) {
144
+ rejectWith(call, e);
145
+ }
146
+ }
147
+
148
+ @PluginMethod
149
+ public void start(PluginCall call) {
150
+ if (getPermissionState("location") != PermissionState.GRANTED) {
151
+ requestPermissionForAlias("location", call, "startAfterPermission");
152
+ return;
153
+ }
154
+ facade.start();
155
+ call.resolve();
156
+ }
157
+
158
+ @PermissionCallback
159
+ private void startAfterPermission(PluginCall call) {
160
+ if (getPermissionState("location") != PermissionState.GRANTED) {
161
+ rejectWith(call, "Location permission denied", null, PluginException.PERMISSION_DENIED_ERROR);
162
+ return;
163
+ }
164
+ facade.start();
165
+ call.resolve();
166
+ }
167
+
168
+ @PluginMethod
169
+ public void stop(PluginCall call) {
170
+ facade.stop();
171
+ call.resolve();
172
+ }
173
+
174
+ @PluginMethod
175
+ public void getCurrentLocation(PluginCall call) {
176
+ int timeout = call.getInt("timeout", Integer.MAX_VALUE);
177
+ long maximumAge = call.getLong("maximumAge", Long.MAX_VALUE);
178
+ boolean enableHighAccuracy = Boolean.TRUE.equals(call.getBoolean("enableHighAccuracy", false));
179
+ try {
180
+ BackgroundLocation location = facade.getCurrentLocation(timeout, maximumAge, enableHighAccuracy);
181
+ call.resolve(JSObject.fromJSONObject(location.toJSONObject()));
182
+ } catch (JSONException e) {
183
+ rejectWith(call, "Error serializing location", e, PluginException.JSON_ERROR);
184
+ } catch (PluginException e) {
185
+ rejectWith(call, e);
186
+ }
187
+ }
188
+
189
+ @PluginMethod
190
+ public void getStationaryLocation(PluginCall call) {
191
+ try {
192
+ BackgroundLocation stationary = facade.getStationaryLocation();
193
+ if (stationary != null) {
194
+ call.resolve(JSObject.fromJSONObject(stationary.toJSONObject()));
195
+ } else {
196
+ call.resolve();
197
+ }
198
+ } catch (JSONException e) {
199
+ rejectWith(call, "Getting stationary location failed", e, PluginException.JSON_ERROR);
200
+ }
201
+ }
202
+
203
+ @PluginMethod
204
+ public void getLocations(PluginCall call) {
205
+ try {
206
+ JSObject ret = new JSObject();
207
+ ret.put("locations", locationsToJsonArray(facade.getLocations()));
208
+ call.resolve(ret);
209
+ } catch (JSONException e) {
210
+ rejectWith(call, "Converting locations to JSON failed", e, PluginException.JSON_ERROR);
211
+ }
212
+ }
213
+
214
+ @PluginMethod
215
+ public void getValidLocations(PluginCall call) {
216
+ try {
217
+ JSObject ret = new JSObject();
218
+ ret.put("locations", locationsToJsonArray(facade.getValidLocations()));
219
+ call.resolve(ret);
220
+ } catch (JSONException e) {
221
+ rejectWith(call, "Converting locations to JSON failed", e, PluginException.JSON_ERROR);
222
+ }
223
+ }
224
+
225
+ @PluginMethod
226
+ public void getValidLocationsAndDelete(PluginCall call) {
227
+ try {
228
+ JSObject ret = new JSObject();
229
+ ret.put("locations", locationsToJsonArray(facade.getValidLocationsAndDelete()));
230
+ call.resolve(ret);
231
+ } catch (JSONException e) {
232
+ rejectWith(call, "Converting locations to JSON failed", e, PluginException.JSON_ERROR);
233
+ }
234
+ }
235
+
236
+ @PluginMethod
237
+ public void getConfig(PluginCall call) {
238
+ try {
239
+ Config config = facade.getConfig();
240
+ call.resolve(ConfigMapper.toJSObject(config));
241
+ } catch (JSONException e) {
242
+ rejectWith(call, "Error getting config", e, PluginException.JSON_ERROR);
243
+ }
244
+ }
245
+
246
+ @PluginMethod
247
+ public void deleteLocation(PluginCall call) {
248
+ Long locationId = call.getLong("locationId");
249
+ if (locationId == null) {
250
+ rejectWith(call, "locationId is required", null, PluginException.JSON_ERROR);
251
+ return;
252
+ }
253
+ facade.deleteLocation(locationId);
254
+ call.resolve();
255
+ }
256
+
257
+ @PluginMethod
258
+ public void deleteAllLocations(PluginCall call) {
259
+ facade.deleteAllLocations();
260
+ call.resolve();
261
+ }
262
+
263
+ @PluginMethod
264
+ public void isLocationEnabled(PluginCall call) {
265
+ try {
266
+ JSObject ret = new JSObject();
267
+ ret.put("enabled", facade.locationServicesEnabled());
268
+ call.resolve(ret);
269
+ } catch (PluginException e) {
270
+ rejectWith(call, e);
271
+ }
272
+ }
273
+
274
+ @PluginMethod
275
+ public void showAppSettings(PluginCall call) {
276
+ BackgroundGeolocationFacade.showAppSettings(getAppContext());
277
+ call.resolve();
278
+ }
279
+
280
+ @PluginMethod
281
+ public void openSettings(PluginCall call) {
282
+ BackgroundGeolocationFacade.showAppSettings(getAppContext());
283
+ call.resolve();
284
+ }
285
+
286
+ @PluginMethod
287
+ public void showLocationSettings(PluginCall call) {
288
+ BackgroundGeolocationFacade.showLocationSettings(getAppContext());
289
+ call.resolve();
290
+ }
291
+
292
+ @PluginMethod
293
+ public void watchLocationMode(PluginCall call) {
294
+ call.resolve();
295
+ }
296
+
297
+ @PluginMethod
298
+ public void stopWatchingLocationMode(PluginCall call) {
299
+ call.resolve();
300
+ }
301
+
302
+ @PluginMethod
303
+ public void getLogEntries(PluginCall call) {
304
+ try {
305
+ int limit = call.getInt("limit", 100);
306
+ int offset = call.getInt("fromId", 0);
307
+ String minLevel = call.getString("minLevel", "DEBUG");
308
+ JSObject ret = new JSObject();
309
+ ret.put("entries", logsToJsonArray(limit, offset, minLevel));
310
+ call.resolve(ret);
311
+ } catch (Exception e) {
312
+ rejectWith(call, "Getting logs failed", e, PluginException.SERVICE_ERROR);
313
+ }
314
+ }
315
+
316
+ @PluginMethod
317
+ public void checkStatus(PluginCall call) {
318
+ try {
319
+ JSObject ret = new JSObject();
320
+ ret.put("isRunning", facade.isRunning());
321
+ ret.put("locationServicesEnabled", facade.locationServicesEnabled());
322
+ ret.put("authorization", facade.getAuthorizationStatus());
323
+ ret.put("hasPermissions", facade.hasPermissions());
324
+ call.resolve(ret);
325
+ } catch (PluginException e) {
326
+ rejectWith(call, e);
327
+ }
328
+ }
329
+
330
+ @PluginMethod
331
+ public void startTask(PluginCall call) {
332
+ JSObject ret = new JSObject();
333
+ ret.put("taskKey", taskCounter.incrementAndGet());
334
+ call.resolve(ret);
335
+ }
336
+
337
+ @PluginMethod
338
+ public void endTask(PluginCall call) {
339
+ call.resolve();
340
+ }
341
+
342
+ @PluginMethod
343
+ public void forceSync(PluginCall call) {
344
+ facade.forceSync();
345
+ call.resolve();
346
+ }
347
+
348
+ @PluginMethod
349
+ public void clearSync(PluginCall call) {
350
+ facade.clearSync();
351
+ call.resolve();
352
+ }
353
+
354
+ @PluginMethod
355
+ public void getPendingSyncCount(PluginCall call) {
356
+ try {
357
+ long count = facade.getPendingSyncCount();
358
+ JSObject ret = new JSObject();
359
+ ret.put("count", (int) Math.min(count, Integer.MAX_VALUE));
360
+ call.resolve(ret);
361
+ } catch (Exception e) {
362
+ rejectWith(call, "getPendingSyncCount failed", e, PluginException.SERVICE_ERROR);
363
+ }
364
+ }
365
+
366
+ @PluginMethod
367
+ public void switchMode(PluginCall call) {
368
+ Integer mode = call.getInt("mode");
369
+ if (mode == null) {
370
+ rejectWith(call, "mode is required", null, PluginException.JSON_ERROR);
371
+ return;
372
+ }
373
+ facade.switchMode(mode);
374
+ call.resolve();
375
+ }
376
+
377
+ @PluginMethod
378
+ public void startSession(PluginCall call) {
379
+ facade.startSession();
380
+ call.resolve();
381
+ }
382
+
383
+ @PluginMethod
384
+ public void clearSession(PluginCall call) {
385
+ facade.clearSession();
386
+ call.resolve();
387
+ }
388
+
389
+ @PluginMethod
390
+ public void getSessionLocations(PluginCall call) {
391
+ try {
392
+ JSObject ret = new JSObject();
393
+ ret.put("locations", locationsToJsonArray(facade.getSessionLocations()));
394
+ call.resolve(ret);
395
+ } catch (JSONException e) {
396
+ rejectWith(call, "getSessionLocations failed", e, PluginException.JSON_ERROR);
397
+ }
398
+ }
399
+
400
+ @PluginMethod
401
+ public void getSessionLocationsCount(PluginCall call) {
402
+ try {
403
+ int count = facade.getSessionLocationsCount();
404
+ JSObject ret = new JSObject();
405
+ ret.put("count", count);
406
+ call.resolve(ret);
407
+ } catch (Exception e) {
408
+ rejectWith(call, "getSessionLocationsCount failed", e, PluginException.SERVICE_ERROR);
409
+ }
410
+ }
411
+
412
+ @PluginMethod
413
+ public void getPluginVersion(PluginCall call) {
414
+ JSObject ret = new JSObject();
415
+ ret.put("version", PLUGIN_VERSION);
416
+ call.resolve(ret);
417
+ }
418
+
419
+ @PluginMethod
420
+ public void getDiagnostics(PluginCall call) {
421
+ try {
422
+ Context ctx = getAppContext();
423
+ JSObject d = new JSObject();
424
+ d.put("isRunning", facade.isRunning());
425
+ try {
426
+ d.put("locationServicesEnabled", facade.locationServicesEnabled());
427
+ } catch (PluginException e) {
428
+ d.put("locationServicesEnabled", JSONObject.NULL);
429
+ }
430
+ try {
431
+ Config cfg = facade.getConfig();
432
+ if (cfg != null) {
433
+ d.put("startOnBoot", cfg.getStartOnBoot());
434
+ }
435
+ } catch (Exception ignored) {}
436
+ try {
437
+ d.put("pendingSyncCount", (int) Math.min(facade.getPendingSyncCount(), Integer.MAX_VALUE));
438
+ } catch (Exception ignored) {}
439
+ try {
440
+ BackgroundLocation last = facade.getStationaryLocation();
441
+ d.put("lastLocationAt", last != null ? last.getTime() : JSONObject.NULL);
442
+ } catch (Exception e) {
443
+ d.put("lastLocationAt", JSONObject.NULL);
444
+ }
445
+
446
+ d.put("fineLocationGranted", hasPermission(ctx, Manifest.permission.ACCESS_FINE_LOCATION));
447
+ d.put("coarseLocationGranted", hasPermission(ctx, Manifest.permission.ACCESS_COARSE_LOCATION));
448
+ if (Build.VERSION.SDK_INT >= 29) {
449
+ d.put("backgroundLocationGranted", hasPermission(ctx, "android.permission.ACCESS_BACKGROUND_LOCATION"));
450
+ d.put("activityRecognitionGranted", hasPermission(ctx, "android.permission.ACTIVITY_RECOGNITION"));
451
+ } else {
452
+ d.put("backgroundLocationGranted", true);
453
+ d.put("activityRecognitionGranted", true);
454
+ }
455
+ if (Build.VERSION.SDK_INT >= 33) {
456
+ d.put("notificationPermissionGranted", hasPermission(ctx, "android.permission.POST_NOTIFICATIONS"));
457
+ } else {
458
+ d.put("notificationPermissionGranted", true);
459
+ }
460
+
461
+ d.put("batteryOptimizationIgnored", isIgnoringBatteryOptimizations(ctx));
462
+ d.put("manufacturer", Build.MANUFACTURER != null ? Build.MANUFACTURER : "");
463
+ d.put("foregroundServiceType", readForegroundServiceTypeFromManifest(ctx));
464
+
465
+ call.resolve(d);
466
+ } catch (Exception e) {
467
+ rejectWith(call, "getDiagnostics failed", e, PluginException.SERVICE_ERROR);
468
+ }
469
+ }
470
+
471
+ @PluginMethod
472
+ public void isIgnoringBatteryOptimizations(PluginCall call) {
473
+ JSObject ret = new JSObject();
474
+ ret.put("whitelisted", BatteryOemHelper.isIgnoringBatteryOptimizations(getAppContext()));
475
+ call.resolve(ret);
476
+ }
477
+
478
+ @PluginMethod
479
+ public void requestIgnoreBatteryOptimizations(PluginCall call) {
480
+ try {
481
+ BatteryOemHelper.requestIgnoreBatteryOptimizations(getActivity());
482
+ } catch (Exception ignored) {}
483
+ JSObject ret = new JSObject();
484
+ ret.put("whitelisted", BatteryOemHelper.isIgnoringBatteryOptimizations(getAppContext()));
485
+ call.resolve(ret);
486
+ }
487
+
488
+ @PluginMethod
489
+ public void openBatterySettings(PluginCall call) {
490
+ try {
491
+ BatteryOemHelper.openBatterySettings(getActivity());
492
+ } catch (Exception ignored) {}
493
+ call.resolve();
494
+ }
495
+
496
+ @PluginMethod
497
+ public void openAutoStartSettings(PluginCall call) {
498
+ try {
499
+ JSONObject result = BatteryOemHelper.openAutoStartSettings(getActivity());
500
+ call.resolve(JSObject.fromJSONObject(result));
501
+ } catch (Exception e) {
502
+ rejectWith(call, "openAutoStartSettings failed", e, PluginException.SERVICE_ERROR);
503
+ }
504
+ }
505
+
506
+ @PluginMethod
507
+ public void getManufacturerHelp(PluginCall call) {
508
+ try {
509
+ JSONObject result = BatteryOemHelper.getManufacturerHelp();
510
+ call.resolve(JSObject.fromJSONObject(result));
511
+ } catch (Exception e) {
512
+ rejectWith(call, "getManufacturerHelp failed", e, PluginException.SERVICE_ERROR);
513
+ }
514
+ }
515
+
516
+ @PluginMethod
517
+ public void triggerSOS(PluginCall call) {
518
+ try {
519
+ JSObject payload = call.getObject("payload");
520
+ facade.triggerSOS(payload);
521
+ call.resolve();
522
+ } catch (Exception e) {
523
+ rejectWith(call, "triggerSOS failed", e, PluginException.SERVICE_ERROR);
524
+ }
525
+ }
526
+
527
+ @PluginMethod
528
+ public void requestBackgroundLocationPermission(PluginCall call) {
529
+ if (Build.VERSION.SDK_INT < 29) {
530
+ JSObject ret = new JSObject();
531
+ ret.put("granted", true);
532
+ ret.put("notRequired", true);
533
+ call.resolve(ret);
534
+ return;
535
+ }
536
+ if (getPermissionState("backgroundLocation") == PermissionState.GRANTED) {
537
+ JSObject ret = new JSObject();
538
+ ret.put("granted", true);
539
+ call.resolve(ret);
540
+ return;
541
+ }
542
+ requestPermissionForAlias("backgroundLocation", call, "backgroundLocationPermissionCallback");
543
+ }
544
+
545
+ @PermissionCallback
546
+ private void backgroundLocationPermissionCallback(PluginCall call) {
547
+ boolean granted = getPermissionState("backgroundLocation") == PermissionState.GRANTED;
548
+ JSObject ret = new JSObject();
549
+ ret.put("granted", granted);
550
+ if (!granted) {
551
+ JSONArray denied = new JSONArray();
552
+ denied.put("android.permission.ACCESS_BACKGROUND_LOCATION");
553
+ ret.put("denied", denied);
554
+ }
555
+ call.resolve(ret);
556
+ }
557
+
558
+ @PluginMethod
559
+ public void requestActivityRecognitionPermission(PluginCall call) {
560
+ if (Build.VERSION.SDK_INT < 29) {
561
+ JSObject ret = new JSObject();
562
+ ret.put("granted", true);
563
+ ret.put("notRequired", true);
564
+ call.resolve(ret);
565
+ return;
566
+ }
567
+ if (getPermissionState("activity") == PermissionState.GRANTED) {
568
+ JSObject ret = new JSObject();
569
+ ret.put("granted", true);
570
+ call.resolve(ret);
571
+ return;
572
+ }
573
+ requestPermissionForAlias("activity", call, "activityPermissionCallback");
574
+ }
575
+
576
+ @PermissionCallback
577
+ private void activityPermissionCallback(PluginCall call) {
578
+ boolean granted = getPermissionState("activity") == PermissionState.GRANTED;
579
+ JSObject ret = new JSObject();
580
+ ret.put("granted", granted);
581
+ if (!granted) {
582
+ JSONArray denied = new JSONArray();
583
+ denied.put("android.permission.ACTIVITY_RECOGNITION");
584
+ ret.put("denied", denied);
585
+ }
586
+ call.resolve(ret);
587
+ }
588
+
589
+ @PluginMethod
590
+ public void requestNotificationPermission(PluginCall call) {
591
+ if (Build.VERSION.SDK_INT < 33) {
592
+ JSObject ret = new JSObject();
593
+ ret.put("granted", true);
594
+ ret.put("notRequired", true);
595
+ call.resolve(ret);
596
+ return;
597
+ }
598
+ if (getPermissionState("notifications") == PermissionState.GRANTED) {
599
+ JSObject ret = new JSObject();
600
+ ret.put("granted", true);
601
+ call.resolve(ret);
602
+ return;
603
+ }
604
+ requestPermissionForAlias("notifications", call, "notificationsPermissionCallback");
605
+ }
606
+
607
+ @PermissionCallback
608
+ private void notificationsPermissionCallback(PluginCall call) {
609
+ boolean granted = getPermissionState("notifications") == PermissionState.GRANTED;
610
+ JSObject ret = new JSObject();
611
+ ret.put("granted", granted);
612
+ if (!granted) {
613
+ JSONArray denied = new JSONArray();
614
+ denied.put("android.permission.POST_NOTIFICATIONS");
615
+ ret.put("denied", denied);
616
+ }
617
+ call.resolve(ret);
618
+ }
619
+
620
+ @PluginMethod
621
+ public void registerHeadlessTask(PluginCall call) {
622
+ String taskJs = call.getString("task");
623
+ if (taskJs == null || taskJs.length() == 0) {
624
+ rejectWith(call, "task body is required", null, PluginException.JSON_ERROR);
625
+ return;
626
+ }
627
+ HeadlessTaskRegistry.getInstance().registerHeadlessTask(taskJs);
628
+ facade.registerHeadlessTask(JsEvaluatorTaskRunner.class.getName());
629
+ call.resolve();
630
+ }
631
+
632
+ @Override
633
+ @PluginMethod
634
+ public void removeAllListeners(PluginCall call) {
635
+ super.removeAllListeners(call);
636
+ }
637
+
638
+ @Override
639
+ public void onAuthorizationChanged(int authStatus) {
640
+ JSObject payload = new JSObject();
641
+ payload.put("status", authStatus);
642
+ notifyListeners(AUTHORIZATION_EVENT, payload);
643
+ }
644
+
645
+ @Override
646
+ public void onLocationChanged(BackgroundLocation location) {
647
+ try {
648
+ notifyListeners(LOCATION_EVENT, JSObject.fromJSONObject(location.toJSONObjectWithId()));
649
+ } catch (JSONException e) {
650
+ emitError(new PluginException(e.getMessage(), PluginException.JSON_ERROR));
651
+ }
652
+ }
653
+
654
+ @Override
655
+ public void onStationaryChanged(BackgroundLocation location) {
656
+ try {
657
+ notifyListeners(STATIONARY_EVENT, JSObject.fromJSONObject(location.toJSONObjectWithId()));
658
+ } catch (JSONException e) {
659
+ emitError(new PluginException(e.getMessage(), PluginException.JSON_ERROR));
660
+ }
661
+ }
662
+
663
+ @Override
664
+ public void onActivityChanged(BackgroundActivity activity) {
665
+ try {
666
+ notifyListeners(ACTIVITY_EVENT, JSObject.fromJSONObject(activity.toJSONObject()));
667
+ } catch (JSONException e) {
668
+ emitError(new PluginException(e.getMessage(), PluginException.JSON_ERROR));
669
+ }
670
+ }
671
+
672
+ @Override
673
+ public void onServiceStatusChanged(int status) {
674
+ switch (status) {
675
+ case BackgroundGeolocationFacade.SERVICE_STARTED:
676
+ notifyListeners(START_EVENT, new JSObject());
677
+ return;
678
+ case BackgroundGeolocationFacade.SERVICE_STOPPED:
679
+ notifyListeners(STOP_EVENT, new JSObject());
680
+ return;
681
+ }
682
+ }
683
+
684
+ @Override
685
+ public void onAbortRequested() {
686
+ notifyListeners(ABORT_REQUESTED_EVENT, new JSObject());
687
+ }
688
+
689
+ @Override
690
+ public void onHttpAuthorization() {
691
+ notifyListeners(HTTP_AUTHORIZATION_EVENT, new JSObject());
692
+ }
693
+
694
+ @Override
695
+ public void onError(PluginException e) {
696
+ emitError(e);
697
+ }
698
+
699
+ // v3.5 Phase 4 — sync queue events
700
+ @Override
701
+ public void onSyncStart() {
702
+ notifyListeners("syncStart", new JSObject());
703
+ }
704
+
705
+ @Override
706
+ public void onSyncSuccess(int locationsSent) {
707
+ JSObject p = new JSObject();
708
+ p.put("sent", locationsSent);
709
+ notifyListeners("syncSuccess", p);
710
+ }
711
+
712
+ @Override
713
+ public void onSyncError(int httpStatus, String message) {
714
+ JSObject p = new JSObject();
715
+ p.put("httpStatus", httpStatus);
716
+ p.put("message", message != null ? message : "");
717
+ notifyListeners("syncError", p);
718
+ }
719
+
720
+ @Override
721
+ public void onSyncProgress(int progress) {
722
+ JSObject p = new JSObject();
723
+ p.put("progress", progress);
724
+ notifyListeners("syncProgress", p);
725
+ }
726
+
727
+ @Override
728
+ public void onHeartbeat(BackgroundLocation location) {
729
+ notifyListeners("heartbeat", locationToJSObject(location));
730
+ }
731
+
732
+ // v4.0 Phase 6 — driver-insight events
733
+ @Override
734
+ public void onTripStart(BackgroundLocation location) {
735
+ notifyListeners("tripStart", locationToJSObject(location));
736
+ }
737
+
738
+ @Override
739
+ public void onTripEnd(BackgroundLocation location, double distance, long durationMs) {
740
+ JSObject p = new JSObject();
741
+ p.put("location", locationJsonOrNull(location));
742
+ p.put("distance", distance);
743
+ p.put("durationMs", durationMs);
744
+ notifyListeners("tripEnd", p);
745
+ }
746
+
747
+ @Override
748
+ public void onMoving(BackgroundLocation location) {
749
+ notifyListeners("moving", locationToJSObject(location));
750
+ }
751
+
752
+ @Override
753
+ public void onStopped(BackgroundLocation location) {
754
+ notifyListeners("stopped", locationToJSObject(location));
755
+ }
756
+
757
+ @Override
758
+ public void onSpeeding(BackgroundLocation location, double speedKmh, double limitKmh) {
759
+ JSObject p = new JSObject();
760
+ p.put("location", locationJsonOrNull(location));
761
+ p.put("speedKmh", speedKmh);
762
+ p.put("limitKmh", limitKmh);
763
+ notifyListeners("speeding", p);
764
+ }
765
+
766
+ @Override
767
+ public void onProviderChange(String provider) {
768
+ JSObject p = new JSObject();
769
+ p.put("provider", provider != null ? provider : "");
770
+ notifyListeners("providerChange", p);
771
+ }
772
+
773
+ @Override
774
+ public void onSOS(BackgroundLocation location, JSONObject userPayload) {
775
+ try {
776
+ JSObject p = userPayload != null ? JSObject.fromJSONObject(userPayload) : new JSObject();
777
+ p.put("location", locationJsonOrNull(location));
778
+ notifyListeners("sos", p);
779
+ } catch (JSONException e) {
780
+ JSObject p = new JSObject();
781
+ p.put("location", locationJsonOrNull(location));
782
+ notifyListeners("sos", p);
783
+ }
784
+ }
785
+
786
+ // v4.1 GPS-derived sensor-like events
787
+ @Override
788
+ public void onHardBrake(BackgroundLocation location, double decelMps2) {
789
+ emitDrivingEvent("hardBrake", location, decelMps2);
790
+ }
791
+
792
+ @Override
793
+ public void onRapidAcceleration(BackgroundLocation location, double accelMps2) {
794
+ emitDrivingEvent("rapidAcceleration", location, accelMps2);
795
+ }
796
+
797
+ @Override
798
+ public void onSharpTurn(BackgroundLocation location, double degPerSec) {
799
+ emitDrivingEvent("sharpTurn", location, degPerSec);
800
+ }
801
+
802
+ @Override
803
+ public void onPossibleCrash(BackgroundLocation location, double value, String source) {
804
+ JSObject p = new JSObject();
805
+ p.put("location", locationJsonOrNull(location));
806
+ p.put("value", value);
807
+ p.put("source", source != null ? source : "gps");
808
+ notifyListeners("possibleCrash", p);
809
+ }
810
+
811
+ @Override
812
+ public void onPhoneUsageWhileDriving(BackgroundLocation location) {
813
+ notifyListeners("phoneUsageWhileDriving", locationToJSObject(location));
814
+ }
815
+
816
+ private void emitDrivingEvent(String name, BackgroundLocation location, double value) {
817
+ JSObject p = new JSObject();
818
+ p.put("location", locationJsonOrNull(location));
819
+ p.put("value", value);
820
+ notifyListeners(name, p);
821
+ }
822
+
823
+ private void emitError(PluginException e) {
824
+ JSObject payload = new JSObject();
825
+ payload.put("code", e.getCode());
826
+ payload.put("message", e.getMessage());
827
+ notifyListeners(ERROR_EVENT, payload);
828
+ }
829
+
830
+ private static JSONArray locationsToJsonArray(Collection<BackgroundLocation> locations) throws JSONException {
831
+ JSONArray arr = new JSONArray();
832
+ for (BackgroundLocation location : locations) {
833
+ arr.put(location.toJSONObjectWithId());
834
+ }
835
+ return arr;
836
+ }
837
+
838
+ private JSONArray logsToJsonArray(int limit, int offset, String minLevel) throws Exception {
839
+ JSONArray arr = new JSONArray();
840
+ Collection<LogEntry> entries = facade.getLogEntries(limit, offset, minLevel);
841
+ for (LogEntry entry : entries) {
842
+ arr.put(entry.toJSONObject());
843
+ }
844
+ return arr;
845
+ }
846
+
847
+ private static JSObject locationToJSObject(BackgroundLocation location) {
848
+ if (location == null) return new JSObject();
849
+ try {
850
+ return JSObject.fromJSONObject(location.toJSONObjectWithId());
851
+ } catch (JSONException e) {
852
+ return new JSObject();
853
+ }
854
+ }
855
+
856
+ private static Object locationJsonOrNull(BackgroundLocation location) {
857
+ if (location == null) return JSONObject.NULL;
858
+ try {
859
+ return location.toJSONObjectWithId();
860
+ } catch (JSONException e) {
861
+ return JSONObject.NULL;
862
+ }
863
+ }
864
+
865
+ private static boolean hasPermission(Context ctx, String permission) {
866
+ try {
867
+ return ctx.getPackageManager().checkPermission(permission, ctx.getPackageName())
868
+ == PackageManager.PERMISSION_GRANTED;
869
+ } catch (Exception e) {
870
+ return false;
871
+ }
872
+ }
873
+
874
+ private static boolean isIgnoringBatteryOptimizations(Context ctx) {
875
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true;
876
+ try {
877
+ PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
878
+ return pm != null && pm.isIgnoringBatteryOptimizations(ctx.getPackageName());
879
+ } catch (Exception e) {
880
+ return false;
881
+ }
882
+ }
883
+
884
+ private static int readForegroundServiceTypeFromManifest(Context ctx) {
885
+ if (Build.VERSION.SDK_INT < 34) return 0;
886
+ try {
887
+ android.content.ComponentName cn = new android.content.ComponentName(
888
+ ctx, com.marianhello.bgloc.service.LocationServiceImpl.class);
889
+ android.content.pm.ServiceInfo si = ctx.getPackageManager().getServiceInfo(
890
+ cn, android.content.pm.PackageManager.ComponentInfoFlags.of(0));
891
+ java.lang.reflect.Field f = android.content.pm.ServiceInfo.class.getField("foregroundServiceType");
892
+ Object v = f.get(si);
893
+ return (v instanceof Integer) ? (Integer) v : 0;
894
+ } catch (Throwable e) {
895
+ return 0;
896
+ }
897
+ }
898
+ }