@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.
- package/JosuelmmCapacitorBackgroundGeolocation.podspec +34 -0
- package/LICENSE +17 -0
- package/NOTICE.md +32 -0
- package/Package.swift +45 -0
- package/README.md +402 -0
- package/android/build.gradle +79 -0
- package/android/proguard-rules.pro +1 -0
- package/android/src/main/AndroidManifest.xml +83 -0
- package/android/src/main/java/com/evgenii/jsevaluator/HandlerWrapper.java +18 -0
- package/android/src/main/java/com/evgenii/jsevaluator/JavaScriptInterface.java +22 -0
- package/android/src/main/java/com/evgenii/jsevaluator/JsEvaluator.java +133 -0
- package/android/src/main/java/com/evgenii/jsevaluator/JsFunctionCallFormatter.java +37 -0
- package/android/src/main/java/com/evgenii/jsevaluator/WebViewWrapper.java +71 -0
- package/android/src/main/java/com/evgenii/jsevaluator/interfaces/CallJavaResultInterface.java +8 -0
- package/android/src/main/java/com/evgenii/jsevaluator/interfaces/HandlerWrapperInterface.java +5 -0
- package/android/src/main/java/com/evgenii/jsevaluator/interfaces/JsCallback.java +10 -0
- package/android/src/main/java/com/evgenii/jsevaluator/interfaces/JsEvaluatorInterface.java +18 -0
- package/android/src/main/java/com/evgenii/jsevaluator/interfaces/WebViewWrapperInterface.java +14 -0
- package/android/src/main/java/com/josuelmm/capacitor/backgroundgeolocation/BackgroundGeolocationPlugin.java +898 -0
- package/android/src/main/java/com/josuelmm/capacitor/backgroundgeolocation/ConfigMapper.java +303 -0
- package/android/src/main/java/com/josuelmm/capacitor/backgroundgeolocation/HeadlessTaskRegistry.java +34 -0
- package/android/src/main/java/com/josuelmm/capacitor/backgroundgeolocation/JsEvaluatorTaskRunner.java +63 -0
- package/android/src/main/java/com/marianhello/bgloc/BackgroundGeolocationFacade.java +699 -0
- package/android/src/main/java/com/marianhello/bgloc/BootCompletedReceiver.java +103 -0
- package/android/src/main/java/com/marianhello/bgloc/Config.java +1155 -0
- package/android/src/main/java/com/marianhello/bgloc/ConnectivityListener.java +5 -0
- package/android/src/main/java/com/marianhello/bgloc/HttpPostService.java +362 -0
- package/android/src/main/java/com/marianhello/bgloc/LocationManager.java +138 -0
- package/android/src/main/java/com/marianhello/bgloc/PluginDelegate.java +45 -0
- package/android/src/main/java/com/marianhello/bgloc/PluginException.java +38 -0
- package/android/src/main/java/com/marianhello/bgloc/PostLocationTask.java +238 -0
- package/android/src/main/java/com/marianhello/bgloc/ResourceResolver.java +55 -0
- package/android/src/main/java/com/marianhello/bgloc/data/AbstractLocationTemplate.java +69 -0
- package/android/src/main/java/com/marianhello/bgloc/data/ArrayListLocationTemplate.java +88 -0
- package/android/src/main/java/com/marianhello/bgloc/data/BackgroundActivity.java +108 -0
- package/android/src/main/java/com/marianhello/bgloc/data/BackgroundLocation.java +1088 -0
- package/android/src/main/java/com/marianhello/bgloc/data/ConfigJsonMapper.java +211 -0
- package/android/src/main/java/com/marianhello/bgloc/data/ConfigurationDAO.java +13 -0
- package/android/src/main/java/com/marianhello/bgloc/data/DAOFactory.java +17 -0
- package/android/src/main/java/com/marianhello/bgloc/data/HashMapLocationTemplate.java +82 -0
- package/android/src/main/java/com/marianhello/bgloc/data/LocationDAO.java +27 -0
- package/android/src/main/java/com/marianhello/bgloc/data/LocationTemplate.java +12 -0
- package/android/src/main/java/com/marianhello/bgloc/data/LocationTemplateFactory.java +71 -0
- package/android/src/main/java/com/marianhello/bgloc/data/LocationTransform.java +19 -0
- package/android/src/main/java/com/marianhello/bgloc/data/SessionLocationDAO.java +18 -0
- package/android/src/main/java/com/marianhello/bgloc/data/provider/ContentProviderLocationDAO.java +406 -0
- package/android/src/main/java/com/marianhello/bgloc/data/provider/LocationContentProvider.java +321 -0
- package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationContract.java +94 -0
- package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationDAO.java +227 -0
- package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationContract.java +122 -0
- package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationDAO.java +550 -0
- package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java +189 -0
- package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionContract.java +74 -0
- package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionLocationDAO.java +169 -0
- package/android/src/main/java/com/marianhello/bgloc/driving/DrivingEventsDetector.java +265 -0
- package/android/src/main/java/com/marianhello/bgloc/headless/AbstractTaskRunner.java +15 -0
- package/android/src/main/java/com/marianhello/bgloc/headless/ActivityTask.java +48 -0
- package/android/src/main/java/com/marianhello/bgloc/headless/JsCallback.java +10 -0
- package/android/src/main/java/com/marianhello/bgloc/headless/LocationTask.java +60 -0
- package/android/src/main/java/com/marianhello/bgloc/headless/StationaryTask.java +25 -0
- package/android/src/main/java/com/marianhello/bgloc/headless/Task.java +8 -0
- package/android/src/main/java/com/marianhello/bgloc/headless/TaskRunner.java +5 -0
- package/android/src/main/java/com/marianhello/bgloc/headless/TaskRunnerFactory.java +8 -0
- package/android/src/main/java/com/marianhello/bgloc/http/UrlTemplateResolver.java +115 -0
- package/android/src/main/java/com/marianhello/bgloc/oem/BatteryOemHelper.java +214 -0
- package/android/src/main/java/com/marianhello/bgloc/provider/AbstractLocationProvider.java +218 -0
- package/android/src/main/java/com/marianhello/bgloc/provider/ActivityRecognitionLocationProvider.java +385 -0
- package/android/src/main/java/com/marianhello/bgloc/provider/DistanceFilterLocationProvider.java +685 -0
- package/android/src/main/java/com/marianhello/bgloc/provider/LocationProvider.java +32 -0
- package/android/src/main/java/com/marianhello/bgloc/provider/LocationProviderFactory.java +47 -0
- package/android/src/main/java/com/marianhello/bgloc/provider/ProviderDelegate.java +12 -0
- package/android/src/main/java/com/marianhello/bgloc/provider/RawLocationProvider.java +175 -0
- package/android/src/main/java/com/marianhello/bgloc/sensor/SensorFusionDetector.java +199 -0
- package/android/src/main/java/com/marianhello/bgloc/service/LocationService.java +16 -0
- package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +1531 -0
- package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceInfo.java +6 -0
- package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceInfoImpl.java +41 -0
- package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceIntentBuilder.java +203 -0
- package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java +156 -0
- package/android/src/main/java/com/marianhello/bgloc/sync/AccountHelper.java +39 -0
- package/android/src/main/java/com/marianhello/bgloc/sync/Authenticator.java +68 -0
- package/android/src/main/java/com/marianhello/bgloc/sync/AuthenticatorService.java +28 -0
- package/android/src/main/java/com/marianhello/bgloc/sync/BatchManager.java +311 -0
- package/android/src/main/java/com/marianhello/bgloc/sync/NotificationHelper.java +148 -0
- package/android/src/main/java/com/marianhello/bgloc/sync/SyncAdapter.java +301 -0
- package/android/src/main/java/com/marianhello/bgloc/sync/SyncService.java +68 -0
- package/android/src/main/java/com/marianhello/logging/DBLogReader.java +208 -0
- package/android/src/main/java/com/marianhello/logging/LogEntry.java +99 -0
- package/android/src/main/java/com/marianhello/logging/LoggerManager.java +70 -0
- package/android/src/main/java/com/marianhello/logging/UncaughtExceptionLogger.java +36 -0
- package/android/src/main/java/com/marianhello/utils/CloneHelper.java +22 -0
- package/android/src/main/java/com/marianhello/utils/Convert.java +56 -0
- package/android/src/main/java/com/marianhello/utils/TextUtils.java +72 -0
- package/android/src/main/java/com/marianhello/utils/ToneGenerator.java +68 -0
- package/android/src/main/java/org/apache/commons/io/Charsets.java +153 -0
- package/android/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java +344 -0
- package/android/src/main/java/org/chromium/content/browser/ThreadUtils.java +134 -0
- package/android/src/main/java/ru/andremoniy/sqlbuilder/SqlExpression.java +398 -0
- package/android/src/main/java/ru/andremoniy/sqlbuilder/SqlSelectStatement.java +671 -0
- package/android/src/main/java/ru/andremoniy/sqlbuilder/SqlStatement.java +29 -0
- package/android/src/main/java/ru/andremoniy/utils/TextUtils.java +61 -0
- package/android/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/android/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/android/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/android/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/android/src/main/res/values/strings.xml +15 -0
- package/android/src/main/res/xml/authenticator.xml +7 -0
- package/android/src/main/res/xml/syncadapter.xml +9 -0
- package/dist/esm/definitions.d.ts +1052 -0
- package/dist/esm/definitions.js +142 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +8 -0
- package/dist/esm/index.js +23 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +92 -0
- package/dist/esm/web.js +242 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +415 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +418 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/BackgroundGeolocationPlugin/BackgroundGeolocationPlugin-Bridging-Header.h +18 -0
- package/ios/Sources/BackgroundGeolocationPlugin/BackgroundGeolocationPlugin.m +52 -0
- package/ios/Sources/BackgroundGeolocationPlugin/BackgroundGeolocationPlugin.swift +750 -0
- package/ios/Tests/BackgroundGeolocationPluginTests/BackgroundGeolocationPluginTests.swift +12 -0
- package/ios/common/BackgroundGeolocation/CocoaLumberjack.h +1945 -0
- package/ios/common/BackgroundGeolocation/CocoaLumberjack.m +5255 -0
- package/ios/common/BackgroundGeolocation/FMDB.h +2357 -0
- package/ios/common/BackgroundGeolocation/FMDB.m +2672 -0
- package/ios/common/BackgroundGeolocation/FMDBLogger.h +42 -0
- package/ios/common/BackgroundGeolocation/FMDBLogger.m +264 -0
- package/ios/common/BackgroundGeolocation/INTULocationManager/INTUHeadingRequest.h +41 -0
- package/ios/common/BackgroundGeolocation/INTULocationManager/INTUHeadingRequest.m +68 -0
- package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationManager+Internal.h +33 -0
- package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationManager.h +178 -0
- package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationManager.m +1025 -0
- package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationRequest.h +103 -0
- package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationRequest.m +238 -0
- package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationRequestDefines.h +163 -0
- package/ios/common/BackgroundGeolocation/INTULocationManager/INTURequestIDGenerator.h +39 -0
- package/ios/common/BackgroundGeolocation/INTULocationManager/INTURequestIDGenerator.m +37 -0
- package/ios/common/BackgroundGeolocation/MAURAbstractLocationProvider.h +51 -0
- package/ios/common/BackgroundGeolocation/MAURAbstractLocationProvider.m +53 -0
- package/ios/common/BackgroundGeolocation/MAURActivity.h +23 -0
- package/ios/common/BackgroundGeolocation/MAURActivity.m +52 -0
- package/ios/common/BackgroundGeolocation/MAURActivityLocationProvider.h +18 -0
- package/ios/common/BackgroundGeolocation/MAURActivityLocationProvider.m +340 -0
- package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.h +88 -0
- package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +1193 -0
- package/ios/common/BackgroundGeolocation/MAURBackgroundSync.h +46 -0
- package/ios/common/BackgroundGeolocation/MAURBackgroundSync.m +283 -0
- package/ios/common/BackgroundGeolocation/MAURBackgroundTaskManager.h +25 -0
- package/ios/common/BackgroundGeolocation/MAURBackgroundTaskManager.m +105 -0
- package/ios/common/BackgroundGeolocation/MAURConfig.h +99 -0
- package/ios/common/BackgroundGeolocation/MAURConfig.m +636 -0
- package/ios/common/BackgroundGeolocation/MAURConfigurationContract.h +53 -0
- package/ios/common/BackgroundGeolocation/MAURConfigurationContract.m +54 -0
- package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.h +20 -0
- package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.m +550 -0
- package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.h +17 -0
- package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.m +124 -0
- package/ios/common/BackgroundGeolocation/MAURLocation.h +73 -0
- package/ios/common/BackgroundGeolocation/MAURLocation.m +392 -0
- package/ios/common/BackgroundGeolocation/MAURLocationContract.h +38 -0
- package/ios/common/BackgroundGeolocation/MAURLocationContract.m +39 -0
- package/ios/common/BackgroundGeolocation/MAURLocationManager.h +53 -0
- package/ios/common/BackgroundGeolocation/MAURLocationManager.m +305 -0
- package/ios/common/BackgroundGeolocation/MAURLogReader.h +26 -0
- package/ios/common/BackgroundGeolocation/MAURLogReader.m +122 -0
- package/ios/common/BackgroundGeolocation/MAURLogging.h +19 -0
- package/ios/common/BackgroundGeolocation/MAURPostLocationTask.h +53 -0
- package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +367 -0
- package/ios/common/BackgroundGeolocation/MAURProviderDelegate.h +52 -0
- package/ios/common/BackgroundGeolocation/MAURRawLocationProvider.h +18 -0
- package/ios/common/BackgroundGeolocation/MAURRawLocationProvider.m +138 -0
- package/ios/common/BackgroundGeolocation/MAURSQLiteConfigurationDAO.h +26 -0
- package/ios/common/BackgroundGeolocation/MAURSQLiteConfigurationDAO.m +335 -0
- package/ios/common/BackgroundGeolocation/MAURSQLiteHelper.h +57 -0
- package/ios/common/BackgroundGeolocation/MAURSQLiteHelper.m +93 -0
- package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.h +52 -0
- package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.m +520 -0
- package/ios/common/BackgroundGeolocation/MAURSQLiteOpenHelper.h +32 -0
- package/ios/common/BackgroundGeolocation/MAURSQLiteOpenHelper.m +276 -0
- package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.h +41 -0
- package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.m +137 -0
- package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.h +29 -0
- package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.m +31 -0
- package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.h +25 -0
- package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.m +153 -0
- package/ios/common/BackgroundGeolocation/MAURUncaughtExceptionLogger.h +20 -0
- package/ios/common/BackgroundGeolocation/MAURUncaughtExceptionLogger.m +62 -0
- package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.h +31 -0
- package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.m +107 -0
- package/ios/common/BackgroundGeolocation/Reachability.h +102 -0
- package/ios/common/BackgroundGeolocation/Reachability.m +475 -0
- package/ios/common/BackgroundGeolocation/SQLQueryBuilder/README.md +170 -0
- package/ios/common/BackgroundGeolocation/SQLQueryBuilder/ext/NSString+ZIMString.h +55 -0
- package/ios/common/BackgroundGeolocation/SQLQueryBuilder/ext/NSString+ZIMString.m +47 -0
- package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlDataManipulationCommand.h +27 -0
- package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlExpression.h +250 -0
- package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlExpression.m +259 -0
- package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlSelectStatement.h +360 -0
- package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlSelectStatement.m +427 -0
- package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlStatement.h +37 -0
- package/ios/common/BackgroundGeolocation/module.modulemap +16 -0
- package/package.json +82 -0
package/android/src/main/java/com/marianhello/bgloc/provider/DistanceFilterLocationProvider.java
ADDED
|
@@ -0,0 +1,685 @@
|
|
|
1
|
+
package com.marianhello.bgloc.provider;
|
|
2
|
+
|
|
3
|
+
import android.app.AlarmManager;
|
|
4
|
+
import android.app.PendingIntent;
|
|
5
|
+
import android.content.BroadcastReceiver;
|
|
6
|
+
import android.content.Context;
|
|
7
|
+
import android.content.Intent;
|
|
8
|
+
import android.content.IntentFilter;
|
|
9
|
+
import android.location.Location;
|
|
10
|
+
import android.location.LocationListener;
|
|
11
|
+
import android.location.LocationManager;
|
|
12
|
+
import android.os.Build;
|
|
13
|
+
import android.os.Bundle;
|
|
14
|
+
import android.os.Looper;
|
|
15
|
+
|
|
16
|
+
import com.google.android.gms.common.ConnectionResult;
|
|
17
|
+
import com.google.android.gms.common.GoogleApiAvailability;
|
|
18
|
+
import com.google.android.gms.location.FusedLocationProviderClient;
|
|
19
|
+
import com.google.android.gms.location.LocationCallback;
|
|
20
|
+
import com.google.android.gms.location.LocationRequest;
|
|
21
|
+
import com.google.android.gms.location.LocationResult;
|
|
22
|
+
import com.google.android.gms.location.LocationServices;
|
|
23
|
+
import com.google.android.gms.location.Priority;
|
|
24
|
+
|
|
25
|
+
import com.marianhello.bgloc.Config;
|
|
26
|
+
import com.marianhello.utils.ToneGenerator.Tone;
|
|
27
|
+
|
|
28
|
+
import java.util.List;
|
|
29
|
+
|
|
30
|
+
import static java.lang.Math.abs;
|
|
31
|
+
import static java.lang.Math.pow;
|
|
32
|
+
import static java.lang.Math.round;
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* v4.5.2 — Distance-filter provider with a runtime-chosen backend:
|
|
37
|
+
* <ul>
|
|
38
|
+
* <li><b>Fused path</b> (Play Services available): {@link FusedLocationProviderClient}
|
|
39
|
+
* + {@link LocationCallback}. Better fused GPS+Network blending and battery.</li>
|
|
40
|
+
* <li><b>Legacy path</b> (Play Services missing — Huawei/HMS, AOSP, China ROMs):
|
|
41
|
+
* {@link android.location.LocationManager} + {@link LocationListener}. Preserves the
|
|
42
|
+
* original DISTANCE_FILTER behavior so the plugin works on every Android device.</li>
|
|
43
|
+
* </ul>
|
|
44
|
+
*
|
|
45
|
+
* Stationary detection in both paths relies on the existing alarm-driven polling
|
|
46
|
+
* (no geofencing / proximity alerts), per product decision.
|
|
47
|
+
*/
|
|
48
|
+
public class DistanceFilterLocationProvider extends AbstractLocationProvider implements LocationListener {
|
|
49
|
+
|
|
50
|
+
private static final String TAG = DistanceFilterLocationProvider.class.getSimpleName();
|
|
51
|
+
private static final String P_NAME = "com.marianhello.bgloc";
|
|
52
|
+
|
|
53
|
+
private static final String STATIONARY_ALARM_ACTION = P_NAME + ".STATIONARY_ALARM_ACTION";
|
|
54
|
+
private static final String SINGLE_LOCATION_UPDATE_ACTION = P_NAME + ".SINGLE_LOCATION_UPDATE_ACTION";
|
|
55
|
+
private static final String STATIONARY_LOCATION_MONITOR_ACTION = P_NAME + ".STATIONARY_LOCATION_MONITOR_ACTION";
|
|
56
|
+
|
|
57
|
+
// v4.5.1: defaults — overridable per-config via config.stationaryTimeout / stationaryPollInterval / stationaryPollFast
|
|
58
|
+
private static final long DEFAULT_STATIONARY_TIMEOUT = 5 * 1000 * 60;
|
|
59
|
+
private static final long DEFAULT_STATIONARY_LOCATION_POLLING_INTERVAL_LAZY = 3 * 1000 * 60;
|
|
60
|
+
private static final long DEFAULT_STATIONARY_LOCATION_POLLING_INTERVAL_AGGRESSIVE = 1 * 1000 * 60;
|
|
61
|
+
private static final int MAX_STATIONARY_ACQUISITION_ATTEMPTS = 5;
|
|
62
|
+
private static final int MAX_SPEED_ACQUISITION_ATTEMPTS = 3;
|
|
63
|
+
|
|
64
|
+
// v4.5.2 — Aggressive interval used while acquiring stationary location or speed (FLP path).
|
|
65
|
+
private static final long ACQUISITION_INTERVAL_MS = 1000L;
|
|
66
|
+
|
|
67
|
+
private Boolean isMoving = false;
|
|
68
|
+
private Boolean isAcquiringStationaryLocation = false;
|
|
69
|
+
private Boolean isAcquiringSpeed = false;
|
|
70
|
+
private Integer locationAcquisitionAttempts = 0;
|
|
71
|
+
|
|
72
|
+
private Location lastLocation;
|
|
73
|
+
private Location stationaryLocation;
|
|
74
|
+
private float stationaryRadius;
|
|
75
|
+
private PendingIntent stationaryAlarmPI;
|
|
76
|
+
private PendingIntent stationaryLocationPollingPI;
|
|
77
|
+
private PendingIntent singleUpdatePI; // legacy path only
|
|
78
|
+
private long stationaryLocationPollingInterval;
|
|
79
|
+
|
|
80
|
+
private Integer scaledDistanceFilter;
|
|
81
|
+
|
|
82
|
+
private FusedLocationProviderClient fusedClient; // null on the legacy path
|
|
83
|
+
private LocationManager locationManager; // always non-null after onCreate
|
|
84
|
+
private AlarmManager alarmManager;
|
|
85
|
+
private boolean usingFused = false;
|
|
86
|
+
|
|
87
|
+
private boolean isStarted = false;
|
|
88
|
+
|
|
89
|
+
/** v4.5.1: read overrides from {@link com.marianhello.bgloc.Config}; fall back to defaults. */
|
|
90
|
+
private long getStationaryTimeout() {
|
|
91
|
+
Integer v = mConfig != null ? mConfig.getStationaryTimeout() : null;
|
|
92
|
+
return v != null ? v.longValue() : DEFAULT_STATIONARY_TIMEOUT;
|
|
93
|
+
}
|
|
94
|
+
private long getStationaryPollLazy() {
|
|
95
|
+
Integer v = mConfig != null ? mConfig.getStationaryPollInterval() : null;
|
|
96
|
+
return v != null ? v.longValue() : DEFAULT_STATIONARY_LOCATION_POLLING_INTERVAL_LAZY;
|
|
97
|
+
}
|
|
98
|
+
private long getStationaryPollFast() {
|
|
99
|
+
Integer v = mConfig != null ? mConfig.getStationaryPollFast() : null;
|
|
100
|
+
return v != null ? v.longValue() : DEFAULT_STATIONARY_LOCATION_POLLING_INTERVAL_AGGRESSIVE;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public DistanceFilterLocationProvider(Context context) {
|
|
104
|
+
super(context);
|
|
105
|
+
PROVIDER_ID = Config.DISTANCE_FILTER_PROVIDER;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ====== FLP path callbacks ======
|
|
109
|
+
|
|
110
|
+
private final LocationCallback fusedCallback = new LocationCallback() {
|
|
111
|
+
@Override
|
|
112
|
+
public void onLocationResult(LocationResult result) {
|
|
113
|
+
if (result == null) return;
|
|
114
|
+
List<Location> locations = result.getLocations();
|
|
115
|
+
if (locations == null) return;
|
|
116
|
+
for (Location location : locations) {
|
|
117
|
+
handleNewLocation(location);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/** FLP one-shot callback used by the stationary polling alarm. */
|
|
123
|
+
private final LocationCallback fusedPollCallback = new LocationCallback() {
|
|
124
|
+
@Override
|
|
125
|
+
public void onLocationResult(LocationResult result) {
|
|
126
|
+
if (result == null || fusedClient == null) return;
|
|
127
|
+
try {
|
|
128
|
+
fusedClient.removeLocationUpdates(this);
|
|
129
|
+
} catch (Exception ignored) { /* fire-and-forget */ }
|
|
130
|
+
Location loc = result.getLastLocation();
|
|
131
|
+
if (loc != null) {
|
|
132
|
+
logger.debug("Stationary monitor single update: {}", loc);
|
|
133
|
+
onPollStationaryLocation(loc);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// ====== Lifecycle ======
|
|
139
|
+
|
|
140
|
+
@Override
|
|
141
|
+
public void onCreate() {
|
|
142
|
+
super.onCreate();
|
|
143
|
+
|
|
144
|
+
locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
|
|
145
|
+
alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
|
|
146
|
+
|
|
147
|
+
// v4.5.2 — pick the location backend at runtime. Play Services missing
|
|
148
|
+
// (Huawei/HMS, AOSP, China ROMs) → use the OS LocationManager so the
|
|
149
|
+
// provider still works.
|
|
150
|
+
int gps = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(mContext);
|
|
151
|
+
usingFused = (gps == ConnectionResult.SUCCESS);
|
|
152
|
+
if (usingFused) {
|
|
153
|
+
fusedClient = LocationServices.getFusedLocationProviderClient(mContext);
|
|
154
|
+
logger.info("DISTANCE_FILTER_PROVIDER using FusedLocationProviderClient (Play Services available).");
|
|
155
|
+
} else {
|
|
156
|
+
logger.info("DISTANCE_FILTER_PROVIDER falling back to LocationManager (Play Services unavailable, code={}).", gps);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
int updateCurrentFlag = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
|
160
|
+
? PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
|
161
|
+
: PendingIntent.FLAG_UPDATE_CURRENT;
|
|
162
|
+
// v4.5.2: singleUpdatePI must be MUTABLE on API 31+ because
|
|
163
|
+
// LocationManager.requestSingleUpdate() fills the resulting Location
|
|
164
|
+
// into the intent's extras at delivery time. FLAG_IMMUTABLE blocks that
|
|
165
|
+
// population, so the receiver would never see the fix.
|
|
166
|
+
int singleUpdateFlag = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
|
167
|
+
? PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE
|
|
168
|
+
: PendingIntent.FLAG_CANCEL_CURRENT;
|
|
169
|
+
|
|
170
|
+
Intent stationaryAlarmIntent = new Intent(mContext, StationaryAlarmReceiver.class);
|
|
171
|
+
stationaryAlarmIntent.setAction(STATIONARY_ALARM_ACTION);
|
|
172
|
+
stationaryAlarmPI = PendingIntent.getBroadcast(mContext, 9000, stationaryAlarmIntent, updateCurrentFlag);
|
|
173
|
+
registerReceiver(stationaryAlarmReceiver, new IntentFilter(STATIONARY_ALARM_ACTION));
|
|
174
|
+
|
|
175
|
+
Intent stationaryLocationMonitorIntent = new Intent(mContext, StationaryLocationMonitorReceiver.class);
|
|
176
|
+
stationaryLocationMonitorIntent.setAction(STATIONARY_LOCATION_MONITOR_ACTION);
|
|
177
|
+
stationaryLocationPollingPI = PendingIntent.getBroadcast(mContext, 9002, stationaryLocationMonitorIntent, updateCurrentFlag);
|
|
178
|
+
registerReceiver(stationaryLocationMonitorReceiver, new IntentFilter(STATIONARY_LOCATION_MONITOR_ACTION));
|
|
179
|
+
|
|
180
|
+
// Legacy single-update PI + receiver (only used when usingFused == false).
|
|
181
|
+
if (!usingFused) {
|
|
182
|
+
Intent singleLocationUpdateIntent = new Intent(mContext, SingleUpdateReceiver.class);
|
|
183
|
+
singleLocationUpdateIntent.setAction(SINGLE_LOCATION_UPDATE_ACTION);
|
|
184
|
+
singleUpdatePI = PendingIntent.getBroadcast(mContext, 9003, singleLocationUpdateIntent, singleUpdateFlag);
|
|
185
|
+
registerReceiver(singleUpdateReceiver, new IntentFilter(SINGLE_LOCATION_UPDATE_ACTION));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@Override
|
|
190
|
+
public void onStart() {
|
|
191
|
+
if (isStarted) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (locationManager == null) {
|
|
195
|
+
logger.error("LocationManager is null");
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (alarmManager == null) {
|
|
199
|
+
logger.error("AlarmManager is null");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (mConfig == null) {
|
|
203
|
+
logger.warn("DistanceFilterLocationProvider started without config");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
logger.info("Start recording (path={})", usingFused ? "fused" : "legacy");
|
|
208
|
+
scaledDistanceFilter = mConfig.getDistanceFilter();
|
|
209
|
+
isStarted = true;
|
|
210
|
+
setPace(false);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@Override
|
|
214
|
+
public void onStop() {
|
|
215
|
+
if (!isStarted) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
unsubscribeLocationUpdates();
|
|
220
|
+
if (alarmManager != null) {
|
|
221
|
+
if (stationaryAlarmPI != null) alarmManager.cancel(stationaryAlarmPI);
|
|
222
|
+
if (stationaryLocationPollingPI != null) alarmManager.cancel(stationaryLocationPollingPI);
|
|
223
|
+
}
|
|
224
|
+
} catch (SecurityException ignored) {
|
|
225
|
+
} finally {
|
|
226
|
+
isStarted = false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
@Override
|
|
231
|
+
public void onCommand(int commandId, int arg1) {
|
|
232
|
+
switch(commandId) {
|
|
233
|
+
case CMD_SWITCH_MODE:
|
|
234
|
+
setPace(arg1 == BACKGROUND_MODE ? false : true);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
@Override
|
|
240
|
+
public void onConfigure(Config config) {
|
|
241
|
+
super.onConfigure(config);
|
|
242
|
+
if (isStarted) {
|
|
243
|
+
onStop();
|
|
244
|
+
onStart();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
@Override
|
|
249
|
+
public boolean isStarted() {
|
|
250
|
+
return isStarted;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** GPS first, fall back to Network — used only by the legacy LocationManager path. */
|
|
254
|
+
private String pickProvider() {
|
|
255
|
+
if (locationManager == null) return null;
|
|
256
|
+
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
|
|
257
|
+
return LocationManager.GPS_PROVIDER;
|
|
258
|
+
}
|
|
259
|
+
if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
|
|
260
|
+
return LocationManager.NETWORK_PROVIDER;
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** Cheap check used to decide whether to emit SERVICE_ERROR when subscribing. */
|
|
266
|
+
private boolean anyProviderEnabled() {
|
|
267
|
+
if (locationManager == null) return true;
|
|
268
|
+
boolean gpsOn = false, netOn = false;
|
|
269
|
+
try { gpsOn = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); }
|
|
270
|
+
catch (Exception ignored) { /* may throw on devices with no GPS hardware */ }
|
|
271
|
+
try { netOn = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); }
|
|
272
|
+
catch (Exception ignored) { }
|
|
273
|
+
return gpsOn || netOn;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Translate the plugin's desiredAccuracy buckets into FLP priorities (FLP path only). */
|
|
277
|
+
private int translatePriority(Integer accuracy) {
|
|
278
|
+
if (accuracy == null) {
|
|
279
|
+
return Priority.PRIORITY_BALANCED_POWER_ACCURACY;
|
|
280
|
+
}
|
|
281
|
+
if (accuracy >= 10000) {
|
|
282
|
+
return Priority.PRIORITY_PASSIVE;
|
|
283
|
+
}
|
|
284
|
+
if (accuracy >= 1000) {
|
|
285
|
+
return Priority.PRIORITY_LOW_POWER;
|
|
286
|
+
}
|
|
287
|
+
if (accuracy >= 100) {
|
|
288
|
+
return Priority.PRIORITY_BALANCED_POWER_ACCURACY;
|
|
289
|
+
}
|
|
290
|
+
return Priority.PRIORITY_HIGH_ACCURACY;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private void unsubscribeLocationUpdates() {
|
|
294
|
+
try {
|
|
295
|
+
if (usingFused && fusedClient != null) {
|
|
296
|
+
fusedClient.removeLocationUpdates(fusedCallback);
|
|
297
|
+
fusedClient.removeLocationUpdates(fusedPollCallback);
|
|
298
|
+
}
|
|
299
|
+
if (locationManager != null) {
|
|
300
|
+
locationManager.removeUpdates(this);
|
|
301
|
+
}
|
|
302
|
+
} catch (SecurityException ignored) {
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* @param value true → aggressive moving tracking, false → stationary monitoring
|
|
308
|
+
*/
|
|
309
|
+
private void setPace(Boolean value) {
|
|
310
|
+
if (!isStarted) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (mConfig == null) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
logger.info("Setting pace: {}", value);
|
|
318
|
+
|
|
319
|
+
Boolean wasMoving = isMoving;
|
|
320
|
+
isMoving = value;
|
|
321
|
+
isAcquiringStationaryLocation = false;
|
|
322
|
+
isAcquiringSpeed = false;
|
|
323
|
+
stationaryLocation = null;
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
unsubscribeLocationUpdates();
|
|
327
|
+
|
|
328
|
+
if (isMoving) {
|
|
329
|
+
if (!wasMoving) {
|
|
330
|
+
isAcquiringSpeed = true;
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
isAcquiringStationaryLocation = true;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (!anyProviderEnabled()) {
|
|
337
|
+
handleServiceError("No location provider available (GPS and Network disabled).");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (usingFused) {
|
|
341
|
+
subscribeFused();
|
|
342
|
+
} else {
|
|
343
|
+
subscribeLegacy();
|
|
344
|
+
}
|
|
345
|
+
} catch (SecurityException e) {
|
|
346
|
+
logger.error("Security exception: {}", e.getMessage());
|
|
347
|
+
this.handleSecurityException(e);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
private void subscribeFused() {
|
|
352
|
+
if (fusedClient == null) return;
|
|
353
|
+
LocationRequest request;
|
|
354
|
+
if (isAcquiringSpeed || isAcquiringStationaryLocation) {
|
|
355
|
+
locationAcquisitionAttempts = 0;
|
|
356
|
+
request = new LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, ACQUISITION_INTERVAL_MS)
|
|
357
|
+
.setMinUpdateIntervalMillis(ACQUISITION_INTERVAL_MS)
|
|
358
|
+
.setWaitForAccurateLocation(false)
|
|
359
|
+
.build();
|
|
360
|
+
} else {
|
|
361
|
+
int priority = translatePriority(mConfig.getDesiredAccuracy());
|
|
362
|
+
long interval = mConfig.getInterval();
|
|
363
|
+
LocationRequest.Builder b = new LocationRequest.Builder(priority, interval)
|
|
364
|
+
.setMinUpdateIntervalMillis(Math.min(interval, 1000L))
|
|
365
|
+
.setWaitForAccurateLocation(false);
|
|
366
|
+
if (scaledDistanceFilter != null && scaledDistanceFilter > 0) {
|
|
367
|
+
b.setMinUpdateDistanceMeters(scaledDistanceFilter.floatValue());
|
|
368
|
+
}
|
|
369
|
+
request = b.build();
|
|
370
|
+
}
|
|
371
|
+
try {
|
|
372
|
+
fusedClient.requestLocationUpdates(request, fusedCallback, Looper.getMainLooper());
|
|
373
|
+
} catch (SecurityException e) {
|
|
374
|
+
this.handleSecurityException(e);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
private void subscribeLegacy() {
|
|
379
|
+
if (locationManager == null) return;
|
|
380
|
+
try {
|
|
381
|
+
if (isAcquiringSpeed || isAcquiringStationaryLocation) {
|
|
382
|
+
locationAcquisitionAttempts = 0;
|
|
383
|
+
// Burst: subscribe to every non-passive provider for fastest lock.
|
|
384
|
+
List<String> matchingProviders = locationManager.getAllProviders();
|
|
385
|
+
for (String provider : matchingProviders) {
|
|
386
|
+
if (!LocationManager.PASSIVE_PROVIDER.equals(provider)) {
|
|
387
|
+
logger.info("Requesting location updates from provider {}", provider);
|
|
388
|
+
locationManager.requestLocationUpdates(provider, 0, 0, this);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
} else {
|
|
392
|
+
// v4.5.2 — subscribe to GPS AND Network simultaneously when both
|
|
393
|
+
// are available. The previous version only used GPS-or-Network
|
|
394
|
+
// (excluyente), which on cheap/vehicular Androids could leave the
|
|
395
|
+
// app waiting for a GPS fix while a quick Network fix was available.
|
|
396
|
+
boolean gpsOn = false, netOn = false;
|
|
397
|
+
try { gpsOn = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); }
|
|
398
|
+
catch (Exception ignored) { }
|
|
399
|
+
try { netOn = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); }
|
|
400
|
+
catch (Exception ignored) { }
|
|
401
|
+
if (!gpsOn && !netOn) {
|
|
402
|
+
logger.warn("No location provider available (GPS and Network disabled)");
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
long interval = mConfig.getInterval();
|
|
406
|
+
int distance = scaledDistanceFilter != null ? scaledDistanceFilter : 0;
|
|
407
|
+
if (gpsOn) {
|
|
408
|
+
logger.info("Requesting location updates from GPS");
|
|
409
|
+
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, interval, distance, this);
|
|
410
|
+
}
|
|
411
|
+
if (netOn) {
|
|
412
|
+
logger.info("Requesting location updates from Network");
|
|
413
|
+
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, interval, distance, this);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
} catch (SecurityException e) {
|
|
417
|
+
this.handleSecurityException(e);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// ====== LocationListener (legacy path) ======
|
|
422
|
+
|
|
423
|
+
@Override
|
|
424
|
+
public void onLocationChanged(Location location) {
|
|
425
|
+
handleNewLocation(location);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
@Override
|
|
429
|
+
public void onStatusChanged(String provider, int status, Bundle extras) {
|
|
430
|
+
logger.debug("Provider {} status changed: {}", provider, status);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
@Override
|
|
434
|
+
public void onProviderEnabled(String provider) {
|
|
435
|
+
logger.debug("Provider {} was enabled", provider);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
@Override
|
|
439
|
+
public void onProviderDisabled(String provider) {
|
|
440
|
+
logger.warn("Provider {} was disabled", provider);
|
|
441
|
+
// v4.5.2: surface as an error so JS layer can prompt the user to enable
|
|
442
|
+
// location services. Only when no fallback provider is left.
|
|
443
|
+
if (locationManager != null && pickProvider() == null) {
|
|
444
|
+
handleServiceError("Location provider '" + provider + "' disabled and no fallback available.");
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// ====== State machine (path-agnostic) ======
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Same logic as the legacy {@code onLocationChanged}: handles the moving / stationary /
|
|
452
|
+
* acquisition state machine. Called from both the FLP callback and the LocationListener.
|
|
453
|
+
*/
|
|
454
|
+
private void handleNewLocation(Location location) {
|
|
455
|
+
if (location == null) return;
|
|
456
|
+
logger.debug("Location change: {} isMoving={}", location.toString(), isMoving);
|
|
457
|
+
|
|
458
|
+
if (!isMoving && !isAcquiringStationaryLocation && stationaryLocation==null) {
|
|
459
|
+
// Perhaps our GPS signal was interrupted, re-acquire a stationary location now.
|
|
460
|
+
setPace(false);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
showDebugToast("mv:" + isMoving + ",acy:" + location.getAccuracy() + ",v:" + location.getSpeed() + ",df:" + scaledDistanceFilter);
|
|
464
|
+
|
|
465
|
+
if (isAcquiringStationaryLocation) {
|
|
466
|
+
if (stationaryLocation == null || stationaryLocation.getAccuracy() > location.getAccuracy()) {
|
|
467
|
+
stationaryLocation = location;
|
|
468
|
+
}
|
|
469
|
+
if (++locationAcquisitionAttempts == MAX_STATIONARY_ACQUISITION_ATTEMPTS) {
|
|
470
|
+
isAcquiringStationaryLocation = false;
|
|
471
|
+
enterStationary(stationaryLocation);
|
|
472
|
+
handleStationary(stationaryLocation, stationaryRadius);
|
|
473
|
+
return;
|
|
474
|
+
} else {
|
|
475
|
+
playDebugTone(Tone.BEEP);
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
} else if (isAcquiringSpeed) {
|
|
479
|
+
if (++locationAcquisitionAttempts == MAX_SPEED_ACQUISITION_ATTEMPTS) {
|
|
480
|
+
playDebugTone(Tone.DOODLY_DOO);
|
|
481
|
+
isAcquiringSpeed = false;
|
|
482
|
+
scaledDistanceFilter = calculateDistanceFilter(location.getSpeed());
|
|
483
|
+
setPace(true);
|
|
484
|
+
} else {
|
|
485
|
+
playDebugTone(Tone.BEEP);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
} else if (isMoving) {
|
|
489
|
+
playDebugTone(Tone.BEEP);
|
|
490
|
+
|
|
491
|
+
if ((location.getSpeed() >= 1) && (location.getAccuracy() <= mConfig.getStationaryRadius())) {
|
|
492
|
+
resetStationaryAlarm();
|
|
493
|
+
}
|
|
494
|
+
Integer newDistanceFilter = calculateDistanceFilter(location.getSpeed());
|
|
495
|
+
if (newDistanceFilter != scaledDistanceFilter.intValue()) {
|
|
496
|
+
logger.info("Updating distanceFilter: new={} old={}", newDistanceFilter, scaledDistanceFilter);
|
|
497
|
+
scaledDistanceFilter = newDistanceFilter;
|
|
498
|
+
setPace(true);
|
|
499
|
+
}
|
|
500
|
+
if (lastLocation != null && location.distanceTo(lastLocation) < mConfig.getDistanceFilter()) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
} else if (stationaryLocation != null) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
lastLocation = location;
|
|
507
|
+
handleLocation(location);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
public void resetStationaryAlarm() {
|
|
511
|
+
if (alarmManager == null || stationaryAlarmPI == null) return;
|
|
512
|
+
alarmManager.cancel(stationaryAlarmPI);
|
|
513
|
+
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + getStationaryTimeout(), stationaryAlarmPI);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
private Integer calculateDistanceFilter(Float speed) {
|
|
517
|
+
Double newDistanceFilter = (double) mConfig.getDistanceFilter();
|
|
518
|
+
if (speed < 100) {
|
|
519
|
+
float roundedDistanceFilter = (round(speed / 5) * 5);
|
|
520
|
+
newDistanceFilter = pow(roundedDistanceFilter, 2) + (double) mConfig.getDistanceFilter();
|
|
521
|
+
}
|
|
522
|
+
return (newDistanceFilter.intValue() < 1000) ? newDistanceFilter.intValue() : 1000;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* v4.5.2 — Stop active updates and start the polling-based stationary monitor.
|
|
527
|
+
* The previous version also called {@code addProximityAlert} (geofence); that
|
|
528
|
+
* path has been removed per product decision (no geofencing).
|
|
529
|
+
*/
|
|
530
|
+
private void enterStationary(Location location) {
|
|
531
|
+
if (location == null || mConfig == null) return;
|
|
532
|
+
try {
|
|
533
|
+
unsubscribeLocationUpdates();
|
|
534
|
+
|
|
535
|
+
float radius = mConfig.getStationaryRadius();
|
|
536
|
+
float proximityRadius = (location.getAccuracy() < radius) ? radius : location.getAccuracy();
|
|
537
|
+
stationaryLocation = location;
|
|
538
|
+
this.stationaryRadius = proximityRadius;
|
|
539
|
+
|
|
540
|
+
logger.info("enterStationary: lat={} lon={} acy={} radius={}", location.getLatitude(), location.getLongitude(), location.getAccuracy(), proximityRadius);
|
|
541
|
+
|
|
542
|
+
startPollingStationaryLocation(getStationaryPollLazy());
|
|
543
|
+
} catch (SecurityException e) {
|
|
544
|
+
logger.error("Security exception: {}", e.getMessage());
|
|
545
|
+
this.handleSecurityException(e);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/** Engage aggressive geolocation after stationary exit. */
|
|
550
|
+
public void onExitStationaryRegion(Location location) {
|
|
551
|
+
if (location == null || alarmManager == null) return;
|
|
552
|
+
|
|
553
|
+
playDebugTone(Tone.BEEP_BEEP_BEEP);
|
|
554
|
+
|
|
555
|
+
logger.info("Exited stationary: lat={} long={} acy={}", location.getLatitude(), location.getLongitude(), location.getAccuracy());
|
|
556
|
+
|
|
557
|
+
try {
|
|
558
|
+
alarmManager.cancel(stationaryLocationPollingPI);
|
|
559
|
+
this.setPace(true);
|
|
560
|
+
} catch (SecurityException e) {
|
|
561
|
+
logger.error("Security exception: {}", e.getMessage());
|
|
562
|
+
this.handleSecurityException(e);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
public void startPollingStationaryLocation(long interval) {
|
|
567
|
+
if (alarmManager == null || stationaryLocationPollingPI == null) return;
|
|
568
|
+
stationaryLocationPollingInterval = interval;
|
|
569
|
+
alarmManager.cancel(stationaryLocationPollingPI);
|
|
570
|
+
long start = System.currentTimeMillis() + 60_000;
|
|
571
|
+
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, start, interval, stationaryLocationPollingPI);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
public void onPollStationaryLocation(Location location) {
|
|
575
|
+
if (location == null || mConfig == null) return;
|
|
576
|
+
|
|
577
|
+
float radius = mConfig.getStationaryRadius();
|
|
578
|
+
if (isMoving) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
playDebugTone(Tone.BEEP);
|
|
582
|
+
|
|
583
|
+
float distance = 0.0f;
|
|
584
|
+
if (stationaryLocation != null) {
|
|
585
|
+
distance = abs(location.distanceTo(stationaryLocation) - stationaryLocation.getAccuracy() - location.getAccuracy());
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
showDebugToast("Stationary exit in " + (radius - distance) + "m");
|
|
589
|
+
|
|
590
|
+
logger.info("Distance from stationary location: {}", distance);
|
|
591
|
+
if (distance > radius) {
|
|
592
|
+
onExitStationaryRegion(location);
|
|
593
|
+
} else if (distance > 0) {
|
|
594
|
+
startPollingStationaryLocation(getStationaryPollFast());
|
|
595
|
+
} else if (stationaryLocationPollingInterval != getStationaryPollLazy()) {
|
|
596
|
+
startPollingStationaryLocation(getStationaryPollLazy());
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// ====== Receivers ======
|
|
601
|
+
|
|
602
|
+
private class StationaryAlarmReceiver extends BroadcastReceiver {
|
|
603
|
+
@Override
|
|
604
|
+
public void onReceive(Context context, Intent intent) {
|
|
605
|
+
logger.info("stationaryAlarm fired");
|
|
606
|
+
setPace(false);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
private BroadcastReceiver stationaryAlarmReceiver = new StationaryAlarmReceiver();
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Triggered by the inexact repeating alarm to poll a single fresh location while
|
|
613
|
+
* inside the stationary region. Uses FLP one-shot when available, otherwise falls
|
|
614
|
+
* back to {@link LocationManager#requestSingleUpdate(String, PendingIntent)}.
|
|
615
|
+
*/
|
|
616
|
+
private class StationaryLocationMonitorReceiver extends BroadcastReceiver {
|
|
617
|
+
@Override
|
|
618
|
+
public void onReceive(Context context, Intent intent) {
|
|
619
|
+
logger.info("Stationary location monitor fired");
|
|
620
|
+
playDebugTone(Tone.DIALTONE);
|
|
621
|
+
|
|
622
|
+
if (usingFused && fusedClient != null) {
|
|
623
|
+
LocationRequest oneShot = new LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 0L)
|
|
624
|
+
.setMaxUpdates(1)
|
|
625
|
+
.setWaitForAccurateLocation(false)
|
|
626
|
+
.build();
|
|
627
|
+
try {
|
|
628
|
+
fusedClient.requestLocationUpdates(oneShot, fusedPollCallback, Looper.getMainLooper());
|
|
629
|
+
} catch (SecurityException e) {
|
|
630
|
+
logger.error("Security exception (FLP one-shot): {}", e.getMessage());
|
|
631
|
+
} catch (IllegalArgumentException e) {
|
|
632
|
+
logger.warn("FLP one-shot failed: {}", e.getMessage());
|
|
633
|
+
}
|
|
634
|
+
} else if (locationManager != null && singleUpdatePI != null) {
|
|
635
|
+
String provider = pickProvider();
|
|
636
|
+
if (provider == null) {
|
|
637
|
+
logger.warn("Stationary monitor: no provider available");
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
try {
|
|
641
|
+
locationManager.requestSingleUpdate(provider, singleUpdatePI);
|
|
642
|
+
} catch (SecurityException e) {
|
|
643
|
+
logger.error("Security exception (single update): {}", e.getMessage());
|
|
644
|
+
} catch (IllegalArgumentException e) {
|
|
645
|
+
logger.warn("requestSingleUpdate failed: {}", e.getMessage());
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
private BroadcastReceiver stationaryLocationMonitorReceiver = new StationaryLocationMonitorReceiver();
|
|
651
|
+
|
|
652
|
+
/** Legacy single-update receiver — feeds {@link #onPollStationaryLocation(Location)}. */
|
|
653
|
+
private class SingleUpdateReceiver extends BroadcastReceiver {
|
|
654
|
+
@Override
|
|
655
|
+
public void onReceive(Context context, Intent intent) {
|
|
656
|
+
Bundle extras = intent.getExtras();
|
|
657
|
+
if (extras == null) return;
|
|
658
|
+
Location location = extras.getParcelable(LocationManager.KEY_LOCATION_CHANGED);
|
|
659
|
+
if (location != null) {
|
|
660
|
+
logger.debug("Single location update: {}", location);
|
|
661
|
+
onPollStationaryLocation(location);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
private BroadcastReceiver singleUpdateReceiver = new SingleUpdateReceiver();
|
|
666
|
+
|
|
667
|
+
@Override
|
|
668
|
+
public void onDestroy() {
|
|
669
|
+
logger.info("Destroying DistanceFilterLocationProvider");
|
|
670
|
+
|
|
671
|
+
this.onStop();
|
|
672
|
+
if (alarmManager != null) {
|
|
673
|
+
if (stationaryAlarmPI != null) alarmManager.cancel(stationaryAlarmPI);
|
|
674
|
+
if (stationaryLocationPollingPI != null) alarmManager.cancel(stationaryLocationPollingPI);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
try { unregisterReceiver(stationaryAlarmReceiver); } catch (Exception ignored) { }
|
|
678
|
+
try { unregisterReceiver(stationaryLocationMonitorReceiver); } catch (Exception ignored) { }
|
|
679
|
+
if (!usingFused) {
|
|
680
|
+
try { unregisterReceiver(singleUpdateReceiver); } catch (Exception ignored) { }
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
super.onDestroy();
|
|
684
|
+
}
|
|
685
|
+
}
|