@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
|
@@ -14,6 +14,7 @@ package com.tenforwardconsulting.bgloc.cordova;
|
|
|
14
14
|
import android.app.Activity;
|
|
15
15
|
import android.app.Application;
|
|
16
16
|
import android.content.Context;
|
|
17
|
+
import android.os.Build;
|
|
17
18
|
|
|
18
19
|
import com.marianhello.bgloc.BackgroundGeolocationFacade;
|
|
19
20
|
import com.marianhello.bgloc.Config;
|
|
@@ -87,9 +88,15 @@ public class BackgroundGeolocationPlugin extends CordovaPlugin implements Plugin
|
|
|
87
88
|
public static final String ACTION_OPEN_AUTOSTART_SETTINGS = "openAutoStartSettings";
|
|
88
89
|
public static final String ACTION_GET_MANUFACTURER_HELP = "getManufacturerHelp";
|
|
89
90
|
public static final String ACTION_TRIGGER_SOS = "triggerSOS";
|
|
91
|
+
// v4.5: runtime permission helpers — opt-in. The app drives the flow; the plugin
|
|
92
|
+
// simply asks the OS dialog (or returns the current state on iOS where Apple does
|
|
93
|
+
// not surface separate runtime gates for background location / activity recognition).
|
|
94
|
+
public static final String ACTION_REQUEST_BACKGROUND_PERMISSION = "requestBackgroundLocationPermission";
|
|
95
|
+
public static final String ACTION_REQUEST_ACTIVITY_PERMISSION = "requestActivityRecognitionPermission";
|
|
96
|
+
public static final String ACTION_REQUEST_NOTIFICATION_PERMISSION = "requestNotificationPermission";
|
|
90
97
|
|
|
91
98
|
/** Plugin version; keep in sync with plugin.xml. */
|
|
92
|
-
public static final String PLUGIN_VERSION = "4.2
|
|
99
|
+
public static final String PLUGIN_VERSION = "4.5.2";
|
|
93
100
|
|
|
94
101
|
private BackgroundGeolocationFacade facade;
|
|
95
102
|
|
|
@@ -507,11 +514,64 @@ public class BackgroundGeolocationPlugin extends CordovaPlugin implements Plugin
|
|
|
507
514
|
callbackContext.sendPluginResult(ErrorPluginResult.from("triggerSOS failed", e, PluginException.SERVICE_ERROR));
|
|
508
515
|
}
|
|
509
516
|
return true;
|
|
517
|
+
} else if (ACTION_REQUEST_BACKGROUND_PERMISSION.equals(action)) {
|
|
518
|
+
// Android 10+ (API 29+). Returns {granted: bool}.
|
|
519
|
+
return requestPermissionAction(callbackContext,
|
|
520
|
+
Build.VERSION.SDK_INT >= 29 ? android.Manifest.permission.ACCESS_BACKGROUND_LOCATION : android.Manifest.permission.ACCESS_FINE_LOCATION);
|
|
521
|
+
} else if (ACTION_REQUEST_ACTIVITY_PERMISSION.equals(action)) {
|
|
522
|
+
// Android 10+ (API 29+) needs runtime grant for activity recognition.
|
|
523
|
+
return requestPermissionAction(callbackContext,
|
|
524
|
+
Build.VERSION.SDK_INT >= 29 ? "android.permission.ACTIVITY_RECOGNITION" : null);
|
|
525
|
+
} else if (ACTION_REQUEST_NOTIFICATION_PERMISSION.equals(action)) {
|
|
526
|
+
// Android 13+ (API 33+) requires POST_NOTIFICATIONS at runtime.
|
|
527
|
+
return requestPermissionAction(callbackContext,
|
|
528
|
+
Build.VERSION.SDK_INT >= 33 ? "android.permission.POST_NOTIFICATIONS" : null);
|
|
510
529
|
}
|
|
511
530
|
|
|
512
531
|
return false;
|
|
513
532
|
}
|
|
514
533
|
|
|
534
|
+
/** v4.5: shared helper — request a single runtime permission via PermissionManager.
|
|
535
|
+
* Returns {granted: true} if already granted (or unsupported on this OS version).
|
|
536
|
+
* Returns {granted: false, denied: [name]} if user denies.
|
|
537
|
+
*/
|
|
538
|
+
private boolean requestPermissionAction(final CallbackContext cb, final String permission) {
|
|
539
|
+
if (permission == null) {
|
|
540
|
+
// OS version where this permission does not exist: act as already granted.
|
|
541
|
+
try {
|
|
542
|
+
JSONObject r = new JSONObject();
|
|
543
|
+
r.put("granted", true);
|
|
544
|
+
r.put("notRequired", true);
|
|
545
|
+
cb.success(r);
|
|
546
|
+
} catch (JSONException e) { cb.success(); }
|
|
547
|
+
return true;
|
|
548
|
+
}
|
|
549
|
+
Context ctx = cordova.getActivity().getApplicationContext();
|
|
550
|
+
if (hasPermission(ctx, permission)) {
|
|
551
|
+
try { JSONObject r = new JSONObject(); r.put("granted", true); cb.success(r); }
|
|
552
|
+
catch (JSONException e) { cb.success(); }
|
|
553
|
+
return true;
|
|
554
|
+
}
|
|
555
|
+
com.intentfilter.androidpermissions.PermissionManager pm =
|
|
556
|
+
com.intentfilter.androidpermissions.PermissionManager.getInstance(ctx);
|
|
557
|
+
pm.checkPermissions(java.util.Arrays.asList(permission),
|
|
558
|
+
new com.intentfilter.androidpermissions.PermissionManager.PermissionRequestListener() {
|
|
559
|
+
@Override public void onPermissionGranted() {
|
|
560
|
+
try { JSONObject r = new JSONObject(); r.put("granted", true); cb.success(r); }
|
|
561
|
+
catch (JSONException e) { cb.success(); }
|
|
562
|
+
}
|
|
563
|
+
@Override public void onPermissionDenied(com.intentfilter.androidpermissions.models.DeniedPermissions deniedPermissions) {
|
|
564
|
+
try {
|
|
565
|
+
JSONObject r = new JSONObject();
|
|
566
|
+
r.put("granted", false);
|
|
567
|
+
r.put("denied", new org.json.JSONArray().put(permission));
|
|
568
|
+
cb.success(r);
|
|
569
|
+
} catch (JSONException e) { cb.success(); }
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
return true;
|
|
573
|
+
}
|
|
574
|
+
|
|
515
575
|
/** v3.5 Phase 4: extended diagnostics. */
|
|
516
576
|
private JSONObject buildDiagnostics() throws JSONException {
|
|
517
577
|
JSONObject d = new JSONObject();
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
<uses-library android:name="org.apache.http.legacy" android:required="true" />
|
|
59
59
|
</application>
|
|
60
60
|
|
|
61
|
-
<uses-
|
|
61
|
+
<uses-feature android:name="android.hardware.location" android:required="false" />
|
|
62
62
|
<uses-permission android:name="android.permission.INTERNET"/>
|
|
63
63
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
64
64
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
@@ -17,6 +17,8 @@ import android.content.pm.PackageManager;
|
|
|
17
17
|
import android.os.Build;
|
|
18
18
|
import android.util.Log;
|
|
19
19
|
|
|
20
|
+
import androidx.core.content.ContextCompat;
|
|
21
|
+
|
|
20
22
|
import com.marianhello.bgloc.data.ConfigurationDAO;
|
|
21
23
|
import com.marianhello.bgloc.data.DAOFactory;
|
|
22
24
|
import com.marianhello.bgloc.service.LocationServiceImpl;
|
|
@@ -32,6 +34,20 @@ public class BootCompletedReceiver extends BroadcastReceiver {
|
|
|
32
34
|
@Override
|
|
33
35
|
public void onReceive(Context context, Intent intent) {
|
|
34
36
|
String action = intent != null ? intent.getAction() : null;
|
|
37
|
+
|
|
38
|
+
// v4.5.2 — hardening: ignore arbitrary broadcasts directed at this
|
|
39
|
+
// receiver. Without this, any explicit intent (e.g. a malicious app
|
|
40
|
+
// targeting our package) could trigger the service auto-start path.
|
|
41
|
+
// Accept only the canonical boot/package-replaced actions plus the
|
|
42
|
+
// OEM-specific quick-boot variants used by HTC and Samsung.
|
|
43
|
+
if (!Intent.ACTION_BOOT_COMPLETED.equals(action)
|
|
44
|
+
&& !"android.intent.action.QUICKBOOT_POWERON".equals(action)
|
|
45
|
+
&& !"com.htc.intent.action.QUICKBOOT_POWERON".equals(action)
|
|
46
|
+
&& !Intent.ACTION_MY_PACKAGE_REPLACED.equals(action)) {
|
|
47
|
+
Log.w(TAG, "Ignoring unsupported broadcast: " + action);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
35
51
|
Log.d(TAG, "Received boot/replace broadcast: " + action);
|
|
36
52
|
ConfigurationDAO dao = DAOFactory.createConfigurationDAO(context);
|
|
37
53
|
Config config = null;
|
|
@@ -76,11 +92,12 @@ public class BootCompletedReceiver extends BroadcastReceiver {
|
|
|
76
92
|
}
|
|
77
93
|
|
|
78
94
|
private static boolean hasLocationPermission(Context context) {
|
|
79
|
-
|
|
80
|
-
|
|
95
|
+
// v4.5.1 — ContextCompat handles API < 23 (always granted at install time).
|
|
96
|
+
return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
|
97
|
+
|| ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
|
81
98
|
}
|
|
82
99
|
|
|
83
100
|
private static boolean hasBackgroundLocationPermission(Context context) {
|
|
84
|
-
return
|
|
101
|
+
return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
|
85
102
|
}
|
|
86
103
|
}
|
|
@@ -83,6 +83,22 @@ public class Config implements Parcelable
|
|
|
83
83
|
private String mockLocationPolicy; // allow | flag | drop (default allow)
|
|
84
84
|
// v4.0 (Phase 6): driver insights
|
|
85
85
|
private DrivingEventsOptions drivingEvents;
|
|
86
|
+
// v4.4: include device battery in every location payload (default true).
|
|
87
|
+
private Boolean includeBattery;
|
|
88
|
+
// v4.5.1: battery-saving knobs.
|
|
89
|
+
/** WakeLock policy: 'none' | 'posting' | 'always'. Default 'posting'. */
|
|
90
|
+
private String wakeLockMode;
|
|
91
|
+
/** ms before declaring stationary. DistanceFilterLocationProvider default 5*60_000. */
|
|
92
|
+
private Integer stationaryTimeout;
|
|
93
|
+
/** Lazy poll interval while stationary (ms). Default 3*60_000. */
|
|
94
|
+
private Integer stationaryPollInterval;
|
|
95
|
+
/** Aggressive poll interval while stationary (ms). Default 60_000. */
|
|
96
|
+
private Integer stationaryPollFast;
|
|
97
|
+
// v4.5.2 — provider hardening
|
|
98
|
+
/** 0-100. Activity-recognition transitions below this confidence are ignored. Default 50. */
|
|
99
|
+
private Integer activityConfidenceThreshold;
|
|
100
|
+
/** Discard fixes whose `accuracy` (m) is worse than this. `null` (default) disables the filter. */
|
|
101
|
+
private Float maxAcceptedAccuracy;
|
|
86
102
|
|
|
87
103
|
/** v4.0 Phase 6 + v4.1: driver-insights configuration. Plain holder; no Parcelable to keep this class diff small. */
|
|
88
104
|
public static class DrivingEventsOptions {
|
|
@@ -149,6 +165,13 @@ public class Config implements Parcelable
|
|
|
149
165
|
this.queryParams = CloneHelper.deepCopy(config.queryParams);
|
|
150
166
|
this.heartbeatInterval = config.heartbeatInterval;
|
|
151
167
|
this.mockLocationPolicy = config.mockLocationPolicy;
|
|
168
|
+
this.includeBattery = config.includeBattery;
|
|
169
|
+
this.wakeLockMode = config.wakeLockMode;
|
|
170
|
+
this.stationaryTimeout = config.stationaryTimeout;
|
|
171
|
+
this.stationaryPollInterval = config.stationaryPollInterval;
|
|
172
|
+
this.stationaryPollFast = config.stationaryPollFast;
|
|
173
|
+
this.activityConfidenceThreshold = config.activityConfidenceThreshold;
|
|
174
|
+
this.maxAcceptedAccuracy = config.maxAcceptedAccuracy;
|
|
152
175
|
if (config.drivingEvents != null) {
|
|
153
176
|
DrivingEventsOptions de = new DrivingEventsOptions();
|
|
154
177
|
de.enabled = config.drivingEvents.enabled;
|
|
@@ -251,7 +274,19 @@ public class Config implements Parcelable
|
|
|
251
274
|
de.phoneUsageCooldownMs = dePhoneUsageCooldown;
|
|
252
275
|
this.drivingEvents = de;
|
|
253
276
|
}
|
|
254
|
-
|
|
277
|
+
// v4.4: includeBattery
|
|
278
|
+
setIncludeBattery((Boolean) in.readValue(null));
|
|
279
|
+
// v4.5.1: battery-saving knobs
|
|
280
|
+
setWakeLockMode(in.readString());
|
|
281
|
+
setStationaryTimeout((Integer) in.readValue(null));
|
|
282
|
+
setStationaryPollInterval((Integer) in.readValue(null));
|
|
283
|
+
setStationaryPollFast((Integer) in.readValue(null));
|
|
284
|
+
// v4.5.2 provider hardening
|
|
285
|
+
setActivityConfidenceThreshold((Integer) in.readValue(null));
|
|
286
|
+
setMaxAcceptedAccuracy((Float) in.readValue(null));
|
|
287
|
+
// v4.5.1 — pass the plugin's classloader so getSerializable() can deserialize
|
|
288
|
+
// LocationTemplate / HashMap subclasses across IPC boundaries (e.g. SyncService :sync process).
|
|
289
|
+
Bundle bundle = in.readBundle(Config.class.getClassLoader());
|
|
255
290
|
setHttpHeaders((HashMap<String, String>) bundle.getSerializable("httpHeaders"));
|
|
256
291
|
setQueryParams((HashMap<String, String>) bundle.getSerializable("queryParams"));
|
|
257
292
|
setTemplate((LocationTemplate) bundle.getSerializable(AbstractLocationTemplate.BUNDLE_KEY));
|
|
@@ -298,6 +333,13 @@ public class Config implements Parcelable
|
|
|
298
333
|
config.queryParams = null;
|
|
299
334
|
config.heartbeatInterval = 0;
|
|
300
335
|
config.mockLocationPolicy = "allow";
|
|
336
|
+
config.includeBattery = true; // v4.4: on by default
|
|
337
|
+
config.wakeLockMode = "posting"; // v4.5.1: hold wake lock only while posting/syncing
|
|
338
|
+
config.stationaryTimeout = 5 * 60 * 1000;
|
|
339
|
+
config.stationaryPollInterval = 3 * 60 * 1000;
|
|
340
|
+
config.stationaryPollFast = 60 * 1000;
|
|
341
|
+
config.activityConfidenceThreshold = 50; // v4.5.2: ignore <50% confidence transitions
|
|
342
|
+
config.maxAcceptedAccuracy = null; // v4.5.2: off by default (no JS regression)
|
|
301
343
|
|
|
302
344
|
return config;
|
|
303
345
|
}
|
|
@@ -365,6 +407,16 @@ public class Config implements Parcelable
|
|
|
365
407
|
out.writeLong (de != null ? de.phoneUsageWindowMs : 4_000L);
|
|
366
408
|
out.writeLong (de != null ? de.phoneUsageCooldownMs : 60_000L);
|
|
367
409
|
out.writeInt (de != null ? 1 : 0);
|
|
410
|
+
// v4.4: includeBattery
|
|
411
|
+
out.writeValue(getIncludeBattery());
|
|
412
|
+
// v4.5.1
|
|
413
|
+
out.writeString(getWakeLockMode());
|
|
414
|
+
out.writeValue(getStationaryTimeout());
|
|
415
|
+
out.writeValue(getStationaryPollInterval());
|
|
416
|
+
out.writeValue(getStationaryPollFast());
|
|
417
|
+
// v4.5.2
|
|
418
|
+
out.writeValue(getActivityConfidenceThreshold());
|
|
419
|
+
out.writeValue(getMaxAcceptedAccuracy());
|
|
368
420
|
Bundle bundle = new Bundle();
|
|
369
421
|
bundle.putSerializable("httpHeaders", getHttpHeaders());
|
|
370
422
|
bundle.putSerializable("queryParams", getQueryParams());
|
|
@@ -762,6 +814,28 @@ public class Config implements Parcelable
|
|
|
762
814
|
this.enableWatchdog = enableWatchdog;
|
|
763
815
|
}
|
|
764
816
|
|
|
817
|
+
@Nullable
|
|
818
|
+
public Boolean getIncludeBattery() {
|
|
819
|
+
return includeBattery;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
public void setIncludeBattery(Boolean includeBattery) {
|
|
823
|
+
this.includeBattery = includeBattery;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
@Nullable public String getWakeLockMode() { return wakeLockMode; }
|
|
827
|
+
public void setWakeLockMode(String mode) { this.wakeLockMode = mode; }
|
|
828
|
+
@Nullable public Integer getStationaryTimeout() { return stationaryTimeout; }
|
|
829
|
+
public void setStationaryTimeout(Integer ms) { this.stationaryTimeout = ms; }
|
|
830
|
+
@Nullable public Integer getStationaryPollInterval() { return stationaryPollInterval; }
|
|
831
|
+
public void setStationaryPollInterval(Integer ms) { this.stationaryPollInterval = ms; }
|
|
832
|
+
@Nullable public Integer getStationaryPollFast() { return stationaryPollFast; }
|
|
833
|
+
public void setStationaryPollFast(Integer ms) { this.stationaryPollFast = ms; }
|
|
834
|
+
@Nullable public Integer getActivityConfidenceThreshold() { return activityConfidenceThreshold; }
|
|
835
|
+
public void setActivityConfidenceThreshold(Integer v) { this.activityConfidenceThreshold = v; }
|
|
836
|
+
@Nullable public Float getMaxAcceptedAccuracy() { return maxAcceptedAccuracy; }
|
|
837
|
+
public void setMaxAcceptedAccuracy(Float v) { this.maxAcceptedAccuracy = v; }
|
|
838
|
+
|
|
765
839
|
public boolean hasShowTime() {
|
|
766
840
|
return showTime != null;
|
|
767
841
|
}
|
|
@@ -1056,6 +1130,18 @@ public class Config implements Parcelable
|
|
|
1056
1130
|
if (config2.drivingEvents != null) {
|
|
1057
1131
|
merger.setDrivingEvents(config2.drivingEvents);
|
|
1058
1132
|
}
|
|
1133
|
+
// v4.4.1 — was missing: configure({includeBattery: false}) was being ignored.
|
|
1134
|
+
if (config2.includeBattery != null) {
|
|
1135
|
+
merger.setIncludeBattery(config2.getIncludeBattery());
|
|
1136
|
+
}
|
|
1137
|
+
// v4.5.1 — battery-saving knobs.
|
|
1138
|
+
if (config2.wakeLockMode != null) merger.setWakeLockMode(config2.wakeLockMode);
|
|
1139
|
+
if (config2.stationaryTimeout != null) merger.setStationaryTimeout(config2.stationaryTimeout);
|
|
1140
|
+
if (config2.stationaryPollInterval != null) merger.setStationaryPollInterval(config2.stationaryPollInterval);
|
|
1141
|
+
if (config2.stationaryPollFast != null) merger.setStationaryPollFast(config2.stationaryPollFast);
|
|
1142
|
+
// v4.5.2
|
|
1143
|
+
if (config2.activityConfidenceThreshold != null) merger.setActivityConfidenceThreshold(config2.activityConfidenceThreshold);
|
|
1144
|
+
if (config2.maxAcceptedAccuracy != null) merger.setMaxAcceptedAccuracy(config2.maxAcceptedAccuracy);
|
|
1059
1145
|
|
|
1060
1146
|
return merger;
|
|
1061
1147
|
}
|
|
@@ -11,6 +11,7 @@ import androidx.core.util.TimeUtils;
|
|
|
11
11
|
|
|
12
12
|
import com.marianhello.bgloc.data.sqlite.SQLiteLocationContract.LocationEntry;
|
|
13
13
|
|
|
14
|
+
import org.json.JSONArray;
|
|
14
15
|
import org.json.JSONException;
|
|
15
16
|
import org.json.JSONObject;
|
|
16
17
|
|
|
@@ -43,6 +44,20 @@ public class BackgroundLocation implements Parcelable {
|
|
|
43
44
|
private int status = POST_PENDING;
|
|
44
45
|
private Bundle extras = null;
|
|
45
46
|
|
|
47
|
+
/**
|
|
48
|
+
* v4.3 — Driving events anexados al fix actual.
|
|
49
|
+
* v4.5: ahora se persiste en SQLite (events_json TEXT) y se propaga vía Parcel para
|
|
50
|
+
* sobrevivir a la cola de sync. Si el POST en real-time falla, los eventos llegan al
|
|
51
|
+
* backend cuando la location se sincroniza más tarde.
|
|
52
|
+
*/
|
|
53
|
+
private JSONArray drivingEvents;
|
|
54
|
+
|
|
55
|
+
/** v4.4 — Device battery percentage (0-100) at the time of this fix, or null if unknown
|
|
56
|
+
* or {@code includeBattery} is disabled. v4.5: persisted in SQLite + Parcel. */
|
|
57
|
+
private Integer batteryLevel;
|
|
58
|
+
/** v4.4 — Whether the device is charging at the time of this fix. v4.5: persisted. */
|
|
59
|
+
private Boolean isCharging;
|
|
60
|
+
|
|
46
61
|
private static final long TWO_MINUTES_IN_NANOS = 1000000000L * 60 * 2;
|
|
47
62
|
|
|
48
63
|
public BackgroundLocation() {}
|
|
@@ -106,6 +121,12 @@ public class BackgroundLocation implements Parcelable {
|
|
|
106
121
|
mockFlags = l.mockFlags;
|
|
107
122
|
status = l.status;
|
|
108
123
|
extras = (l.extras == null) ? null : new Bundle(l.extras);
|
|
124
|
+
// v4.5: copy v4.3+ persisted fields
|
|
125
|
+
if (l.drivingEvents != null) {
|
|
126
|
+
try { drivingEvents = new JSONArray(l.drivingEvents.toString()); } catch (JSONException ignored) {}
|
|
127
|
+
}
|
|
128
|
+
batteryLevel = l.batteryLevel;
|
|
129
|
+
isCharging = l.isCharging;
|
|
109
130
|
}
|
|
110
131
|
|
|
111
132
|
private static BackgroundLocation fromParcel(Parcel in) {
|
|
@@ -134,6 +155,13 @@ public class BackgroundLocation implements Parcelable {
|
|
|
134
155
|
l.mockFlags = in.readInt();
|
|
135
156
|
l.status = in.readInt();
|
|
136
157
|
l.extras = in.readBundle();
|
|
158
|
+
// v4.5: read driving events / battery / charging
|
|
159
|
+
String evJson = in.readString();
|
|
160
|
+
if (evJson != null) {
|
|
161
|
+
try { l.drivingEvents = new JSONArray(evJson); } catch (JSONException ignored) {}
|
|
162
|
+
}
|
|
163
|
+
l.batteryLevel = (Integer) in.readValue(null);
|
|
164
|
+
l.isCharging = (Boolean) in.readValue(null);
|
|
137
165
|
|
|
138
166
|
return l;
|
|
139
167
|
}
|
|
@@ -205,6 +233,18 @@ public class BackgroundLocation implements Parcelable {
|
|
|
205
233
|
l.setStatus(c.getInt(c.getColumnIndex(LocationEntry.COLUMN_NAME_STATUS)));
|
|
206
234
|
l.setLocationId(c.getLong(c.getColumnIndex(LocationEntry._ID)));
|
|
207
235
|
l.setMockFlags(c.getInt((c.getColumnIndex(LocationEntry.COLUMN_NAME_MOCK_FLAGS))));
|
|
236
|
+
// v4.5: events / battery / charging — guarded for DBs that may have NULL after migration.
|
|
237
|
+
int idxEv = c.getColumnIndex(LocationEntry.COLUMN_NAME_EVENTS_JSON);
|
|
238
|
+
if (idxEv >= 0 && !c.isNull(idxEv)) {
|
|
239
|
+
String s = c.getString(idxEv);
|
|
240
|
+
if (s != null && !s.isEmpty()) {
|
|
241
|
+
try { l.drivingEvents = new JSONArray(s); } catch (JSONException ignored) {}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
int idxBat = c.getColumnIndex(LocationEntry.COLUMN_NAME_BATTERY_LEVEL);
|
|
245
|
+
if (idxBat >= 0 && !c.isNull(idxBat)) l.batteryLevel = c.getInt(idxBat);
|
|
246
|
+
int idxChg = c.getColumnIndex(LocationEntry.COLUMN_NAME_IS_CHARGING);
|
|
247
|
+
if (idxChg >= 0 && !c.isNull(idxChg)) l.isCharging = (c.getInt(idxChg) == 1);
|
|
208
248
|
|
|
209
249
|
return l;
|
|
210
250
|
}
|
|
@@ -239,6 +279,10 @@ public class BackgroundLocation implements Parcelable {
|
|
|
239
279
|
dest.writeInt(mockFlags);
|
|
240
280
|
dest.writeInt(status);
|
|
241
281
|
dest.writeBundle(extras);
|
|
282
|
+
// v4.5: persist driving events / battery / charging through Parcel.
|
|
283
|
+
dest.writeString(drivingEvents != null ? drivingEvents.toString() : null);
|
|
284
|
+
dest.writeValue(batteryLevel);
|
|
285
|
+
dest.writeValue(isCharging);
|
|
242
286
|
}
|
|
243
287
|
|
|
244
288
|
public static final Parcelable.Creator<BackgroundLocation> CREATOR
|
|
@@ -899,10 +943,37 @@ public class BackgroundLocation implements Parcelable {
|
|
|
899
943
|
if (hasRadius) json.put("radius", radius);
|
|
900
944
|
if (hasIsFromMockProvider()) json.put("isFromMockProvider", isFromMockProvider());
|
|
901
945
|
if (hasMockLocationsEnabled()) json.put("mockLocationsEnabled", areMockLocationsEnabled());
|
|
946
|
+
// v4.3: driving events anexados a este fix (si los hay).
|
|
947
|
+
if (drivingEvents != null && drivingEvents.length() > 0) {
|
|
948
|
+
json.put("events", drivingEvents);
|
|
949
|
+
}
|
|
950
|
+
// v4.4: device battery snapshot.
|
|
951
|
+
if (batteryLevel != null) json.put("battery", batteryLevel);
|
|
952
|
+
if (isCharging != null) json.put("isCharging", isCharging);
|
|
902
953
|
|
|
903
954
|
return json;
|
|
904
955
|
}
|
|
905
956
|
|
|
957
|
+
// v4.3 — driving event helpers
|
|
958
|
+
/** Append a driving event to this location. The event survives only until the next
|
|
959
|
+
* serialization in real-time POST. NOT persisted in SQLite. */
|
|
960
|
+
public void addDrivingEvent(JSONObject event) {
|
|
961
|
+
if (event == null) return;
|
|
962
|
+
if (drivingEvents == null) drivingEvents = new JSONArray();
|
|
963
|
+
drivingEvents.put(event);
|
|
964
|
+
}
|
|
965
|
+
public JSONArray getDrivingEvents() { return drivingEvents; }
|
|
966
|
+
public boolean hasDrivingEvents() { return drivingEvents != null && drivingEvents.length() > 0; }
|
|
967
|
+
public void clearDrivingEvents() { drivingEvents = null; }
|
|
968
|
+
/** v4.5: bulk setter used by SQLite hydration to restore the persisted events array. */
|
|
969
|
+
public void setDrivingEvents(JSONArray events) { this.drivingEvents = events; }
|
|
970
|
+
|
|
971
|
+
// v4.4 — battery helpers
|
|
972
|
+
public void setBatteryLevel(Integer level) { this.batteryLevel = level; }
|
|
973
|
+
public Integer getBatteryLevel() { return batteryLevel; }
|
|
974
|
+
public void setCharging(Boolean charging) { this.isCharging = charging; }
|
|
975
|
+
public Boolean isCharging() { return isCharging; }
|
|
976
|
+
|
|
906
977
|
/**
|
|
907
978
|
* Returns location as JSON object containing location id
|
|
908
979
|
* Note: Location id is not unique and is usually being recycled when
|
|
@@ -942,6 +1013,18 @@ public class BackgroundLocation implements Parcelable {
|
|
|
942
1013
|
values.put(LocationEntry.COLUMN_NAME_STATUS, status);
|
|
943
1014
|
values.put(LocationEntry.COLUMN_NAME_BATCH_START_MILLIS, batchStartMillis);
|
|
944
1015
|
values.put(LocationEntry.COLUMN_NAME_MOCK_FLAGS, mockFlags);
|
|
1016
|
+
// v4.5.1 — always write these columns (with NULL when absent) so that recycled rows
|
|
1017
|
+
// in ContentProviderLocationDAO's max-rows UPDATE path do not inherit stale events,
|
|
1018
|
+
// battery or charging state from the location previously stored at that _id.
|
|
1019
|
+
if (drivingEvents != null && drivingEvents.length() > 0) {
|
|
1020
|
+
values.put(LocationEntry.COLUMN_NAME_EVENTS_JSON, drivingEvents.toString());
|
|
1021
|
+
} else {
|
|
1022
|
+
values.putNull(LocationEntry.COLUMN_NAME_EVENTS_JSON);
|
|
1023
|
+
}
|
|
1024
|
+
if (batteryLevel != null) values.put(LocationEntry.COLUMN_NAME_BATTERY_LEVEL, batteryLevel);
|
|
1025
|
+
else values.putNull(LocationEntry.COLUMN_NAME_BATTERY_LEVEL);
|
|
1026
|
+
if (isCharging != null) values.put(LocationEntry.COLUMN_NAME_IS_CHARGING, isCharging ? 1 : 0);
|
|
1027
|
+
else values.putNull(LocationEntry.COLUMN_NAME_IS_CHARGING);
|
|
945
1028
|
return values;
|
|
946
1029
|
}
|
|
947
1030
|
|
|
@@ -988,6 +1071,17 @@ public class BackgroundLocation implements Parcelable {
|
|
|
988
1071
|
if ("@mockLocationsEnabled".equals(key)) {
|
|
989
1072
|
return hasMockLocationsEnabled() ? areMockLocationsEnabled() : JSONObject.NULL;
|
|
990
1073
|
}
|
|
1074
|
+
// v4.3 — driving events array (only present if events were attached during this fix).
|
|
1075
|
+
if ("@events".equals(key)) {
|
|
1076
|
+
return drivingEvents != null ? drivingEvents : JSONObject.NULL;
|
|
1077
|
+
}
|
|
1078
|
+
// v4.4 — battery snapshot
|
|
1079
|
+
if ("@battery".equals(key)) {
|
|
1080
|
+
return batteryLevel != null ? batteryLevel : JSONObject.NULL;
|
|
1081
|
+
}
|
|
1082
|
+
if ("@isCharging".equals(key)) {
|
|
1083
|
+
return isCharging != null ? isCharging : JSONObject.NULL;
|
|
1084
|
+
}
|
|
991
1085
|
|
|
992
1086
|
return null;
|
|
993
1087
|
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
package com.marianhello.bgloc.data;
|
|
2
|
+
|
|
3
|
+
import com.marianhello.bgloc.Config;
|
|
4
|
+
|
|
5
|
+
import org.json.JSONException;
|
|
6
|
+
import org.json.JSONObject;
|
|
7
|
+
|
|
8
|
+
import java.util.HashMap;
|
|
9
|
+
import java.util.Iterator;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* v4.4.1 — JSON serializer/deserializer for the full {@link Config} state.
|
|
13
|
+
*
|
|
14
|
+
* Lives in {@code common} so both the SQLite DAO (also common) and the Cordova
|
|
15
|
+
* {@code ConfigMapper} (cordova) can reuse it without creating a common→cordova
|
|
16
|
+
* dependency. Used to persist a single {@code config_json} TEXT column instead of
|
|
17
|
+
* adding one schema column per new field on every release.
|
|
18
|
+
*
|
|
19
|
+
* Round-trip: every JS-configurable key the plugin understands is preserved.
|
|
20
|
+
* Anything not present in the input JSON keeps the {@link Config} default.
|
|
21
|
+
*/
|
|
22
|
+
public final class ConfigJsonMapper {
|
|
23
|
+
|
|
24
|
+
private ConfigJsonMapper() {}
|
|
25
|
+
|
|
26
|
+
/** Serialize the current Config state to a JSONObject suitable for storage.
|
|
27
|
+
* String fields use {@link JSONObject#NULL} when the user explicitly cleared them
|
|
28
|
+
* (i.e. equals {@link Config#NullString}) so the sentinel survives the round-trip. */
|
|
29
|
+
public static JSONObject toJSONObject(Config c) throws JSONException {
|
|
30
|
+
JSONObject j = new JSONObject();
|
|
31
|
+
if (c == null) return j;
|
|
32
|
+
j.put("stationaryRadius", c.getStationaryRadius());
|
|
33
|
+
j.put("distanceFilter", c.getDistanceFilter());
|
|
34
|
+
j.put("desiredAccuracy", c.getDesiredAccuracy());
|
|
35
|
+
j.put("debug", c.isDebugging());
|
|
36
|
+
j.put("notificationTitle", nullable(c.getNotificationTitle()));
|
|
37
|
+
j.put("notificationText", nullable(c.getNotificationText()));
|
|
38
|
+
j.put("notificationSyncTitle", nullable(c.getNotificationSyncTitle()));
|
|
39
|
+
j.put("notificationSyncText", nullable(c.getNotificationSyncText()));
|
|
40
|
+
j.put("notificationSyncCompletedText", nullable(c.getNotificationSyncCompletedText()));
|
|
41
|
+
j.put("notificationSyncFailedText", nullable(c.getNotificationSyncFailedText()));
|
|
42
|
+
j.put("notificationIconLarge", nullable(c.getLargeNotificationIcon()));
|
|
43
|
+
j.put("notificationIconSmall", nullable(c.getSmallNotificationIcon()));
|
|
44
|
+
j.put("notificationIconColor", nullable(c.getNotificationIconColor()));
|
|
45
|
+
j.put("locationProvider", c.getLocationProvider());
|
|
46
|
+
j.put("interval", c.getInterval());
|
|
47
|
+
j.put("fastestInterval", c.getFastestInterval());
|
|
48
|
+
j.put("activitiesInterval", c.getActivitiesInterval());
|
|
49
|
+
j.put("stopOnTerminate", c.getStopOnTerminate());
|
|
50
|
+
j.put("startOnBoot", c.getStartOnBoot());
|
|
51
|
+
j.put("startForeground", c.getStartForeground());
|
|
52
|
+
j.put("notificationsEnabled", c.getNotificationsEnabled());
|
|
53
|
+
j.put("stopOnStillActivity", c.getStopOnStillActivity());
|
|
54
|
+
j.put("url", nullable(c.getUrl()));
|
|
55
|
+
j.put("syncUrl", nullable(c.getSyncUrl()));
|
|
56
|
+
j.put("syncThreshold", c.getSyncThreshold());
|
|
57
|
+
j.put("syncEnabled", c.getSyncEnabled());
|
|
58
|
+
j.put("maxLocations", c.getMaxLocations());
|
|
59
|
+
j.put("enableWatchdog", c.getEnableWatchdog());
|
|
60
|
+
j.put("showTime", c.getShowTime());
|
|
61
|
+
j.put("showDistance", c.getShowDistance());
|
|
62
|
+
j.put("httpMethod", c.getHttpMethod());
|
|
63
|
+
j.put("syncHttpMethod", c.getSyncHttpMethod());
|
|
64
|
+
j.put("httpMode", c.getHttpMode());
|
|
65
|
+
j.put("syncMode", c.getSyncMode());
|
|
66
|
+
j.put("heartbeatInterval", c.getHeartbeatInterval());
|
|
67
|
+
j.put("mockLocationPolicy", c.getMockLocationPolicy());
|
|
68
|
+
j.put("includeBattery", c.getIncludeBattery());
|
|
69
|
+
// v4.5.1: battery knobs
|
|
70
|
+
j.put("wakeLockMode", c.getWakeLockMode());
|
|
71
|
+
j.put("stationaryTimeout", c.getStationaryTimeout());
|
|
72
|
+
j.put("stationaryPollInterval", c.getStationaryPollInterval());
|
|
73
|
+
j.put("stationaryPollFast", c.getStationaryPollFast());
|
|
74
|
+
// v4.5.2: provider hardening knobs
|
|
75
|
+
j.put("activityConfidenceThreshold", c.getActivityConfidenceThreshold());
|
|
76
|
+
j.put("maxAcceptedAccuracy", c.getMaxAcceptedAccuracy());
|
|
77
|
+
|
|
78
|
+
if (c.getHttpHeaders() != null) {
|
|
79
|
+
j.put("httpHeaders", new JSONObject(c.getHttpHeaders()));
|
|
80
|
+
}
|
|
81
|
+
if (c.getQueryParams() != null) {
|
|
82
|
+
j.put("queryParams", new JSONObject(c.getQueryParams()));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
Config.DrivingEventsOptions de = c.getDrivingEvents();
|
|
86
|
+
if (de != null) {
|
|
87
|
+
JSONObject deJson = new JSONObject();
|
|
88
|
+
deJson.put("enabled", de.enabled);
|
|
89
|
+
deJson.put("speedLimit", de.speedLimitKmh);
|
|
90
|
+
deJson.put("minMovingSpeed", de.minMovingSpeedMps);
|
|
91
|
+
deJson.put("stoppedDuration", de.stoppedDurationMs);
|
|
92
|
+
deJson.put("minTripSpeed", de.minTripSpeedMps);
|
|
93
|
+
deJson.put("minTripDuration", de.minTripDurationMs);
|
|
94
|
+
deJson.put("hardBrakeMps2", de.hardBrakeMps2);
|
|
95
|
+
deJson.put("rapidAccelMps2", de.rapidAccelMps2);
|
|
96
|
+
deJson.put("sharpTurnDegPerSec", de.sharpTurnDegPerSec);
|
|
97
|
+
deJson.put("crashImpactKmh", de.crashImpactKmh);
|
|
98
|
+
deJson.put("crashWindowMs", de.crashWindowMs);
|
|
99
|
+
deJson.put("sensorFusion", de.sensorFusion);
|
|
100
|
+
deJson.put("crashImpactG", de.crashImpactG);
|
|
101
|
+
deJson.put("sensorCrashCooldownMs", de.sensorCrashCooldownMs);
|
|
102
|
+
deJson.put("phoneUsageWindowMs", de.phoneUsageWindowMs);
|
|
103
|
+
deJson.put("phoneUsageCooldownMs", de.phoneUsageCooldownMs);
|
|
104
|
+
j.put("drivingEvents", deJson);
|
|
105
|
+
}
|
|
106
|
+
return j;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Deserialize a previously serialized config JSON. Missing keys are skipped (defaults preserved). */
|
|
110
|
+
public static Config fromJSONObject(JSONObject j) throws JSONException {
|
|
111
|
+
Config c = Config.getDefault();
|
|
112
|
+
if (j == null) return c;
|
|
113
|
+
if (j.has("stationaryRadius")) c.setStationaryRadius((float) j.getDouble("stationaryRadius"));
|
|
114
|
+
if (j.has("distanceFilter")) c.setDistanceFilter(j.getInt("distanceFilter"));
|
|
115
|
+
if (j.has("desiredAccuracy")) c.setDesiredAccuracy(j.getInt("desiredAccuracy"));
|
|
116
|
+
if (j.has("debug")) c.setDebugging(j.getBoolean("debug"));
|
|
117
|
+
if (j.has("notificationTitle")) c.setNotificationTitle(readNullable(j, "notificationTitle"));
|
|
118
|
+
if (j.has("notificationText")) c.setNotificationText(readNullable(j, "notificationText"));
|
|
119
|
+
if (j.has("notificationSyncTitle")) c.setNotificationSyncTitle(readNullable(j, "notificationSyncTitle"));
|
|
120
|
+
if (j.has("notificationSyncText")) c.setNotificationSyncText(readNullable(j, "notificationSyncText"));
|
|
121
|
+
if (j.has("notificationSyncCompletedText")) c.setNotificationSyncCompletedText(readNullable(j, "notificationSyncCompletedText"));
|
|
122
|
+
if (j.has("notificationSyncFailedText")) c.setNotificationSyncFailedText(readNullable(j, "notificationSyncFailedText"));
|
|
123
|
+
if (j.has("notificationIconLarge")) c.setLargeNotificationIcon(readNullable(j, "notificationIconLarge"));
|
|
124
|
+
if (j.has("notificationIconSmall")) c.setSmallNotificationIcon(readNullable(j, "notificationIconSmall"));
|
|
125
|
+
if (j.has("notificationIconColor")) c.setNotificationIconColor(readNullable(j, "notificationIconColor"));
|
|
126
|
+
if (j.has("locationProvider")) c.setLocationProvider(j.getInt("locationProvider"));
|
|
127
|
+
if (j.has("interval")) c.setInterval(j.getInt("interval"));
|
|
128
|
+
if (j.has("fastestInterval")) c.setFastestInterval(j.getInt("fastestInterval"));
|
|
129
|
+
if (j.has("activitiesInterval")) c.setActivitiesInterval(j.getInt("activitiesInterval"));
|
|
130
|
+
if (j.has("stopOnTerminate")) c.setStopOnTerminate(j.getBoolean("stopOnTerminate"));
|
|
131
|
+
if (j.has("startOnBoot")) c.setStartOnBoot(j.getBoolean("startOnBoot"));
|
|
132
|
+
if (j.has("startForeground")) c.setStartForeground(j.getBoolean("startForeground"));
|
|
133
|
+
if (j.has("notificationsEnabled")) c.setNotificationsEnabled(j.getBoolean("notificationsEnabled"));
|
|
134
|
+
if (j.has("stopOnStillActivity")) c.setStopOnStillActivity(j.getBoolean("stopOnStillActivity"));
|
|
135
|
+
if (j.has("url")) c.setUrl(readNullable(j, "url"));
|
|
136
|
+
if (j.has("syncUrl")) c.setSyncUrl(readNullable(j, "syncUrl"));
|
|
137
|
+
if (j.has("syncThreshold")) c.setSyncThreshold(j.getInt("syncThreshold"));
|
|
138
|
+
if (j.has("syncEnabled")) c.setSyncEnabled(j.getBoolean("syncEnabled"));
|
|
139
|
+
if (j.has("maxLocations")) c.setMaxLocations(j.getInt("maxLocations"));
|
|
140
|
+
if (j.has("enableWatchdog")) c.setEnableWatchdog(j.getBoolean("enableWatchdog"));
|
|
141
|
+
if (j.has("showTime")) c.setShowTime(j.getBoolean("showTime"));
|
|
142
|
+
if (j.has("showDistance")) c.setShowDistance(j.getBoolean("showDistance"));
|
|
143
|
+
if (has(j, "httpMethod")) c.setHttpMethod(j.getString("httpMethod"));
|
|
144
|
+
if (has(j, "syncHttpMethod")) c.setSyncHttpMethod(j.getString("syncHttpMethod"));
|
|
145
|
+
if (has(j, "httpMode")) c.setHttpMode(j.getString("httpMode"));
|
|
146
|
+
if (has(j, "syncMode")) c.setSyncMode(j.getString("syncMode"));
|
|
147
|
+
if (j.has("heartbeatInterval")) c.setHeartbeatInterval(j.getInt("heartbeatInterval"));
|
|
148
|
+
if (has(j, "mockLocationPolicy")) c.setMockLocationPolicy(j.getString("mockLocationPolicy"));
|
|
149
|
+
if (j.has("includeBattery")) c.setIncludeBattery(j.getBoolean("includeBattery"));
|
|
150
|
+
// v4.5.1: battery knobs
|
|
151
|
+
if (has(j, "wakeLockMode")) c.setWakeLockMode(j.getString("wakeLockMode"));
|
|
152
|
+
if (j.has("stationaryTimeout") && !j.isNull("stationaryTimeout")) c.setStationaryTimeout(j.getInt("stationaryTimeout"));
|
|
153
|
+
if (j.has("stationaryPollInterval") && !j.isNull("stationaryPollInterval")) c.setStationaryPollInterval(j.getInt("stationaryPollInterval"));
|
|
154
|
+
if (j.has("stationaryPollFast") && !j.isNull("stationaryPollFast")) c.setStationaryPollFast(j.getInt("stationaryPollFast"));
|
|
155
|
+
// v4.5.2
|
|
156
|
+
if (j.has("activityConfidenceThreshold") && !j.isNull("activityConfidenceThreshold")) c.setActivityConfidenceThreshold(j.getInt("activityConfidenceThreshold"));
|
|
157
|
+
if (j.has("maxAcceptedAccuracy") && !j.isNull("maxAcceptedAccuracy")) c.setMaxAcceptedAccuracy((float) j.getDouble("maxAcceptedAccuracy"));
|
|
158
|
+
|
|
159
|
+
if (has(j, "httpHeaders")) c.setHttpHeaders(jsonToHashMap(j.getJSONObject("httpHeaders")));
|
|
160
|
+
if (has(j, "queryParams")) c.setQueryParams(jsonToHashMap(j.getJSONObject("queryParams")));
|
|
161
|
+
|
|
162
|
+
if (has(j, "drivingEvents")) {
|
|
163
|
+
JSONObject de = j.getJSONObject("drivingEvents");
|
|
164
|
+
Config.DrivingEventsOptions o = new Config.DrivingEventsOptions();
|
|
165
|
+
if (de.has("enabled")) o.enabled = de.getBoolean("enabled");
|
|
166
|
+
if (de.has("speedLimit")) o.speedLimitKmh = de.getDouble("speedLimit");
|
|
167
|
+
if (de.has("minMovingSpeed")) o.minMovingSpeedMps = de.getDouble("minMovingSpeed");
|
|
168
|
+
if (de.has("stoppedDuration")) o.stoppedDurationMs = de.getLong("stoppedDuration");
|
|
169
|
+
if (de.has("minTripSpeed")) o.minTripSpeedMps = de.getDouble("minTripSpeed");
|
|
170
|
+
if (de.has("minTripDuration")) o.minTripDurationMs = de.getLong("minTripDuration");
|
|
171
|
+
if (de.has("hardBrakeMps2")) o.hardBrakeMps2 = de.getDouble("hardBrakeMps2");
|
|
172
|
+
if (de.has("rapidAccelMps2")) o.rapidAccelMps2 = de.getDouble("rapidAccelMps2");
|
|
173
|
+
if (de.has("sharpTurnDegPerSec")) o.sharpTurnDegPerSec = de.getDouble("sharpTurnDegPerSec");
|
|
174
|
+
if (de.has("crashImpactKmh")) o.crashImpactKmh = de.getDouble("crashImpactKmh");
|
|
175
|
+
if (de.has("crashWindowMs")) o.crashWindowMs = de.getLong("crashWindowMs");
|
|
176
|
+
if (de.has("sensorFusion")) o.sensorFusion = de.getBoolean("sensorFusion");
|
|
177
|
+
if (de.has("crashImpactG")) o.crashImpactG = de.getDouble("crashImpactG");
|
|
178
|
+
if (de.has("sensorCrashCooldownMs")) o.sensorCrashCooldownMs = de.getLong("sensorCrashCooldownMs");
|
|
179
|
+
if (de.has("phoneUsageWindowMs")) o.phoneUsageWindowMs = de.getLong("phoneUsageWindowMs");
|
|
180
|
+
if (de.has("phoneUsageCooldownMs")) o.phoneUsageCooldownMs = de.getLong("phoneUsageCooldownMs");
|
|
181
|
+
c.setDrivingEvents(o);
|
|
182
|
+
}
|
|
183
|
+
return c;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private static HashMap<String, String> jsonToHashMap(JSONObject obj) throws JSONException {
|
|
187
|
+
HashMap<String, String> map = new HashMap<>();
|
|
188
|
+
Iterator<String> keys = obj.keys();
|
|
189
|
+
while (keys.hasNext()) {
|
|
190
|
+
String k = keys.next();
|
|
191
|
+
map.put(k, String.valueOf(obj.get(k)));
|
|
192
|
+
}
|
|
193
|
+
return map;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private static boolean has(JSONObject j, String key) {
|
|
197
|
+
return j.has(key) && !j.isNull(key);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Map {@link Config#NullString} or null to JSONObject.NULL so the sentinel survives. */
|
|
201
|
+
private static Object nullable(String s) {
|
|
202
|
+
if (s == null || s == Config.NullString) return JSONObject.NULL;
|
|
203
|
+
return s;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Inverse of {@link #nullable(String)}. JSON null → {@link Config#NullString}. */
|
|
207
|
+
private static String readNullable(JSONObject j, String key) throws JSONException {
|
|
208
|
+
if (j.isNull(key)) return Config.NullString;
|
|
209
|
+
return j.getString(key);
|
|
210
|
+
}
|
|
211
|
+
}
|