@josuelmm/cordova-background-geolocation 4.2.3 → 4.5.2
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/.npmignore +11 -0
- package/CHANGELOG.md +261 -0
- package/README.md +306 -115
- package/android/CDVBackgroundGeolocation/src/main/java/com/marianhello/bgloc/cordova/ConfigMapper.java +34 -0
- package/android/CDVBackgroundGeolocation/src/main/java/com/tenforwardconsulting/bgloc/cordova/BackgroundGeolocationPlugin.java +61 -1
- package/android/common/src/main/AndroidManifest.xml +1 -1
- package/android/common/src/main/java/com/marianhello/bgloc/BootCompletedReceiver.java +20 -3
- package/android/common/src/main/java/com/marianhello/bgloc/Config.java +87 -1
- package/android/common/src/main/java/com/marianhello/bgloc/data/BackgroundLocation.java +94 -0
- package/android/common/src/main/java/com/marianhello/bgloc/data/ConfigJsonMapper.java +211 -0
- package/android/common/src/main/java/com/marianhello/bgloc/data/LocationTemplateFactory.java +6 -0
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationContract.java +5 -1
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationDAO.java +32 -1
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationContract.java +12 -2
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationDAO.java +33 -2
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java +15 -1
- package/android/common/src/main/java/com/marianhello/bgloc/provider/AbstractLocationProvider.java +48 -1
- package/android/common/src/main/java/com/marianhello/bgloc/provider/ActivityRecognitionLocationProvider.java +105 -6
- package/android/common/src/main/java/com/marianhello/bgloc/provider/DistanceFilterLocationProvider.java +336 -250
- package/android/common/src/main/java/com/marianhello/bgloc/provider/RawLocationProvider.java +69 -19
- package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +246 -21
- package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java +5 -2
- package/android/common/src/main/java/com/marianhello/bgloc/sync/BatchManager.java +46 -13
- package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.m +23 -1
- package/ios/common/BackgroundGeolocation/MAURActivityLocationProvider.m +208 -70
- package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +132 -5
- package/ios/common/BackgroundGeolocation/MAURBackgroundSync.m +20 -0
- package/ios/common/BackgroundGeolocation/MAURConfig.h +7 -0
- package/ios/common/BackgroundGeolocation/MAURConfig.m +37 -2
- package/ios/common/BackgroundGeolocation/MAURConfigurationContract.h +3 -0
- package/ios/common/BackgroundGeolocation/MAURConfigurationContract.m +3 -1
- package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.m +10 -1
- package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.m +15 -1
- package/ios/common/BackgroundGeolocation/MAURLocation.h +12 -0
- package/ios/common/BackgroundGeolocation/MAURLocation.m +33 -4
- package/ios/common/BackgroundGeolocation/MAURLocationContract.h +4 -0
- package/ios/common/BackgroundGeolocation/MAURLocationContract.m +5 -1
- package/ios/common/BackgroundGeolocation/MAURLocationManager.m +19 -1
- package/ios/common/BackgroundGeolocation/MAURPostLocationTask.h +9 -0
- package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +59 -1
- package/ios/common/BackgroundGeolocation/MAURRawLocationProvider.m +10 -1
- package/ios/common/BackgroundGeolocation/MAURSQLiteConfigurationDAO.m +54 -4
- package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.h +12 -0
- package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.m +125 -5
- package/package.json +31 -1
- package/plugin.xml +3 -10
- package/www/BackgroundGeolocation.d.ts +143 -3
- package/www/BackgroundGeolocation.js +11 -4
- package/CLAUDE.md +0 -56
- package/HISTORY.md +0 -871
- package/android/CDVBackgroundGeolocation/src/test/java/com/marianhello/ConfigMapperTest.java +0 -220
- package/android/common/src/androidTest/java/com/marianhello/bgloc/BackgroundGeolocationFacadeTest.java +0 -45
- package/android/common/src/androidTest/java/com/marianhello/bgloc/BatchManagerTest.java +0 -570
- package/android/common/src/androidTest/java/com/marianhello/bgloc/ConfigTest.java +0 -76
- package/android/common/src/androidTest/java/com/marianhello/bgloc/ContentProviderLocationDAOTest.java +0 -437
- package/android/common/src/androidTest/java/com/marianhello/bgloc/DBLogReaderTest.java +0 -95
- package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationContentProviderTest.java +0 -159
- package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationServiceProxyTest.java +0 -161
- package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationServiceTest.java +0 -247
- package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteConfigurationDAOTest.java +0 -200
- package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteLocationDAOTest.java +0 -457
- package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteLocationDAOThreadTest.java +0 -96
- package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteOpenHelperTest.java +0 -225
- package/android/common/src/androidTest/java/com/marianhello/bgloc/TestPluginDelegate.java +0 -46
- package/android/common/src/androidTest/java/com/marianhello/bgloc/TestResourceResolver.java +0 -14
- package/android/common/src/androidTest/java/com/marianhello/bgloc/provider/MockLocationProvider.java +0 -50
- package/android/common/src/androidTest/java/com/marianhello/bgloc/provider/TestLocationProviderFactory.java +0 -17
- package/android/common/src/androidTest/java/com/marianhello/bgloc/sqlite/SQLiteOpenHelper10.java +0 -92
- package/android/common/src/androidTest/java/com/marianhello/bgloc/test/LocationProviderTestCase.java +0 -107
- package/android/common/src/androidTest/java/com/marianhello/bgloc/test/TestConstants.java +0 -5
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/ArrayListLocationTemplateTest.java +0 -82
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/BackgroundLocationTest.java +0 -128
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/ConfigTest.java +0 -191
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/DBLogReaderTest.java +0 -37
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/HashMapLocationTemplateTest.java +0 -216
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/HttpPostServiceTest.java +0 -223
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/LocationTemplateFactoryTest.java +0 -50
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/PostLocationTaskTest.java +0 -180
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/TestHelper.java +0 -16
- package/ios/common/BackgroundGeolocation/SOMotionDetector/CHANGELOG.md +0 -2
- package/ios/common/BackgroundGeolocation/SOMotionDetector/LICENSE +0 -21
- package/ios/common/BackgroundGeolocation/SOMotionDetector/README.md +0 -135
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOLocationManager.h +0 -80
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOLocationManager.m +0 -147
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionActivity.h +0 -30
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionActivity.m +0 -42
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionDetector.h +0 -99
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionDetector.m +0 -327
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOStepDetector.h +0 -44
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOStepDetector.m +0 -94
- package/ios/common/BackgroundGeolocationTests/Info.plist +0 -24
- package/ios/common/BackgroundGeolocationTests/MAURBackgroundLocationTest.m +0 -185
- package/ios/common/BackgroundGeolocationTests/MAURConfigTest.m +0 -161
- package/ios/common/BackgroundGeolocationTests/MAURGeolocationOpenHelperTest.m +0 -102
- package/ios/common/BackgroundGeolocationTests/MAURLocationTest.m +0 -216
- package/ios/common/BackgroundGeolocationTests/MAURLocationUploaderTest.m +0 -55
- package/ios/common/BackgroundGeolocationTests/MAURLogReaderTest.m +0 -43
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteConfigurationDAOTest.m +0 -102
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteHelperTest.m +0 -41
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteLocationDAOTests.m +0 -240
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteLocationDAOThreadTest.m +0 -84
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteOpenHelperTest.m +0 -144
- package/ios/common/scripts/xcode-refactor.js +0 -184
package/android/common/src/main/java/com/marianhello/bgloc/provider/RawLocationProvider.java
CHANGED
|
@@ -8,9 +8,14 @@ import android.os.Bundle;
|
|
|
8
8
|
|
|
9
9
|
import com.marianhello.bgloc.Config;
|
|
10
10
|
|
|
11
|
+
import java.util.ArrayList;
|
|
12
|
+
import java.util.List;
|
|
13
|
+
|
|
11
14
|
public class RawLocationProvider extends AbstractLocationProvider implements LocationListener {
|
|
12
15
|
private LocationManager locationManager;
|
|
13
16
|
private boolean isStarted = false;
|
|
17
|
+
// v4.5.2: providers we actively subscribed to (so we can unsubscribe cleanly).
|
|
18
|
+
private final List<String> activeProviders = new ArrayList<>(2);
|
|
14
19
|
|
|
15
20
|
public RawLocationProvider(Context context) {
|
|
16
21
|
super(context);
|
|
@@ -37,31 +42,67 @@ public class RawLocationProvider extends AbstractLocationProvider implements Loc
|
|
|
37
42
|
logger.warn("RawLocationProvider started without config");
|
|
38
43
|
return;
|
|
39
44
|
}
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
// v4.5.2: honor desiredAccuracy and subscribe to all suitable providers
|
|
46
|
+
// simultaneously (GPS + Network when available). Previously RAW only
|
|
47
|
+
// used GPS-or-Network and ignored desiredAccuracy.
|
|
48
|
+
List<String> providers = pickProviders();
|
|
49
|
+
if (providers.isEmpty()) {
|
|
43
50
|
logger.warn("No location provider available (GPS and Network disabled)");
|
|
44
51
|
return;
|
|
45
52
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
activeProviders.clear();
|
|
54
|
+
for (String provider : providers) {
|
|
55
|
+
try {
|
|
56
|
+
logger.info("Requesting location updates from provider {}", provider);
|
|
57
|
+
locationManager.requestLocationUpdates(provider, mConfig.getInterval(), mConfig.getDistanceFilter(), this);
|
|
58
|
+
activeProviders.add(provider);
|
|
59
|
+
} catch (SecurityException e) {
|
|
60
|
+
logger.error("Security exception requesting {} updates: {}", provider, e.getMessage());
|
|
61
|
+
this.handleSecurityException(e);
|
|
62
|
+
} catch (IllegalArgumentException e) {
|
|
63
|
+
logger.warn("requestLocationUpdates({}) failed: {}", provider, e.getMessage());
|
|
64
|
+
}
|
|
53
65
|
}
|
|
66
|
+
isStarted = !activeProviders.isEmpty();
|
|
54
67
|
}
|
|
55
68
|
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
69
|
+
/**
|
|
70
|
+
* v4.5.2: choose providers based on desiredAccuracy.
|
|
71
|
+
* <ul>
|
|
72
|
+
* <li>< 1000 m → include GPS when enabled (HIGH / BALANCED)</li>
|
|
73
|
+
* <li>≥ 10 m → include Network when enabled (covers indoor and quick fixes)</li>
|
|
74
|
+
* <li>≥ 1000 m → Network-only (LOW_POWER)</li>
|
|
75
|
+
* </ul>
|
|
76
|
+
* Falls back to whatever is enabled if the preferred set is empty.
|
|
77
|
+
*/
|
|
78
|
+
private List<String> pickProviders() {
|
|
79
|
+
List<String> result = new ArrayList<>(2);
|
|
80
|
+
if (locationManager == null) return result;
|
|
81
|
+
|
|
82
|
+
Integer da = mConfig != null ? mConfig.getDesiredAccuracy() : null;
|
|
83
|
+
int desired = (da != null) ? da : 100; // default BALANCED
|
|
84
|
+
|
|
85
|
+
boolean wantGps = desired < 1000;
|
|
86
|
+
boolean wantNet = desired >= 10;
|
|
87
|
+
|
|
88
|
+
boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
|
|
89
|
+
boolean netEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
|
|
90
|
+
|
|
91
|
+
if (wantGps && gpsEnabled) result.add(LocationManager.GPS_PROVIDER);
|
|
92
|
+
if (wantNet && netEnabled) result.add(LocationManager.NETWORK_PROVIDER);
|
|
93
|
+
|
|
94
|
+
// Fallback: at least one of the available providers if our preferred set was empty.
|
|
95
|
+
if (result.isEmpty()) {
|
|
96
|
+
if (gpsEnabled) result.add(LocationManager.GPS_PROVIDER);
|
|
97
|
+
else if (netEnabled) result.add(LocationManager.NETWORK_PROVIDER);
|
|
60
98
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Backwards-compatible single-provider picker used by onProviderDisabled to check fallback. */
|
|
103
|
+
private String pickProvider() {
|
|
104
|
+
List<String> ps = pickProviders();
|
|
105
|
+
return ps.isEmpty() ? null : ps.get(0);
|
|
65
106
|
}
|
|
66
107
|
|
|
67
108
|
@Override
|
|
@@ -70,11 +111,14 @@ public class RawLocationProvider extends AbstractLocationProvider implements Loc
|
|
|
70
111
|
return;
|
|
71
112
|
}
|
|
72
113
|
try {
|
|
114
|
+
// v4.5.2: removeUpdates(this) detaches us from every provider we
|
|
115
|
+
// subscribed to via the same LocationListener.
|
|
73
116
|
locationManager.removeUpdates(this);
|
|
74
117
|
} catch (SecurityException e) {
|
|
75
118
|
logger.error("Security exception: {}", e.getMessage());
|
|
76
119
|
this.handleSecurityException(e);
|
|
77
120
|
} finally {
|
|
121
|
+
activeProviders.clear();
|
|
78
122
|
isStarted = false;
|
|
79
123
|
}
|
|
80
124
|
}
|
|
@@ -113,7 +157,13 @@ public class RawLocationProvider extends AbstractLocationProvider implements Loc
|
|
|
113
157
|
|
|
114
158
|
@Override
|
|
115
159
|
public void onProviderDisabled(String provider) {
|
|
116
|
-
logger.
|
|
160
|
+
logger.warn("Provider {} was disabled", provider);
|
|
161
|
+
// v4.5.2: emit SERVICE error when no fallback provider is available so
|
|
162
|
+
// the JS layer can re-prompt the user. Matches DISTANCE_FILTER provider
|
|
163
|
+
// behavior.
|
|
164
|
+
if (locationManager != null && pickProvider() == null) {
|
|
165
|
+
handleServiceError("Location provider '" + provider + "' disabled and no fallback available.");
|
|
166
|
+
}
|
|
117
167
|
}
|
|
118
168
|
|
|
119
169
|
@Override
|
|
@@ -36,6 +36,7 @@ import android.os.Message;
|
|
|
36
36
|
import android.os.PowerManager;
|
|
37
37
|
import android.os.Process;
|
|
38
38
|
import androidx.annotation.Nullable;
|
|
39
|
+
import androidx.core.content.ContextCompat;
|
|
39
40
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
40
41
|
|
|
41
42
|
import com.marianhello.bgloc.Config;
|
|
@@ -179,6 +180,13 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
179
180
|
private com.marianhello.bgloc.sensor.SensorFusionDetector mSensorFusion;
|
|
180
181
|
/** v4.2 Phase 8: cached tripActive state so hot-reload can re-inject it. */
|
|
181
182
|
private volatile boolean mDrivingTripActive = false;
|
|
183
|
+
/** v4.3: events fired without a simultaneous fix (providerChange, sensor crash, phone usage,
|
|
184
|
+
* manual SOS) buffered here and flushed onto the next location's `events` array.
|
|
185
|
+
* v4.4.1: capped at PENDING_DRIVING_EVENTS_MAX entries (oldest evicted) and entries older
|
|
186
|
+
* than PENDING_DRIVING_EVENTS_TTL_MS are dropped at flush time. */
|
|
187
|
+
private final org.json.JSONArray mPendingDrivingEvents = new org.json.JSONArray();
|
|
188
|
+
private static final int PENDING_DRIVING_EVENTS_MAX = 20;
|
|
189
|
+
private static final long PENDING_DRIVING_EVENTS_TTL_MS = 60_000L;
|
|
182
190
|
private static final long WATCHDOG_INTERVAL_MS = 60_000L;
|
|
183
191
|
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
|
|
184
192
|
private final Runnable mWatchdogRunnable = new Runnable() {
|
|
@@ -188,12 +196,23 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
188
196
|
if (!Boolean.TRUE.equals(mConfig.getEnableWatchdog())) return;
|
|
189
197
|
long now = System.currentTimeMillis();
|
|
190
198
|
if (mLastLocationTime > 0 && (now - mLastLocationTime) > WATCHDOG_INTERVAL_MS) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
// v4.5.1: when drivingEvents is enabled, treat "no fixes" while NOT tripActive as
|
|
200
|
+
// intentional stationary → don't restart (saves battery). When drivingEvents is
|
|
201
|
+
// disabled (the plugin has no notion of "trip"), keep the legacy behaviour of
|
|
202
|
+
// restarting on every stale window.
|
|
203
|
+
Config.DrivingEventsOptions de = mConfig.getDrivingEvents();
|
|
204
|
+
boolean drivingEnabled = de != null && de.enabled;
|
|
205
|
+
boolean shouldRestart = !drivingEnabled || mDrivingTripActive;
|
|
206
|
+
if (shouldRestart) {
|
|
207
|
+
logger.info("Location watchdog: no update in {}s, restarting provider", WATCHDOG_INTERVAL_MS / 1000);
|
|
208
|
+
try {
|
|
209
|
+
mProvider.onStop();
|
|
210
|
+
mProvider.onStart();
|
|
211
|
+
} catch (Exception e) {
|
|
212
|
+
logger.warn("Watchdog restart failed", e);
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
logger.debug("Location watchdog: stationary (no active trip); skipping restart");
|
|
197
216
|
}
|
|
198
217
|
}
|
|
199
218
|
mMainHandler.postDelayed(this, WATCHDOG_INTERVAL_MS);
|
|
@@ -476,9 +495,12 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
476
495
|
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG);
|
|
477
496
|
}
|
|
478
497
|
}
|
|
479
|
-
|
|
498
|
+
// v4.5.1: only hold a permanent CPU wake lock when wakeLockMode == 'always'.
|
|
499
|
+
// Default 'posting' acquires only briefly during onLocation/post; 'none' never.
|
|
500
|
+
String wlMode = mConfig.getWakeLockMode() != null ? mConfig.getWakeLockMode() : "posting";
|
|
501
|
+
if ("always".equals(wlMode) && mWakeLock != null && !mWakeLock.isHeld()) {
|
|
480
502
|
mWakeLock.acquire();
|
|
481
|
-
logger.debug("Wake lock acquired");
|
|
503
|
+
logger.debug("Wake lock acquired (mode=always)");
|
|
482
504
|
}
|
|
483
505
|
|
|
484
506
|
if (Boolean.TRUE.equals(mConfig.getEnableWatchdog())) {
|
|
@@ -536,18 +558,21 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
536
558
|
mDrivingDetector = new com.marianhello.bgloc.driving.DrivingEventsDetector(
|
|
537
559
|
new com.marianhello.bgloc.driving.DrivingEventsDetector.Listener() {
|
|
538
560
|
@Override public void onMoving(BackgroundLocation l) {
|
|
561
|
+
attachDrivingEvent(l, "moving", null);
|
|
539
562
|
Bundle b = new Bundle();
|
|
540
563
|
b.putInt("action", MSG_ON_MOVING);
|
|
541
564
|
if (l != null) b.putParcelable("payload", l);
|
|
542
565
|
broadcastMessage(b);
|
|
543
566
|
}
|
|
544
567
|
@Override public void onStopped(BackgroundLocation l) {
|
|
568
|
+
attachDrivingEvent(l, "stopped", null);
|
|
545
569
|
Bundle b = new Bundle();
|
|
546
570
|
b.putInt("action", MSG_ON_STOPPED);
|
|
547
571
|
if (l != null) b.putParcelable("payload", l);
|
|
548
572
|
broadcastMessage(b);
|
|
549
573
|
}
|
|
550
574
|
@Override public void onTripStart(BackgroundLocation l) {
|
|
575
|
+
attachDrivingEvent(l, "tripStart", null);
|
|
551
576
|
Bundle b = new Bundle();
|
|
552
577
|
b.putInt("action", MSG_ON_TRIP_START);
|
|
553
578
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -556,6 +581,9 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
556
581
|
if (mSensorFusion != null) mSensorFusion.setTripActive(true);
|
|
557
582
|
}
|
|
558
583
|
@Override public void onTripEnd(BackgroundLocation l, double distance, long durationMs) {
|
|
584
|
+
org.json.JSONObject extra = new org.json.JSONObject();
|
|
585
|
+
try { extra.put("distance", distance); extra.put("durationMs", durationMs); } catch (org.json.JSONException ignored) {}
|
|
586
|
+
attachDrivingEvent(l, "tripEnd", extra);
|
|
559
587
|
Bundle b = new Bundle();
|
|
560
588
|
b.putInt("action", MSG_ON_TRIP_END);
|
|
561
589
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -566,6 +594,9 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
566
594
|
if (mSensorFusion != null) mSensorFusion.setTripActive(false);
|
|
567
595
|
}
|
|
568
596
|
@Override public void onSpeeding(BackgroundLocation l, double speedKmh, double limitKmh) {
|
|
597
|
+
org.json.JSONObject extra = new org.json.JSONObject();
|
|
598
|
+
try { extra.put("speedKmh", speedKmh); extra.put("limitKmh", limitKmh); } catch (org.json.JSONException ignored) {}
|
|
599
|
+
attachDrivingEvent(l, "speeding", extra);
|
|
569
600
|
Bundle b = new Bundle();
|
|
570
601
|
b.putInt("action", MSG_ON_SPEEDING);
|
|
571
602
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -574,12 +605,19 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
574
605
|
broadcastMessage(b);
|
|
575
606
|
}
|
|
576
607
|
@Override public void onProviderChange(String provider) {
|
|
608
|
+
// No location associated; buffer for next fix.
|
|
609
|
+
org.json.JSONObject ev = new org.json.JSONObject();
|
|
610
|
+
try { ev.put("type", "providerChange"); ev.put("provider", provider != null ? provider : ""); ev.put("time", System.currentTimeMillis()); } catch (org.json.JSONException ignored) {}
|
|
611
|
+
enqueuePendingDrivingEvent(ev);
|
|
577
612
|
Bundle b = new Bundle();
|
|
578
613
|
b.putInt("action", MSG_ON_PROVIDER_CHANGE);
|
|
579
614
|
b.putString("provider", provider != null ? provider : "");
|
|
580
615
|
broadcastMessage(b);
|
|
581
616
|
}
|
|
582
617
|
@Override public void onHardBrake(BackgroundLocation l, double decelMps2) {
|
|
618
|
+
org.json.JSONObject extra = new org.json.JSONObject();
|
|
619
|
+
try { extra.put("value", decelMps2); } catch (org.json.JSONException ignored) {}
|
|
620
|
+
attachDrivingEvent(l, "hardBrake", extra);
|
|
583
621
|
Bundle b = new Bundle();
|
|
584
622
|
b.putInt("action", MSG_ON_HARD_BRAKE);
|
|
585
623
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -587,6 +625,9 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
587
625
|
broadcastMessage(b);
|
|
588
626
|
}
|
|
589
627
|
@Override public void onRapidAcceleration(BackgroundLocation l, double accelMps2) {
|
|
628
|
+
org.json.JSONObject extra = new org.json.JSONObject();
|
|
629
|
+
try { extra.put("value", accelMps2); } catch (org.json.JSONException ignored) {}
|
|
630
|
+
attachDrivingEvent(l, "rapidAcceleration", extra);
|
|
590
631
|
Bundle b = new Bundle();
|
|
591
632
|
b.putInt("action", MSG_ON_RAPID_ACCELERATION);
|
|
592
633
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -594,6 +635,9 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
594
635
|
broadcastMessage(b);
|
|
595
636
|
}
|
|
596
637
|
@Override public void onSharpTurn(BackgroundLocation l, double degPerSec) {
|
|
638
|
+
org.json.JSONObject extra = new org.json.JSONObject();
|
|
639
|
+
try { extra.put("value", degPerSec); } catch (org.json.JSONException ignored) {}
|
|
640
|
+
attachDrivingEvent(l, "sharpTurn", extra);
|
|
597
641
|
Bundle b = new Bundle();
|
|
598
642
|
b.putInt("action", MSG_ON_SHARP_TURN);
|
|
599
643
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -601,6 +645,9 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
601
645
|
broadcastMessage(b);
|
|
602
646
|
}
|
|
603
647
|
@Override public void onPossibleCrash(BackgroundLocation l, double velocityDropKmh) {
|
|
648
|
+
org.json.JSONObject extra = new org.json.JSONObject();
|
|
649
|
+
try { extra.put("value", velocityDropKmh); extra.put("source", "gps"); } catch (org.json.JSONException ignored) {}
|
|
650
|
+
attachDrivingEvent(l, "possibleCrash", extra);
|
|
604
651
|
Bundle b = new Bundle();
|
|
605
652
|
b.putInt("action", MSG_ON_POSSIBLE_CRASH);
|
|
606
653
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -638,6 +685,15 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
638
685
|
com.marianhello.bgloc.sensor.SensorFusionDetector.Listener l =
|
|
639
686
|
new com.marianhello.bgloc.sensor.SensorFusionDetector.Listener() {
|
|
640
687
|
@Override public void onSensorCrash(BackgroundLocation lastLocation, double impactG) {
|
|
688
|
+
// Buffer for next fix (sensor events fire async to GPS).
|
|
689
|
+
try {
|
|
690
|
+
org.json.JSONObject ev = new org.json.JSONObject();
|
|
691
|
+
ev.put("type", "possibleCrash");
|
|
692
|
+
ev.put("value", impactG);
|
|
693
|
+
ev.put("source", "sensor");
|
|
694
|
+
ev.put("time", System.currentTimeMillis());
|
|
695
|
+
enqueuePendingDrivingEvent(ev);
|
|
696
|
+
} catch (org.json.JSONException ignored) {}
|
|
641
697
|
Bundle b = new Bundle();
|
|
642
698
|
b.putInt("action", MSG_ON_POSSIBLE_CRASH);
|
|
643
699
|
if (lastLocation != null) b.putParcelable("payload", lastLocation);
|
|
@@ -646,6 +702,12 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
646
702
|
broadcastMessage(b);
|
|
647
703
|
}
|
|
648
704
|
@Override public void onPhoneUsageWhileDriving(BackgroundLocation lastLocation) {
|
|
705
|
+
try {
|
|
706
|
+
org.json.JSONObject ev = new org.json.JSONObject();
|
|
707
|
+
ev.put("type", "phoneUsageWhileDriving");
|
|
708
|
+
ev.put("time", System.currentTimeMillis());
|
|
709
|
+
enqueuePendingDrivingEvent(ev);
|
|
710
|
+
} catch (org.json.JSONException ignored) {}
|
|
649
711
|
Bundle b = new Bundle();
|
|
650
712
|
b.putInt("action", MSG_ON_PHONE_USAGE_WHILE_DRIVING);
|
|
651
713
|
if (lastLocation != null) b.putParcelable("payload", lastLocation);
|
|
@@ -673,6 +735,89 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
673
735
|
if (sIsRunning) mSensorFusion.start();
|
|
674
736
|
}
|
|
675
737
|
|
|
738
|
+
/** v4.3 — append a {type, time, ...extra} entry to the location's events array. */
|
|
739
|
+
private void attachDrivingEvent(BackgroundLocation loc, String type, org.json.JSONObject extra) {
|
|
740
|
+
if (loc == null || type == null) return;
|
|
741
|
+
try {
|
|
742
|
+
org.json.JSONObject ev = new org.json.JSONObject();
|
|
743
|
+
ev.put("type", type);
|
|
744
|
+
ev.put("time", System.currentTimeMillis());
|
|
745
|
+
if (extra != null) {
|
|
746
|
+
java.util.Iterator<String> keys = extra.keys();
|
|
747
|
+
while (keys.hasNext()) {
|
|
748
|
+
String k = keys.next();
|
|
749
|
+
ev.put(k, extra.opt(k));
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
loc.addDrivingEvent(ev);
|
|
753
|
+
} catch (org.json.JSONException ignored) {}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/** v4.5.1 — acquire a short, time-bounded wake lock when wakeLockMode is 'posting'. */
|
|
757
|
+
private void acquireWakeLockForPosting() {
|
|
758
|
+
if (mWakeLock == null || mConfig == null) return;
|
|
759
|
+
String mode = mConfig.getWakeLockMode() != null ? mConfig.getWakeLockMode() : "posting";
|
|
760
|
+
if (!"posting".equals(mode)) return;
|
|
761
|
+
try {
|
|
762
|
+
// Bounded: SQLite write + HTTP POST should finish well within 30 s.
|
|
763
|
+
if (!mWakeLock.isHeld()) mWakeLock.acquire(30_000L);
|
|
764
|
+
} catch (Throwable ignored) { /* best-effort */ }
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/** v4.4 — read current device battery via sticky broadcast and stamp it onto the location.
|
|
768
|
+
* No permission required. Sticky broadcast returns instantly without blocking.
|
|
769
|
+
* v4.4.1: route through the application context to bypass our own registerReceiver()
|
|
770
|
+
* override (which forces RECEIVER_NOT_EXPORTED + handler — incompatible with sticky-only reads). */
|
|
771
|
+
private void attachBatterySnapshot(BackgroundLocation loc) {
|
|
772
|
+
if (loc == null) return;
|
|
773
|
+
try {
|
|
774
|
+
android.content.IntentFilter filter = new android.content.IntentFilter(android.content.Intent.ACTION_BATTERY_CHANGED);
|
|
775
|
+
android.content.Intent batteryStatus = getApplicationContext().registerReceiver(null, filter);
|
|
776
|
+
if (batteryStatus == null) return;
|
|
777
|
+
int level = batteryStatus.getIntExtra(android.os.BatteryManager.EXTRA_LEVEL, -1);
|
|
778
|
+
int scale = batteryStatus.getIntExtra(android.os.BatteryManager.EXTRA_SCALE, -1);
|
|
779
|
+
if (level >= 0 && scale > 0) {
|
|
780
|
+
loc.setBatteryLevel((int) Math.round(level * 100.0 / scale));
|
|
781
|
+
}
|
|
782
|
+
int status = batteryStatus.getIntExtra(android.os.BatteryManager.EXTRA_STATUS, -1);
|
|
783
|
+
boolean charging = (status == android.os.BatteryManager.BATTERY_STATUS_CHARGING
|
|
784
|
+
|| status == android.os.BatteryManager.BATTERY_STATUS_FULL);
|
|
785
|
+
loc.setCharging(charging);
|
|
786
|
+
} catch (Throwable ignored) { /* best-effort; never fail the fix */ }
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/** v4.3 — drain pending events (those fired without a simultaneous fix) onto this location.
|
|
790
|
+
* v4.4.1: drop entries older than PENDING_DRIVING_EVENTS_TTL_MS so we don't anexar an event
|
|
791
|
+
* whose context (location, speed, etc.) is no longer relevant. */
|
|
792
|
+
private void flushPendingDrivingEvents(BackgroundLocation loc) {
|
|
793
|
+
if (loc == null) return;
|
|
794
|
+
long now = System.currentTimeMillis();
|
|
795
|
+
synchronized (mPendingDrivingEvents) {
|
|
796
|
+
int n = mPendingDrivingEvents.length();
|
|
797
|
+
if (n == 0) return;
|
|
798
|
+
for (int i = 0; i < n; i++) {
|
|
799
|
+
org.json.JSONObject ev = mPendingDrivingEvents.optJSONObject(i);
|
|
800
|
+
if (ev == null) continue;
|
|
801
|
+
long t = ev.optLong("time", now);
|
|
802
|
+
if (now - t <= PENDING_DRIVING_EVENTS_TTL_MS) {
|
|
803
|
+
loc.addDrivingEvent(ev);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
for (int i = n - 1; i >= 0; i--) mPendingDrivingEvents.remove(i);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/** v4.4.1 — append to pending events with cap (oldest evicted). */
|
|
811
|
+
private void enqueuePendingDrivingEvent(org.json.JSONObject ev) {
|
|
812
|
+
if (ev == null) return;
|
|
813
|
+
synchronized (mPendingDrivingEvents) {
|
|
814
|
+
while (mPendingDrivingEvents.length() >= PENDING_DRIVING_EVENTS_MAX) {
|
|
815
|
+
mPendingDrivingEvents.remove(0);
|
|
816
|
+
}
|
|
817
|
+
mPendingDrivingEvents.put(ev);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
676
821
|
@Override
|
|
677
822
|
public synchronized void startForegroundService() {
|
|
678
823
|
start();
|
|
@@ -725,8 +870,9 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
725
870
|
* Required before starting a location foreground service on API 34+.
|
|
726
871
|
*/
|
|
727
872
|
private boolean hasLocationPermission() {
|
|
728
|
-
|
|
729
|
-
|
|
873
|
+
// v4.5.1 — ContextCompat handles API < 23 (permissions granted at install time).
|
|
874
|
+
return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
|
875
|
+
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
|
730
876
|
}
|
|
731
877
|
|
|
732
878
|
/**
|
|
@@ -791,16 +937,32 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
791
937
|
mProvider.onCommand(LocationProvider.CMD_SWITCH_MODE,
|
|
792
938
|
LocationProvider.FOREGROUND_MODE);
|
|
793
939
|
}
|
|
794
|
-
// Android 14+ (API 34): type
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
940
|
+
// Android 14+ (API 34): type is required. Android 12-13 (API 31-33): type accepted (preferred).
|
|
941
|
+
// FOREGROUND_SERVICE_TYPE_LOCATION = 0x00000008. Resolve from merged manifest first;
|
|
942
|
+
// if reflection fails or manifest merge missed the attribute, fall back to LOCATION
|
|
943
|
+
// hardcoded so the FGS still promotes (otherwise: no notification, no background tracking).
|
|
944
|
+
try {
|
|
945
|
+
if (Build.VERSION.SDK_INT >= 30) {
|
|
946
|
+
int type = getManifestForegroundServiceType();
|
|
947
|
+
if (type == 0) {
|
|
948
|
+
// Defensive fallback: every consumer of this plugin requires location FGS.
|
|
949
|
+
// Logging at warn so the failure is visible without breaking the service.
|
|
950
|
+
logger.warn("Manifest foregroundServiceType unreadable; defaulting to LOCATION (0x8). "
|
|
951
|
+
+ "Verify merged AndroidManifest has foregroundServiceType=\"location\"." );
|
|
952
|
+
type = 0x00000008; // ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION
|
|
953
|
+
}
|
|
954
|
+
super.startForeground(NOTIFICATION_ID, notification, type);
|
|
955
|
+
} else {
|
|
956
|
+
super.startForeground(NOTIFICATION_ID, notification);
|
|
957
|
+
}
|
|
958
|
+
} catch (Throwable t) {
|
|
959
|
+
logger.error("startForeground threw {}; retrying without type", t.getMessage());
|
|
960
|
+
try {
|
|
961
|
+
super.startForeground(NOTIFICATION_ID, notification);
|
|
962
|
+
} catch (Throwable t2) {
|
|
963
|
+
logger.error("startForeground retry failed: {}", t2.getMessage());
|
|
799
964
|
return;
|
|
800
965
|
}
|
|
801
|
-
super.startForeground(NOTIFICATION_ID, notification, type);
|
|
802
|
-
} else {
|
|
803
|
-
super.startForeground(NOTIFICATION_ID, notification);
|
|
804
966
|
}
|
|
805
967
|
mIsInForeground = true;
|
|
806
968
|
scheduleNotificationUpdater();
|
|
@@ -955,6 +1117,26 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
955
1117
|
if (!equalsDrivingEvents(prevDe, newDe)) {
|
|
956
1118
|
configureDrivingDetector();
|
|
957
1119
|
}
|
|
1120
|
+
// v4.5.1 — hot-reload wakeLockMode: when transitioning between always /
|
|
1121
|
+
// posting / none, the existing permanent lock (if any) must be released,
|
|
1122
|
+
// or a new permanent lock acquired. Without this, switching mode at runtime
|
|
1123
|
+
// either leaked CPU or left the service running without the requested lock.
|
|
1124
|
+
String prevWl = currentConfig.getWakeLockMode() != null ? currentConfig.getWakeLockMode() : "posting";
|
|
1125
|
+
String newWl = mConfig.getWakeLockMode() != null ? mConfig.getWakeLockMode() : "posting";
|
|
1126
|
+
if (!prevWl.equals(newWl) && mWakeLock != null) {
|
|
1127
|
+
if ("always".equals(newWl)) {
|
|
1128
|
+
if (!mWakeLock.isHeld()) {
|
|
1129
|
+
try { mWakeLock.acquire(); logger.debug("Wake lock acquired (hot-reload → always)"); }
|
|
1130
|
+
catch (Throwable t) { logger.warn("Wake lock acquire failed", t); }
|
|
1131
|
+
}
|
|
1132
|
+
} else {
|
|
1133
|
+
// 'posting' or 'none' — release any permanent lock; per-fix lock continues to work via acquireWakeLockForPosting().
|
|
1134
|
+
if (mWakeLock.isHeld()) {
|
|
1135
|
+
try { mWakeLock.release(); logger.debug("Wake lock released (hot-reload → {})", newWl); }
|
|
1136
|
+
catch (Throwable t) { logger.warn("Wake lock release failed", t); }
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
958
1140
|
}
|
|
959
1141
|
}
|
|
960
1142
|
});
|
|
@@ -1022,15 +1204,19 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
1022
1204
|
|
|
1023
1205
|
@Override
|
|
1024
1206
|
public void onLocation(BackgroundLocation location) {
|
|
1207
|
+
// v4.5.1: in 'posting' wake-lock mode, hold the CPU briefly so SQLite writes + HTTP
|
|
1208
|
+
// POST finish before the system returns to deep sleep. 30s ceiling — plenty for a fix.
|
|
1209
|
+
acquireWakeLockForPosting();
|
|
1025
1210
|
mLastLocationTime = System.currentTimeMillis();
|
|
1026
1211
|
mLastReceivedLocation = location;
|
|
1027
1212
|
sLastReceivedLocation = location;
|
|
1028
1213
|
|
|
1029
|
-
// v4.0 Phase 6: feed the driver-insights state machine
|
|
1214
|
+
// v4.0 Phase 6: feed the driver-insights state machine on the *raw* location so speed/bearing
|
|
1215
|
+
// come straight from the sensors. Listener attaches events to this same instance.
|
|
1030
1216
|
if (mDrivingDetector != null) {
|
|
1031
1217
|
mDrivingDetector.onLocation(location);
|
|
1032
1218
|
}
|
|
1033
|
-
// v4.2 Phase 8: keep sensor pipeline aware of the latest fix.
|
|
1219
|
+
// v4.2 Phase 8: keep sensor pipeline aware of the latest raw fix.
|
|
1034
1220
|
if (mSensorFusion != null) {
|
|
1035
1221
|
mSensorFusion.setLastLocation(location);
|
|
1036
1222
|
}
|
|
@@ -1056,12 +1242,38 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
1056
1242
|
}
|
|
1057
1243
|
logger.debug("New location {}", location.toString());
|
|
1058
1244
|
|
|
1245
|
+
// v4.5.1 — events were attached to the RAW location above (so detector heuristics see real
|
|
1246
|
+
// speed/bearing). If transformLocation() returns a NEW instance, we'd lose those events
|
|
1247
|
+
// and the battery snapshot. Solution: copy them across to the transformed instance below.
|
|
1248
|
+
org.json.JSONArray rawEvents = location.getDrivingEvents();
|
|
1249
|
+
Integer rawBatteryLevel = null;
|
|
1250
|
+
Boolean rawIsCharging = null;
|
|
1251
|
+
|
|
1059
1252
|
location = transformLocation(location);
|
|
1060
1253
|
if (location == null) {
|
|
1061
1254
|
logger.debug("Skipping location as requested by the locationTransform");
|
|
1062
1255
|
return;
|
|
1063
1256
|
}
|
|
1064
1257
|
|
|
1258
|
+
// Re-attach events to the transformed location if the transform produced a new instance.
|
|
1259
|
+
if (rawEvents != null && rawEvents.length() > 0 && location.getDrivingEvents() != rawEvents) {
|
|
1260
|
+
try {
|
|
1261
|
+
org.json.JSONArray copy = new org.json.JSONArray(rawEvents.toString());
|
|
1262
|
+
for (int i = 0; i < copy.length(); i++) {
|
|
1263
|
+
org.json.JSONObject ev = copy.optJSONObject(i);
|
|
1264
|
+
if (ev != null) location.addDrivingEvent(ev);
|
|
1265
|
+
}
|
|
1266
|
+
} catch (org.json.JSONException ignored) {}
|
|
1267
|
+
}
|
|
1268
|
+
// v4.3: drain pending events (providerChange/sensor crash/phone usage) onto the post-transform
|
|
1269
|
+
// instance so they always reach the backend.
|
|
1270
|
+
flushPendingDrivingEvents(location);
|
|
1271
|
+
// v4.4: stamp device battery snapshot onto the *transformed* location so it survives any
|
|
1272
|
+
// user-supplied locationTransform that creates a new instance.
|
|
1273
|
+
if (mConfig == null || !Boolean.FALSE.equals(mConfig.getIncludeBattery())) {
|
|
1274
|
+
attachBatterySnapshot(location);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1065
1277
|
Bundle bundle = new Bundle();
|
|
1066
1278
|
bundle.putInt("action", MSG_ON_LOCATION);
|
|
1067
1279
|
bundle.putParcelable("payload", location);
|
|
@@ -1091,6 +1303,11 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
1091
1303
|
logger.debug("Skipping location as requested by the locationTransform");
|
|
1092
1304
|
return;
|
|
1093
1305
|
}
|
|
1306
|
+
// v4.5.1 — same enrichment as regular fixes: drain pending events and stamp battery.
|
|
1307
|
+
flushPendingDrivingEvents(location);
|
|
1308
|
+
if (mConfig == null || !Boolean.FALSE.equals(mConfig.getIncludeBattery())) {
|
|
1309
|
+
attachBatterySnapshot(location);
|
|
1310
|
+
}
|
|
1094
1311
|
|
|
1095
1312
|
Bundle bundle = new Bundle();
|
|
1096
1313
|
bundle.putInt("action", MSG_ON_STATIONARY);
|
|
@@ -1190,7 +1407,15 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
1190
1407
|
|
|
1191
1408
|
@Override
|
|
1192
1409
|
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
|
|
1193
|
-
|
|
1410
|
+
// v4.5.1 — RECEIVER_NOT_EXPORTED flag is required on Android 13+ (API 33) for non-system
|
|
1411
|
+
// broadcasts and the 5-arg overload exists only from API 26. Guard for older OSs.
|
|
1412
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
1413
|
+
return super.registerReceiver(receiver, filter, null, mServiceHandler, Context.RECEIVER_NOT_EXPORTED);
|
|
1414
|
+
}
|
|
1415
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
1416
|
+
return super.registerReceiver(receiver, filter, null, mServiceHandler);
|
|
1417
|
+
}
|
|
1418
|
+
return super.registerReceiver(receiver, filter);
|
|
1194
1419
|
}
|
|
1195
1420
|
|
|
1196
1421
|
@Override
|
package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java
CHANGED
|
@@ -7,6 +7,8 @@ import android.content.pm.PackageManager;
|
|
|
7
7
|
import android.os.Build;
|
|
8
8
|
import android.util.Log;
|
|
9
9
|
|
|
10
|
+
import androidx.core.content.ContextCompat;
|
|
11
|
+
|
|
10
12
|
import com.marianhello.bgloc.Config;
|
|
11
13
|
|
|
12
14
|
public class LocationServiceProxy implements LocationService, LocationServiceInfo {
|
|
@@ -100,8 +102,9 @@ public class LocationServiceProxy implements LocationService, LocationServiceInf
|
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
private boolean hasLocationPermission() {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
// v4.5.1 — ContextCompat handles API < 23 safely.
|
|
106
|
+
return ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
|
107
|
+
|| ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
@Override
|