@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,385 @@
1
+ package com.marianhello.bgloc.provider;
2
+
3
+ import android.app.PendingIntent;
4
+ import android.content.BroadcastReceiver;
5
+ import android.content.Context;
6
+ import android.content.Intent;
7
+ import android.content.IntentFilter;
8
+ import android.content.pm.PackageManager;
9
+ import android.location.Location;
10
+ import android.Manifest;
11
+ import android.os.Build;
12
+ import android.os.Looper;
13
+ import androidx.core.app.ActivityCompat;
14
+
15
+ import com.google.android.gms.common.ConnectionResult;
16
+ import com.google.android.gms.common.GoogleApiAvailability;
17
+ import com.google.android.gms.location.ActivityRecognition;
18
+ import com.google.android.gms.location.ActivityRecognitionClient;
19
+ import com.google.android.gms.location.ActivityRecognitionResult;
20
+ import com.google.android.gms.location.DetectedActivity;
21
+ import com.google.android.gms.location.FusedLocationProviderClient;
22
+ import com.google.android.gms.location.LocationCallback;
23
+ import com.google.android.gms.location.LocationRequest;
24
+ import com.google.android.gms.location.LocationResult;
25
+ import com.google.android.gms.location.LocationServices;
26
+ import com.google.android.gms.location.Priority;
27
+ import com.marianhello.bgloc.Config;
28
+ import com.marianhello.bgloc.data.BackgroundActivity;
29
+
30
+ import java.util.List;
31
+
32
+
33
+ public class ActivityRecognitionLocationProvider extends AbstractLocationProvider {
34
+
35
+ private static final String P_NAME = "com.marianhello.bgloc";
36
+ private static final String DETECTED_ACTIVITY_UPDATE = P_NAME + ".DETECTED_ACTIVITY_UPDATE";
37
+
38
+ private FusedLocationProviderClient fusedLocationClient;
39
+ private ActivityRecognitionClient activityRecognitionClient;
40
+ private PendingIntent detectedActivitiesPI;
41
+
42
+ // Must default to false: onConfigure() is called before onStart() by LocationServiceImpl.
43
+ private boolean isStarted = false;
44
+ private boolean isTracking = false;
45
+ private boolean isWatchingActivity = false;
46
+ private boolean playServicesAvailable = false;
47
+ private boolean activityPermissionErrorEmitted = false;
48
+ private boolean stopOnStillWarningEmitted = false;
49
+ private DetectedActivity lastActivity = new DetectedActivity(DetectedActivity.UNKNOWN, 100);
50
+
51
+ // v4.5.2: snapshot of fields that require restarting tracking when they change.
52
+ private Integer prevDesiredAccuracy;
53
+ private Integer prevInterval;
54
+ private Integer prevFastestInterval;
55
+ private Integer prevDistanceFilter;
56
+ private Integer prevActivitiesInterval;
57
+ private Boolean prevStopOnStillActivity;
58
+
59
+ private final LocationCallback locationCallback = new LocationCallback() {
60
+ @Override
61
+ public void onLocationResult(LocationResult result) {
62
+ if (result == null) return;
63
+ List<Location> locations = result.getLocations();
64
+ if (locations == null) return;
65
+ for (Location location : locations) {
66
+ onLocationReceived(location);
67
+ }
68
+ }
69
+ };
70
+
71
+ public ActivityRecognitionLocationProvider(Context context) {
72
+ super(context);
73
+ PROVIDER_ID = Config.ACTIVITY_PROVIDER;
74
+ }
75
+
76
+ @Override
77
+ public void onCreate() {
78
+ super.onCreate();
79
+
80
+ // v4.5.2: ACTIVITY_PROVIDER strictly depends on Google Play Services
81
+ // (FusedLocationProviderClient + ActivityRecognitionClient). If GPS is
82
+ // missing/outdated we cannot operate — surface a clear error instead of
83
+ // silently failing.
84
+ int gps = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(mContext);
85
+ playServicesAvailable = (gps == ConnectionResult.SUCCESS);
86
+ if (!playServicesAvailable) {
87
+ String msg = "Google Play Services unavailable for ACTIVITY_PROVIDER (code=" + gps + "); provider will be inert.";
88
+ logger.error(msg);
89
+ handleServiceError(msg);
90
+ return;
91
+ }
92
+
93
+ fusedLocationClient = LocationServices.getFusedLocationProviderClient(mContext);
94
+ activityRecognitionClient = ActivityRecognition.getClient(mContext);
95
+
96
+ Intent detectedActivitiesIntent = new Intent(mContext, DetectedActivitiesReceiver.class);
97
+ detectedActivitiesIntent.setAction(DETECTED_ACTIVITY_UPDATE);
98
+
99
+ int updateCurrentFlag = android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
100
+ ? PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
101
+ : PendingIntent.FLAG_UPDATE_CURRENT;
102
+ detectedActivitiesPI = PendingIntent.getBroadcast(mContext, 9002, detectedActivitiesIntent, updateCurrentFlag);
103
+ registerReceiver(detectedActivitiesReceiver, new IntentFilter(DETECTED_ACTIVITY_UPDATE));
104
+ }
105
+
106
+ @Override
107
+ public void onStart() {
108
+ logger.info("Start recording");
109
+ this.isStarted = true;
110
+
111
+ // v4.5.2: ACTIVITY_PROVIDER hinges on the STILL/ACTIVE state machine.
112
+ // If the host turned that off, the provider degenerates into a tracker
113
+ // that never pauses — warn so it shows up in logcat for the integrator.
114
+ if (mConfig != null && Boolean.FALSE.equals(mConfig.getStopOnStillActivity()) && !stopOnStillWarningEmitted) {
115
+ logger.warn("ACTIVITY_PROVIDER with stopOnStillActivity=false will track continuously; consider DISTANCE_FILTER_PROVIDER or RAW_PROVIDER for that use case.");
116
+ stopOnStillWarningEmitted = true;
117
+ }
118
+
119
+ attachRecorder();
120
+ }
121
+
122
+ @Override
123
+ public void onStop() {
124
+ logger.info("Stop recording");
125
+ this.isStarted = false;
126
+ detachRecorder();
127
+ stopTracking();
128
+ }
129
+
130
+ @Override
131
+ public void onConfigure(Config config) {
132
+ // v4.5.2: only restart tracking if a field that actually affects the
133
+ // LocationRequest / activity-updates subscription has changed. A no-op
134
+ // reconfigure used to drop+re-add the location callback and momentarily
135
+ // leave the service without updates.
136
+ boolean restart = false;
137
+ if (isStarted) {
138
+ restart = !sameValue(prevDesiredAccuracy, config != null ? config.getDesiredAccuracy() : null)
139
+ || !sameValue(prevInterval, config != null ? config.getInterval() : null)
140
+ || !sameValue(prevFastestInterval, config != null ? config.getFastestInterval() : null)
141
+ || !sameValue(prevDistanceFilter, config != null ? config.getDistanceFilter() : null)
142
+ || !sameValue(prevActivitiesInterval, config != null ? config.getActivitiesInterval() : null)
143
+ || !sameValue(prevStopOnStillActivity, config != null ? config.getStopOnStillActivity() : null);
144
+ }
145
+
146
+ super.onConfigure(config);
147
+
148
+ if (restart) {
149
+ logger.debug("Restarting ACTIVITY_PROVIDER tracking after relevant config change.");
150
+ onStop();
151
+ onStart();
152
+ }
153
+
154
+ if (config != null) {
155
+ prevDesiredAccuracy = config.getDesiredAccuracy();
156
+ prevInterval = config.getInterval();
157
+ prevFastestInterval = config.getFastestInterval();
158
+ prevDistanceFilter = config.getDistanceFilter();
159
+ prevActivitiesInterval = config.getActivitiesInterval();
160
+ prevStopOnStillActivity = config.getStopOnStillActivity();
161
+ }
162
+ }
163
+
164
+ private static boolean sameValue(Object a, Object b) {
165
+ return a == null ? b == null : a.equals(b);
166
+ }
167
+
168
+ @Override
169
+ public boolean isStarted() {
170
+ return isStarted;
171
+ }
172
+
173
+ @Override
174
+ public void onCommand(int commandId, int arg1) {
175
+ if (commandId == LocationProvider.CMD_SWITCH_MODE && isStarted) {
176
+ // Foreground: ensure tracking; background: keep current behavior (activity still drives start/stop).
177
+ if (arg1 == LocationProvider.FOREGROUND_MODE) {
178
+ startTracking();
179
+ }
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Same logic as former onLocationChanged: STILL -> stationary + stopTracking, else handleLocation.
185
+ */
186
+ private void onLocationReceived(Location location) {
187
+ logger.debug("Location change: {}", location.toString());
188
+
189
+ if (lastActivity.getType() == DetectedActivity.STILL) {
190
+ handleStationary(location);
191
+ stopTracking();
192
+ return;
193
+ }
194
+
195
+ showDebugToast("acy:" + location.getAccuracy() + ",v:" + location.getSpeed());
196
+
197
+ handleLocation(location);
198
+ }
199
+
200
+ public void startTracking() {
201
+ if (isTracking) { return; }
202
+ if (fusedLocationClient == null || mConfig == null) { return; }
203
+
204
+ int priority = translateDesiredAccuracy(mConfig.getDesiredAccuracy());
205
+ // v3.4: LocationRequest.Builder (play-services-location 21.0.0+) replaces deprecated
206
+ // LocationRequest.create() + setPriority/setInterval/setFastestInterval.
207
+ // v4.5.2: also honor distanceFilter (was ignored on ACTIVITY_PROVIDER), so the
208
+ // FusedLocationProvider can throttle by distance and not just by interval.
209
+ LocationRequest.Builder builder = new LocationRequest.Builder(priority, mConfig.getInterval())
210
+ .setMinUpdateIntervalMillis(mConfig.getFastestInterval())
211
+ .setWaitForAccurateLocation(false);
212
+ Integer distanceFilter = mConfig.getDistanceFilter();
213
+ if (distanceFilter != null && distanceFilter > 0) {
214
+ builder.setMinUpdateDistanceMeters(distanceFilter.floatValue());
215
+ }
216
+ LocationRequest locationRequest = builder.build();
217
+
218
+ try {
219
+ fusedLocationClient.requestLocationUpdates(
220
+ locationRequest,
221
+ locationCallback,
222
+ Looper.getMainLooper()
223
+ );
224
+ isTracking = true;
225
+ logger.debug("Start tracking with priority={} fastestInterval={} interval={} activitiesInterval={} stopOnStillActivity={}",
226
+ priority, mConfig.getFastestInterval(), mConfig.getInterval(), mConfig.getActivitiesInterval(), mConfig.getStopOnStillActivity());
227
+ } catch (SecurityException e) {
228
+ logger.error("Security exception: {}", e.getMessage());
229
+ this.handleSecurityException(e);
230
+ }
231
+ }
232
+
233
+ public void stopTracking() {
234
+ if (!isTracking) { return; }
235
+ if (fusedLocationClient == null) {
236
+ isTracking = false;
237
+ return;
238
+ }
239
+ try {
240
+ fusedLocationClient.removeLocationUpdates(locationCallback);
241
+ } catch (SecurityException e) {
242
+ logger.warn("Security exception removing location updates: {}", e.getMessage());
243
+ } finally {
244
+ isTracking = false;
245
+ }
246
+ }
247
+
248
+ private boolean activityRecognitionPermitted() {
249
+ return Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
250
+ || ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACTIVITY_RECOGNITION) == PackageManager.PERMISSION_GRANTED;
251
+ }
252
+
253
+ private void attachRecorder() {
254
+ if (fusedLocationClient == null || activityRecognitionClient == null) { return; }
255
+ if (mConfig == null) { return; }
256
+
257
+ startTracking();
258
+
259
+ if (!isWatchingActivity && mConfig.getStopOnStillActivity()) {
260
+ // v4.5.2: on Android 10+ ACTIVITY_RECOGNITION is a runtime permission.
261
+ // Without it, requestActivityUpdates() silently returns no broadcasts and
262
+ // STILL/ACTIVE never flips — the provider then runs as a continuous tracker
263
+ // by accident. Emit a one-shot error so the host app knows to request it.
264
+ if (!activityRecognitionPermitted()) {
265
+ if (!activityPermissionErrorEmitted) {
266
+ String msg = "ACTIVITY_RECOGNITION permission denied; ACTIVITY_PROVIDER cannot detect STILL/ACTIVE transitions.";
267
+ logger.warn(msg);
268
+ handlePermissionDenied(msg);
269
+ activityPermissionErrorEmitted = true;
270
+ }
271
+ return;
272
+ }
273
+ activityRecognitionClient.requestActivityUpdates(
274
+ mConfig.getActivitiesInterval(),
275
+ detectedActivitiesPI
276
+ ).addOnFailureListener(e -> logger.error("requestActivityUpdates failed: {}", e.getMessage()));
277
+ isWatchingActivity = true;
278
+ }
279
+ }
280
+
281
+ private void detachRecorder() {
282
+ if (!isWatchingActivity) { return; }
283
+ if (activityRecognitionClient == null) {
284
+ isWatchingActivity = false;
285
+ return;
286
+ }
287
+ try {
288
+ logger.debug("Detaching recorder");
289
+ activityRecognitionClient.removeActivityUpdates(detectedActivitiesPI)
290
+ .addOnSuccessListener(v -> logger.debug("removeActivityUpdates ok"))
291
+ .addOnFailureListener(e -> logger.warn("removeActivityUpdates failed: {}", e.getMessage()));
292
+ } finally {
293
+ isWatchingActivity = false;
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Translates a number representing desired accuracy of Geolocation system from set [0, 10, 100, 1000].
299
+ * 0: most aggressive, most accurate, worst battery drain
300
+ * 1000: least aggressive, least accurate, best for battery.
301
+ */
302
+ private int translateDesiredAccuracy(Integer accuracy) {
303
+ // v3.4: Priority.* (play-services-location 21.0.0+) replaces deprecated LocationRequest.PRIORITY_*.
304
+ if (accuracy == null) {
305
+ return Priority.PRIORITY_BALANCED_POWER_ACCURACY;
306
+ }
307
+ if (accuracy >= 10000) {
308
+ return Priority.PRIORITY_PASSIVE;
309
+ }
310
+ if (accuracy >= 1000) {
311
+ return Priority.PRIORITY_LOW_POWER;
312
+ }
313
+ if (accuracy >= 100) {
314
+ return Priority.PRIORITY_BALANCED_POWER_ACCURACY;
315
+ }
316
+ return Priority.PRIORITY_HIGH_ACCURACY;
317
+ }
318
+
319
+ public static DetectedActivity getProbableActivity(List<DetectedActivity> detectedActivities) {
320
+ int highestConfidence = 0;
321
+ DetectedActivity mostLikelyActivity = new DetectedActivity(DetectedActivity.UNKNOWN, 0);
322
+
323
+ for (DetectedActivity da : detectedActivities) {
324
+ if (da.getType() != DetectedActivity.TILTING && da.getType() != DetectedActivity.UNKNOWN) {
325
+ if (highestConfidence < da.getConfidence()) {
326
+ highestConfidence = da.getConfidence();
327
+ mostLikelyActivity = da;
328
+ }
329
+ }
330
+ }
331
+ return mostLikelyActivity;
332
+ }
333
+
334
+ private class DetectedActivitiesReceiver extends BroadcastReceiver {
335
+ @Override
336
+ public void onReceive(Context context, Intent intent) {
337
+ ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
338
+ if (result == null) return;
339
+ List<DetectedActivity> detectedActivities = result.getProbableActivities();
340
+ if (detectedActivities == null || detectedActivities.isEmpty()) return;
341
+
342
+ DetectedActivity candidate = getProbableActivity(detectedActivities);
343
+
344
+ // v4.5.2: skip transitions whose confidence is below the configured
345
+ // threshold (default 50). Prevents jittery STILL/ACTIVE flips when the
346
+ // motion classifier is unsure — which translated into spurious GPS
347
+ // start/stop bursts in earlier versions.
348
+ Integer threshold = mConfig != null ? mConfig.getActivityConfidenceThreshold() : null;
349
+ if (threshold != null && candidate.getConfidence() < threshold) {
350
+ logger.debug("Ignoring low-confidence activity={} confidence={} threshold={}",
351
+ BackgroundActivity.getActivityString(candidate.getType()),
352
+ candidate.getConfidence(),
353
+ threshold);
354
+ return;
355
+ }
356
+
357
+ lastActivity = candidate;
358
+
359
+ logger.debug("Detected activity={} confidence={}", BackgroundActivity.getActivityString(lastActivity.getType()), lastActivity.getConfidence());
360
+
361
+ handleActivity(lastActivity);
362
+
363
+ if (lastActivity.getType() == DetectedActivity.STILL) {
364
+ showDebugToast("Detected STILL Activity");
365
+ } else {
366
+ showDebugToast("Detected ACTIVE Activity");
367
+ startTracking();
368
+ }
369
+ }
370
+ }
371
+
372
+ private final BroadcastReceiver detectedActivitiesReceiver = new DetectedActivitiesReceiver();
373
+
374
+ @Override
375
+ public void onDestroy() {
376
+ logger.info("Destroying ActivityRecognitionLocationProvider");
377
+ onStop();
378
+ try {
379
+ unregisterReceiver(detectedActivitiesReceiver);
380
+ } catch (Exception ignored) {
381
+ // Receiver may not be registered if onCreate/onStart never completed
382
+ }
383
+ super.onDestroy();
384
+ }
385
+ }