@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
|
@@ -0,0 +1,1193 @@
|
|
|
1
|
+
//
|
|
2
|
+
// MAURBackgroundGeolocationFacade.m
|
|
3
|
+
//
|
|
4
|
+
// Created by Marian Hello on 04/06/16.
|
|
5
|
+
// Version 2.0.0
|
|
6
|
+
//
|
|
7
|
+
// According to apache license
|
|
8
|
+
//
|
|
9
|
+
// This is class is using code from christocracy cordova-plugin-background-geolocation plugin
|
|
10
|
+
// https://github.com/christocracy/cordova-plugin-background-geolocation
|
|
11
|
+
//
|
|
12
|
+
|
|
13
|
+
#import <UIKit/UIKit.h>
|
|
14
|
+
#import <CoreLocation/CoreLocation.h>
|
|
15
|
+
#import <AudioToolbox/AudioToolbox.h>
|
|
16
|
+
#import "MAURBackgroundGeolocationFacade.h"
|
|
17
|
+
#import "MAURPostLocationTask.h"
|
|
18
|
+
#import "MAURSQLiteConfigurationDAO.h"
|
|
19
|
+
#import "MAURSQLiteLocationDAO.h"
|
|
20
|
+
#import "MAURSessionLocationDAO.h"
|
|
21
|
+
#import "MAURBackgroundTaskManager.h"
|
|
22
|
+
#import "MAURLogging.h"
|
|
23
|
+
#import "FMDBLogger.h"
|
|
24
|
+
#import "MAURLogReader.h"
|
|
25
|
+
#import "MAURLocationManager.h"
|
|
26
|
+
#import "MAURActivityLocationProvider.h"
|
|
27
|
+
#import "MAURDistanceFilterLocationProvider.h"
|
|
28
|
+
#import "MAURRawLocationProvider.h"
|
|
29
|
+
#import "MAURUncaughtExceptionLogger.h"
|
|
30
|
+
#import "MAURPostLocationTask.h"
|
|
31
|
+
#import "INTULocationManager.h"
|
|
32
|
+
#import "MAURSensorFusionDetector.h"
|
|
33
|
+
|
|
34
|
+
// error messages
|
|
35
|
+
#define CONFIGURE_ERROR_MSG "Configuration error."
|
|
36
|
+
#define SERVICE_ERROR_MSG "Cannot start service error."
|
|
37
|
+
#define UNKNOWN_LOCATION_PROVIDER_MSG "Unknown location provider."
|
|
38
|
+
|
|
39
|
+
// Position errors
|
|
40
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/PositionError
|
|
41
|
+
#define PERMISSION_DENIED 1
|
|
42
|
+
#define POSITION_UNAVAILABLE 2
|
|
43
|
+
#define TIMEOUT 3
|
|
44
|
+
|
|
45
|
+
static NSString * const BGGeolocationDomain = @"com.marianhello";
|
|
46
|
+
static NSString * const TAG = @"BgGeo";
|
|
47
|
+
|
|
48
|
+
FMDBLogger *sqliteLogger;
|
|
49
|
+
|
|
50
|
+
@interface MAURBackgroundGeolocationFacade () <MAURProviderDelegate, MAURPostLocationTaskDelegate, MAURSensorFusionListener>
|
|
51
|
+
@end
|
|
52
|
+
|
|
53
|
+
// v3.5 Phase 4: notification name for heartbeat events. CDVBackgroundGeolocation observes
|
|
54
|
+
// it to forward into the JS event "heartbeat" with the latest known location.
|
|
55
|
+
NSString * const MAURHeartbeatNotification = @"MAURHeartbeatNotification";
|
|
56
|
+
// v4.0 Phase 6: driver-insight notifications.
|
|
57
|
+
NSString * const MAURTripStartNotification = @"MAURTripStartNotification";
|
|
58
|
+
NSString * const MAURTripEndNotification = @"MAURTripEndNotification";
|
|
59
|
+
NSString * const MAURMovingNotification = @"MAURMovingNotification";
|
|
60
|
+
NSString * const MAURStoppedNotification = @"MAURStoppedNotification";
|
|
61
|
+
NSString * const MAURSpeedingNotification = @"MAURSpeedingNotification";
|
|
62
|
+
NSString * const MAURProviderChangeNotification = @"MAURProviderChangeNotification";
|
|
63
|
+
NSString * const MAURSOSNotification = @"MAURSOSNotification";
|
|
64
|
+
// v4.1
|
|
65
|
+
NSString * const MAURHardBrakeNotification = @"MAURHardBrakeNotification";
|
|
66
|
+
NSString * const MAURRapidAccelerationNotification = @"MAURRapidAccelerationNotification";
|
|
67
|
+
NSString * const MAURSharpTurnNotification = @"MAURSharpTurnNotification";
|
|
68
|
+
NSString * const MAURPossibleCrashNotification = @"MAURPossibleCrashNotification";
|
|
69
|
+
// v4.2
|
|
70
|
+
NSString * const MAURPhoneUsageWhileDrivingNotification = @"MAURPhoneUsageWhileDrivingNotification";
|
|
71
|
+
|
|
72
|
+
@implementation MAURBackgroundGeolocationFacade {
|
|
73
|
+
BOOL isStarted;
|
|
74
|
+
MAUROperationalMode operationMode;
|
|
75
|
+
|
|
76
|
+
UILocalNotification *localNotification;
|
|
77
|
+
|
|
78
|
+
// configurable options
|
|
79
|
+
MAURConfig *_config;
|
|
80
|
+
|
|
81
|
+
MAURLocation *stationaryLocation;
|
|
82
|
+
MAURLocation *lastReceivedLocation; // v3.5 Phase 4: heartbeat payload
|
|
83
|
+
NSTimer *heartbeatTimer; // v3.5 Phase 4
|
|
84
|
+
MAURAbstractLocationProvider<MAURLocationProvider> *locationProvider;
|
|
85
|
+
MAURPostLocationTask *postLocationTask;
|
|
86
|
+
|
|
87
|
+
// v4.0 Phase 6: driver-insights state
|
|
88
|
+
BOOL drIsMoving;
|
|
89
|
+
BOOL drTripActive;
|
|
90
|
+
NSTimeInterval drTripStartedAt;
|
|
91
|
+
double drTripDistanceMeters;
|
|
92
|
+
BOOL drHasPrev;
|
|
93
|
+
double drPrevLat, drPrevLon;
|
|
94
|
+
NSTimeInterval drAboveTripSpeedSince;
|
|
95
|
+
NSTimeInterval drBelowMovingSince;
|
|
96
|
+
BOOL drWasSpeeding;
|
|
97
|
+
NSString *drLastProvider;
|
|
98
|
+
// v4.1 GPS-derived sensor-like state
|
|
99
|
+
double drPrevSpeed;
|
|
100
|
+
NSTimeInterval drPrevSpeedAt;
|
|
101
|
+
double drPrevBearing;
|
|
102
|
+
BOOL drHasPrevBearing;
|
|
103
|
+
NSTimeInterval drPrevBearingAt;
|
|
104
|
+
NSTimeInterval drLastHardBrakeAt, drLastRapidAccelAt, drLastSharpTurnAt, drLastCrashAt;
|
|
105
|
+
// v4.2 sensor fusion
|
|
106
|
+
MAURSensorFusionDetector *sensorFusion;
|
|
107
|
+
// v4.3 — events buffered when no simultaneous fix is available; drained onto next location.
|
|
108
|
+
NSMutableArray *pendingDrivingEvents;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
- (instancetype) init
|
|
113
|
+
{
|
|
114
|
+
self = [super init];
|
|
115
|
+
|
|
116
|
+
if (self == nil) {
|
|
117
|
+
return self;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
[DDLog addLogger:[DDASLLogger sharedInstance] withLevel:DDLogLevelInfo];
|
|
121
|
+
[DDLog addLogger:[DDTTYLogger sharedInstance] withLevel:DDLogLevelDebug];
|
|
122
|
+
|
|
123
|
+
sqliteLogger = [[FMDBLogger alloc] initWithLogDirectory:[self loggerDirectory]];
|
|
124
|
+
sqliteLogger.saveThreshold = 1;
|
|
125
|
+
sqliteLogger.saveInterval = 0;
|
|
126
|
+
sqliteLogger.maxAge = 60 * 60 * 24 * 7; // 7 days
|
|
127
|
+
sqliteLogger.deleteInterval = 60 * 60 * 24; // 1 day
|
|
128
|
+
sqliteLogger.deleteOnEverySave = NO;
|
|
129
|
+
|
|
130
|
+
[DDLog addLogger:sqliteLogger withLevel:DDLogLevelDebug];
|
|
131
|
+
|
|
132
|
+
MAHUncaughtExceptionLogger *logger = mah_get_uncaught_exception_logger();
|
|
133
|
+
logger->setEnabled(YES);
|
|
134
|
+
|
|
135
|
+
postLocationTask = [[MAURPostLocationTask alloc] init];
|
|
136
|
+
postLocationTask.delegate = self;
|
|
137
|
+
|
|
138
|
+
localNotification = [[UILocalNotification alloc] init];
|
|
139
|
+
localNotification.timeZone = [NSTimeZone defaultTimeZone];
|
|
140
|
+
|
|
141
|
+
isStarted = NO;
|
|
142
|
+
pendingDrivingEvents = [[NSMutableArray alloc] init];
|
|
143
|
+
|
|
144
|
+
// v4.5.1 — wire pending events + battery snapshot into the post task so they run AFTER
|
|
145
|
+
// any locationTransform that may produce a new instance / return nil. The previous flow
|
|
146
|
+
// (flush BEFORE add:) lost buffered events whenever the transform returned nil.
|
|
147
|
+
postLocationTask.pendingDrivingEventsBuffer = pendingDrivingEvents;
|
|
148
|
+
__weak typeof(self) weakSelf = self;
|
|
149
|
+
postLocationTask.attachBatterySnapshot = ^(MAURLocation * _Nonnull loc) {
|
|
150
|
+
__strong typeof(self) strongSelf = weakSelf;
|
|
151
|
+
if (strongSelf == nil) return;
|
|
152
|
+
BOOL includeBat = (strongSelf->_config == nil
|
|
153
|
+
|| strongSelf->_config.includeBattery == nil
|
|
154
|
+
|| [strongSelf->_config.includeBattery boolValue]);
|
|
155
|
+
if (includeBat) [strongSelf attachBatterySnapshotTo:loc];
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return self;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* configure manager
|
|
163
|
+
* @param {Config} configuration
|
|
164
|
+
* @param {NSError} optional error
|
|
165
|
+
*/
|
|
166
|
+
- (BOOL) configure:(MAURConfig*)config error:(NSError * __autoreleasing *)outError
|
|
167
|
+
{
|
|
168
|
+
__block NSError *error = nil;
|
|
169
|
+
|
|
170
|
+
MAURConfig *currentConfig = [self getConfig];
|
|
171
|
+
_config = [MAURConfig merge:currentConfig withConfig:config];
|
|
172
|
+
|
|
173
|
+
DDLogInfo(@"%@ #configure: %@", TAG, _config);
|
|
174
|
+
|
|
175
|
+
postLocationTask.config = _config;
|
|
176
|
+
|
|
177
|
+
MAURSQLiteConfigurationDAO* configDAO = [MAURSQLiteConfigurationDAO sharedInstance];
|
|
178
|
+
[configDAO persistConfiguration:_config];
|
|
179
|
+
|
|
180
|
+
// ios 8 requires permissions to send local-notifications
|
|
181
|
+
if ([_config isDebugging]) {
|
|
182
|
+
[self runOnMainThread:^{
|
|
183
|
+
UIApplication *app = [UIApplication sharedApplication];
|
|
184
|
+
if ([[UIApplication sharedApplication]respondsToSelector:@selector(currentUserNotificationSettings)]) {
|
|
185
|
+
UIUserNotificationType wantedTypes = UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert;
|
|
186
|
+
UIUserNotificationSettings *currentSettings = [app currentUserNotificationSettings];
|
|
187
|
+
if (!currentSettings || (currentSettings.types != wantedTypes)) {
|
|
188
|
+
if ([app respondsToSelector:@selector(registerUserNotificationSettings:)]) {
|
|
189
|
+
[app registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:wantedTypes categories:nil]];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (isStarted) {
|
|
197
|
+
// Note: CLLocationManager must be created on a thread with an active run loop (main thread)
|
|
198
|
+
[self runOnMainThread:^{
|
|
199
|
+
|
|
200
|
+
// requesting new provider
|
|
201
|
+
if (![currentConfig.locationProvider isEqual:_config.locationProvider]) {
|
|
202
|
+
[locationProvider onDestroy]; // destroy current provider
|
|
203
|
+
locationProvider = [self getProvider:_config.locationProvider.intValue error:&error];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (locationProvider == nil) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// trap configuration errors
|
|
211
|
+
if (![locationProvider onConfigure:_config error:&error]) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
isStarted = [locationProvider onStart:&error];
|
|
216
|
+
locationProvider.delegate = self;
|
|
217
|
+
}];
|
|
218
|
+
|
|
219
|
+
// v4.1: hot-reload heartbeat scheduler if heartbeatInterval changed.
|
|
220
|
+
NSInteger prevHb = currentConfig.heartbeatInterval != nil ? [currentConfig.heartbeatInterval integerValue] : 0;
|
|
221
|
+
NSInteger newHb = _config.heartbeatInterval != nil ? [_config.heartbeatInterval integerValue] : 0;
|
|
222
|
+
if (prevHb != newHb) {
|
|
223
|
+
[self scheduleHeartbeat]; // cancels and reschedules; is a no-op if 0.
|
|
224
|
+
}
|
|
225
|
+
// Driver-insights detector reads `_config.drivingEvents` on every feed; no rebuild needed
|
|
226
|
+
// unless the dictionary identity changed in a way that toggles `enabled`. Always reset
|
|
227
|
+
// accumulators to apply the new thresholds cleanly from this point on.
|
|
228
|
+
if (![[currentConfig.drivingEvents description] isEqualToString:[_config.drivingEvents description]]) {
|
|
229
|
+
[self drivingDetectorReset];
|
|
230
|
+
// v4.2: re-evaluate sensor fusion as well (might have just been enabled/disabled).
|
|
231
|
+
[self configureSensorFusion];
|
|
232
|
+
if (isStarted) [sensorFusion start];
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (error != nil) {
|
|
237
|
+
if (outError != nil) {
|
|
238
|
+
NSDictionary *userInfo = @{
|
|
239
|
+
NSLocalizedDescriptionKey: NSLocalizedString(@CONFIGURE_ERROR_MSG, nil),
|
|
240
|
+
NSUnderlyingErrorKey : error
|
|
241
|
+
};
|
|
242
|
+
*outError = [NSError errorWithDomain:BGGeolocationDomain code:MAURBGConfigureError userInfo:userInfo];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return NO;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return YES;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Turn on background geolocation
|
|
253
|
+
* in case of failure it calls error callback from configure method
|
|
254
|
+
* may fire two callback when location services are disabled and when authorization failed
|
|
255
|
+
*/
|
|
256
|
+
- (BOOL) start:(NSError * __autoreleasing *)outError
|
|
257
|
+
{
|
|
258
|
+
DDLogInfo(@"%@ #start: %d", TAG, isStarted);
|
|
259
|
+
|
|
260
|
+
if (isStarted) {
|
|
261
|
+
return NO;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
__block NSError *error = nil;
|
|
265
|
+
MAURConfig *config = [self getConfig];
|
|
266
|
+
|
|
267
|
+
postLocationTask.config = config;
|
|
268
|
+
[postLocationTask start];
|
|
269
|
+
|
|
270
|
+
// Note: CLLocationManager must be created on a thread with an active run loop (main thread)
|
|
271
|
+
[self runOnMainThread:^{
|
|
272
|
+
locationProvider = [self getProvider:config.locationProvider.intValue error:&error];
|
|
273
|
+
|
|
274
|
+
if (locationProvider == nil) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// trap configuration errors
|
|
279
|
+
if (![locationProvider onConfigure:config error:&error]) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
isStarted = [locationProvider onStart:&error];
|
|
284
|
+
locationProvider.delegate = self;
|
|
285
|
+
}];
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
if (!isStarted) {
|
|
289
|
+
if (outError != nil) {
|
|
290
|
+
*outError = error;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return NO;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// v3.5 Phase 4: schedule heartbeat once provider is up.
|
|
297
|
+
[self scheduleHeartbeat];
|
|
298
|
+
// v4.2 Phase 8: configure & start sensor fusion if requested.
|
|
299
|
+
[self configureSensorFusion];
|
|
300
|
+
[sensorFusion start];
|
|
301
|
+
|
|
302
|
+
return isStarted;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Turn off background geolocation
|
|
307
|
+
*/
|
|
308
|
+
- (BOOL) stop:(NSError * __autoreleasing *)outError
|
|
309
|
+
{
|
|
310
|
+
DDLogInfo(@"%@ #stop", TAG);
|
|
311
|
+
|
|
312
|
+
if (!isStarted) {
|
|
313
|
+
return YES;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// v3.5 Phase 4: cancel heartbeat scheduler.
|
|
317
|
+
[self cancelHeartbeat];
|
|
318
|
+
// v4.0 Phase 6: reset driver-insights state machine.
|
|
319
|
+
[self drivingDetectorReset];
|
|
320
|
+
// v4.2 Phase 8: stop sensor fusion sampling.
|
|
321
|
+
sensorFusion.tripActive = NO;
|
|
322
|
+
[sensorFusion stop];
|
|
323
|
+
|
|
324
|
+
[postLocationTask stop];
|
|
325
|
+
|
|
326
|
+
[self runOnMainThread:^{
|
|
327
|
+
isStarted = ![locationProvider onStop:outError];
|
|
328
|
+
}];
|
|
329
|
+
|
|
330
|
+
return isStarted;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// v3.5 Phase 4: heartbeat scheduler.
|
|
334
|
+
- (void) scheduleHeartbeat
|
|
335
|
+
{
|
|
336
|
+
[self cancelHeartbeat];
|
|
337
|
+
if (_config == nil || _config.heartbeatInterval == nil) return;
|
|
338
|
+
NSInteger ms = [_config.heartbeatInterval integerValue];
|
|
339
|
+
if (ms <= 0) return;
|
|
340
|
+
NSTimeInterval seconds = ms / 1000.0;
|
|
341
|
+
DDLogDebug(@"%@ scheduling heartbeat every %.2fs", TAG, seconds);
|
|
342
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
343
|
+
heartbeatTimer = [NSTimer scheduledTimerWithTimeInterval:seconds
|
|
344
|
+
target:self
|
|
345
|
+
selector:@selector(onHeartbeatTick:)
|
|
346
|
+
userInfo:nil
|
|
347
|
+
repeats:YES];
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
- (void) cancelHeartbeat
|
|
352
|
+
{
|
|
353
|
+
if (heartbeatTimer != nil) {
|
|
354
|
+
[heartbeatTimer invalidate];
|
|
355
|
+
heartbeatTimer = nil;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
- (void) onHeartbeatTick:(NSTimer *)timer
|
|
360
|
+
{
|
|
361
|
+
NSDictionary *userInfo = lastReceivedLocation != nil
|
|
362
|
+
? @{ @"location": lastReceivedLocation }
|
|
363
|
+
: @{};
|
|
364
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURHeartbeatNotification
|
|
365
|
+
object:self
|
|
366
|
+
userInfo:userInfo];
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
#pragma mark - v4.0 Phase 6 driver-insights state machine
|
|
370
|
+
|
|
371
|
+
- (void) drivingDetectorReset
|
|
372
|
+
{
|
|
373
|
+
drIsMoving = NO;
|
|
374
|
+
drTripActive = NO;
|
|
375
|
+
drTripStartedAt = 0;
|
|
376
|
+
drTripDistanceMeters = 0;
|
|
377
|
+
drHasPrev = NO;
|
|
378
|
+
drAboveTripSpeedSince = 0;
|
|
379
|
+
drBelowMovingSince = 0;
|
|
380
|
+
drWasSpeeding = NO;
|
|
381
|
+
drLastProvider = nil;
|
|
382
|
+
drPrevSpeed = 0;
|
|
383
|
+
drPrevSpeedAt = 0;
|
|
384
|
+
drPrevBearing = 0;
|
|
385
|
+
drHasPrevBearing = NO;
|
|
386
|
+
drPrevBearingAt = 0;
|
|
387
|
+
drLastHardBrakeAt = drLastRapidAccelAt = drLastSharpTurnAt = drLastCrashAt = 0;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
#pragma mark - v4.2 Phase 8 sensor fusion
|
|
391
|
+
|
|
392
|
+
- (void) configureSensorFusion
|
|
393
|
+
{
|
|
394
|
+
NSDictionary *de = _config.drivingEvents;
|
|
395
|
+
BOOL want = [de isKindOfClass:[NSDictionary class]]
|
|
396
|
+
&& [de[@"enabled"] boolValue]
|
|
397
|
+
&& [de[@"sensorFusion"] boolValue];
|
|
398
|
+
if (!want) {
|
|
399
|
+
[sensorFusion stop];
|
|
400
|
+
sensorFusion = nil;
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
if (sensorFusion == nil) {
|
|
404
|
+
sensorFusion = [[MAURSensorFusionDetector alloc] init];
|
|
405
|
+
sensorFusion.listener = self;
|
|
406
|
+
}
|
|
407
|
+
sensorFusion.enabled = YES;
|
|
408
|
+
if (de[@"crashImpactG"]) sensorFusion.crashImpactG = [de[@"crashImpactG"] doubleValue];
|
|
409
|
+
if (de[@"sensorCrashCooldownMs"]) sensorFusion.crashCooldownMs = [de[@"sensorCrashCooldownMs"] doubleValue];
|
|
410
|
+
if (de[@"phoneUsageWindowMs"]) sensorFusion.phoneUsageWindowMs = [de[@"phoneUsageWindowMs"] doubleValue];
|
|
411
|
+
if (de[@"phoneUsageCooldownMs"]) sensorFusion.phoneUsageCooldownMs = [de[@"phoneUsageCooldownMs"] doubleValue];
|
|
412
|
+
// v4.2 hot-reload: re-inject current trip state + last location so a config change
|
|
413
|
+
// mid-trip starts the sensor pipeline in the correct mode.
|
|
414
|
+
sensorFusion.tripActive = drTripActive;
|
|
415
|
+
sensorFusion.lastLocation = lastReceivedLocation;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// MAURSensorFusionListener
|
|
419
|
+
- (void) onSensorCrashWithImpactG:(double)impactG location:(MAURLocation *)location
|
|
420
|
+
{
|
|
421
|
+
[self bufferPendingEvent:@"possibleCrash" extra:@{@"value": @(impactG), @"source": @"sensor"}];
|
|
422
|
+
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
|
423
|
+
if (location != nil) userInfo[@"location"] = location;
|
|
424
|
+
userInfo[@"value"] = @(impactG);
|
|
425
|
+
userInfo[@"source"] = @"sensor";
|
|
426
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURPossibleCrashNotification
|
|
427
|
+
object:self
|
|
428
|
+
userInfo:userInfo];
|
|
429
|
+
}
|
|
430
|
+
- (void) onPhoneUsageWhileDriving:(MAURLocation *)location
|
|
431
|
+
{
|
|
432
|
+
[self bufferPendingEvent:@"phoneUsageWhileDriving" extra:nil];
|
|
433
|
+
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
|
434
|
+
if (location != nil) userInfo[@"location"] = location;
|
|
435
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURPhoneUsageWhileDrivingNotification
|
|
436
|
+
object:self
|
|
437
|
+
userInfo:userInfo];
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// v4.3 — driving event helpers
|
|
441
|
+
- (void) attachDrivingEvent:(NSString *)type to:(MAURLocation *)loc extra:(NSDictionary *)extra
|
|
442
|
+
{
|
|
443
|
+
if (loc == nil || type == nil) return;
|
|
444
|
+
NSMutableDictionary *ev = [NSMutableDictionary dictionary];
|
|
445
|
+
ev[@"type"] = type;
|
|
446
|
+
ev[@"time"] = @((long long)([[NSDate date] timeIntervalSince1970] * 1000.0));
|
|
447
|
+
if (extra != nil) [ev addEntriesFromDictionary:extra];
|
|
448
|
+
if (loc.drivingEvents == nil) loc.drivingEvents = [NSMutableArray array];
|
|
449
|
+
[loc.drivingEvents addObject:ev];
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// v4.4.1 — pending driving events: cap + TTL.
|
|
453
|
+
static NSInteger const kPendingDrivingEventsMax = 20;
|
|
454
|
+
static NSTimeInterval const kPendingDrivingEventsTTLMs = 60000.0;
|
|
455
|
+
|
|
456
|
+
- (void) bufferPendingEvent:(NSString *)type extra:(NSDictionary *)extra
|
|
457
|
+
{
|
|
458
|
+
if (type == nil) return;
|
|
459
|
+
NSMutableDictionary *ev = [NSMutableDictionary dictionary];
|
|
460
|
+
ev[@"type"] = type;
|
|
461
|
+
ev[@"time"] = @((long long)([[NSDate date] timeIntervalSince1970] * 1000.0));
|
|
462
|
+
if (extra != nil) [ev addEntriesFromDictionary:extra];
|
|
463
|
+
@synchronized (pendingDrivingEvents) {
|
|
464
|
+
while ((NSInteger)[pendingDrivingEvents count] >= kPendingDrivingEventsMax) {
|
|
465
|
+
[pendingDrivingEvents removeObjectAtIndex:0];
|
|
466
|
+
}
|
|
467
|
+
[pendingDrivingEvents addObject:ev];
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
- (void) flushPendingDrivingEventsTo:(MAURLocation *)loc
|
|
472
|
+
{
|
|
473
|
+
if (loc == nil) return;
|
|
474
|
+
NSTimeInterval nowMs = [[NSDate date] timeIntervalSince1970] * 1000.0;
|
|
475
|
+
@synchronized (pendingDrivingEvents) {
|
|
476
|
+
if ([pendingDrivingEvents count] == 0) return;
|
|
477
|
+
if (loc.drivingEvents == nil) loc.drivingEvents = [NSMutableArray array];
|
|
478
|
+
for (NSDictionary *ev in pendingDrivingEvents) {
|
|
479
|
+
NSNumber *t = ev[@"time"];
|
|
480
|
+
NSTimeInterval evMs = t != nil ? [t doubleValue] : nowMs;
|
|
481
|
+
if (nowMs - evMs <= kPendingDrivingEventsTTLMs) {
|
|
482
|
+
[loc.drivingEvents addObject:ev];
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
[pendingDrivingEvents removeAllObjects];
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// v4.4 — read device battery via UIDevice. Calling main thread; safe from any thread.
|
|
490
|
+
- (void) attachBatterySnapshotTo:(MAURLocation *)loc
|
|
491
|
+
{
|
|
492
|
+
if (loc == nil) return;
|
|
493
|
+
void (^read)(void) = ^{
|
|
494
|
+
UIDevice *device = [UIDevice currentDevice];
|
|
495
|
+
if (!device.batteryMonitoringEnabled) device.batteryMonitoringEnabled = YES;
|
|
496
|
+
float lvl = device.batteryLevel; // 0.0 - 1.0, or -1 if unknown
|
|
497
|
+
if (lvl >= 0) {
|
|
498
|
+
loc.batteryLevel = @((int) round(lvl * 100.0));
|
|
499
|
+
}
|
|
500
|
+
UIDeviceBatteryState state = device.batteryState;
|
|
501
|
+
BOOL charging = (state == UIDeviceBatteryStateCharging || state == UIDeviceBatteryStateFull);
|
|
502
|
+
loc.isCharging = @(charging);
|
|
503
|
+
};
|
|
504
|
+
if ([NSThread isMainThread]) {
|
|
505
|
+
read();
|
|
506
|
+
} else {
|
|
507
|
+
dispatch_sync(dispatch_get_main_queue(), read);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
- (void) drivingDetectorFeed:(MAURLocation *)loc
|
|
512
|
+
{
|
|
513
|
+
if (loc == nil || _config == nil) return;
|
|
514
|
+
BOOL enabled = NO;
|
|
515
|
+
double speedLimit = 0;
|
|
516
|
+
double minMovingSpeed = 1.0;
|
|
517
|
+
NSTimeInterval stoppedDuration = 60.0;
|
|
518
|
+
double minTripSpeed = 3.0;
|
|
519
|
+
NSTimeInterval minTripDuration = 30.0;
|
|
520
|
+
NSDictionary *de = [_config valueForKey:@"drivingEvents"]; // see MAURConfig: provided as NSDictionary
|
|
521
|
+
if ([de isKindOfClass:[NSDictionary class]]) {
|
|
522
|
+
enabled = [[de objectForKey:@"enabled"] boolValue];
|
|
523
|
+
speedLimit = [[de objectForKey:@"speedLimit"] doubleValue];
|
|
524
|
+
if ([de objectForKey:@"minMovingSpeed"]) minMovingSpeed = [[de objectForKey:@"minMovingSpeed"] doubleValue];
|
|
525
|
+
if ([de objectForKey:@"stoppedDuration"]) stoppedDuration = [[de objectForKey:@"stoppedDuration"] doubleValue] / 1000.0;
|
|
526
|
+
if ([de objectForKey:@"minTripSpeed"]) minTripSpeed = [[de objectForKey:@"minTripSpeed"] doubleValue];
|
|
527
|
+
if ([de objectForKey:@"minTripDuration"]) minTripDuration = [[de objectForKey:@"minTripDuration"] doubleValue] / 1000.0;
|
|
528
|
+
}
|
|
529
|
+
if (!enabled) return;
|
|
530
|
+
|
|
531
|
+
NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
|
|
532
|
+
double speed = loc.speed != nil ? [loc.speed doubleValue] : 0.0;
|
|
533
|
+
if (speed < 0) speed = 0;
|
|
534
|
+
|
|
535
|
+
// Provider change
|
|
536
|
+
NSString *provider = loc.provider;
|
|
537
|
+
if (provider != nil && ![provider isEqualToString:drLastProvider]) {
|
|
538
|
+
drLastProvider = provider;
|
|
539
|
+
[self attachDrivingEvent:@"providerChange" to:loc extra:@{@"provider": provider}];
|
|
540
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURProviderChangeNotification
|
|
541
|
+
object:self
|
|
542
|
+
userInfo:@{@"provider": provider}];
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
double curLat = [loc.latitude doubleValue];
|
|
546
|
+
double curLon = [loc.longitude doubleValue];
|
|
547
|
+
if (drHasPrev && drTripActive) {
|
|
548
|
+
drTripDistanceMeters += [self drHaversineFromLat:drPrevLat lon:drPrevLon toLat:curLat lon:curLon];
|
|
549
|
+
}
|
|
550
|
+
drPrevLat = curLat;
|
|
551
|
+
drPrevLon = curLon;
|
|
552
|
+
drHasPrev = YES;
|
|
553
|
+
|
|
554
|
+
BOOL nowMoving = speed >= minMovingSpeed;
|
|
555
|
+
if (nowMoving) {
|
|
556
|
+
drBelowMovingSince = 0;
|
|
557
|
+
if (!drIsMoving) {
|
|
558
|
+
drIsMoving = YES;
|
|
559
|
+
[self attachDrivingEvent:@"moving" to:loc extra:nil];
|
|
560
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURMovingNotification
|
|
561
|
+
object:self
|
|
562
|
+
userInfo:@{@"location": loc}];
|
|
563
|
+
}
|
|
564
|
+
if (!drTripActive) {
|
|
565
|
+
if (speed >= minTripSpeed) {
|
|
566
|
+
if (drAboveTripSpeedSince == 0) drAboveTripSpeedSince = now;
|
|
567
|
+
if (now - drAboveTripSpeedSince >= minTripDuration) {
|
|
568
|
+
drTripActive = YES;
|
|
569
|
+
drTripStartedAt = now;
|
|
570
|
+
drTripDistanceMeters = 0;
|
|
571
|
+
[self attachDrivingEvent:@"tripStart" to:loc extra:nil];
|
|
572
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURTripStartNotification
|
|
573
|
+
object:self
|
|
574
|
+
userInfo:@{@"location": loc}];
|
|
575
|
+
sensorFusion.tripActive = YES;
|
|
576
|
+
}
|
|
577
|
+
} else {
|
|
578
|
+
drAboveTripSpeedSince = 0;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
} else {
|
|
582
|
+
drAboveTripSpeedSince = 0;
|
|
583
|
+
if (drBelowMovingSince == 0) drBelowMovingSince = now;
|
|
584
|
+
if (drIsMoving && (now - drBelowMovingSince) >= stoppedDuration) {
|
|
585
|
+
drIsMoving = NO;
|
|
586
|
+
[self attachDrivingEvent:@"stopped" to:loc extra:nil];
|
|
587
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURStoppedNotification
|
|
588
|
+
object:self
|
|
589
|
+
userInfo:@{@"location": loc}];
|
|
590
|
+
if (drTripActive) {
|
|
591
|
+
NSTimeInterval durMs = (now - drTripStartedAt) * 1000.0;
|
|
592
|
+
double dist = drTripDistanceMeters;
|
|
593
|
+
drTripActive = NO;
|
|
594
|
+
[self attachDrivingEvent:@"tripEnd" to:loc extra:@{@"distance": @(dist), @"durationMs": @((long long)durMs)}];
|
|
595
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURTripEndNotification
|
|
596
|
+
object:self
|
|
597
|
+
userInfo:@{
|
|
598
|
+
@"location": loc,
|
|
599
|
+
@"distance": @(dist),
|
|
600
|
+
@"durationMs": @((long long)durMs)
|
|
601
|
+
}];
|
|
602
|
+
sensorFusion.tripActive = NO;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (speedLimit > 0) {
|
|
608
|
+
double kmh = speed * 3.6;
|
|
609
|
+
if (kmh > speedLimit) {
|
|
610
|
+
if (!drWasSpeeding) {
|
|
611
|
+
drWasSpeeding = YES;
|
|
612
|
+
[self attachDrivingEvent:@"speeding" to:loc extra:@{@"speedKmh": @(kmh), @"limitKmh": @(speedLimit)}];
|
|
613
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURSpeedingNotification
|
|
614
|
+
object:self
|
|
615
|
+
userInfo:@{
|
|
616
|
+
@"location": loc,
|
|
617
|
+
@"speedKmh": @(kmh),
|
|
618
|
+
@"limitKmh": @(speedLimit)
|
|
619
|
+
}];
|
|
620
|
+
}
|
|
621
|
+
} else {
|
|
622
|
+
drWasSpeeding = NO;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// v4.1 GPS-derived sensor-like events
|
|
627
|
+
double hardBrakeMps2 = 3.5, rapidAccelMps2 = 3.5, sharpTurnDegPerSec = 30, crashImpactKmh = 25;
|
|
628
|
+
NSTimeInterval crashWindow = 2.0;
|
|
629
|
+
if ([de isKindOfClass:[NSDictionary class]]) {
|
|
630
|
+
if ([de objectForKey:@"hardBrakeMps2"]) hardBrakeMps2 = [[de objectForKey:@"hardBrakeMps2"] doubleValue];
|
|
631
|
+
if ([de objectForKey:@"rapidAccelMps2"]) rapidAccelMps2 = [[de objectForKey:@"rapidAccelMps2"] doubleValue];
|
|
632
|
+
if ([de objectForKey:@"sharpTurnDegPerSec"]) sharpTurnDegPerSec = [[de objectForKey:@"sharpTurnDegPerSec"] doubleValue];
|
|
633
|
+
if ([de objectForKey:@"crashImpactKmh"]) crashImpactKmh = [[de objectForKey:@"crashImpactKmh"] doubleValue];
|
|
634
|
+
if ([de objectForKey:@"crashWindowMs"]) crashWindow = [[de objectForKey:@"crashWindowMs"] doubleValue] / 1000.0;
|
|
635
|
+
}
|
|
636
|
+
static const NSTimeInterval kCooldown = 4.0;
|
|
637
|
+
|
|
638
|
+
if (drTripActive && drPrevSpeedAt > 0) {
|
|
639
|
+
NSTimeInterval dt = now - drPrevSpeedAt;
|
|
640
|
+
if (dt > 0 && dt <= 5.0) {
|
|
641
|
+
double dv = speed - drPrevSpeed;
|
|
642
|
+
double accel = dv / dt;
|
|
643
|
+
if (hardBrakeMps2 > 0 && accel <= -hardBrakeMps2 && (now - drLastHardBrakeAt) >= kCooldown) {
|
|
644
|
+
drLastHardBrakeAt = now;
|
|
645
|
+
[self attachDrivingEvent:@"hardBrake" to:loc extra:@{@"value": @(accel)}];
|
|
646
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURHardBrakeNotification
|
|
647
|
+
object:self
|
|
648
|
+
userInfo:@{@"location": loc, @"value": @(accel)}];
|
|
649
|
+
}
|
|
650
|
+
if (rapidAccelMps2 > 0 && accel >= rapidAccelMps2 && (now - drLastRapidAccelAt) >= kCooldown) {
|
|
651
|
+
drLastRapidAccelAt = now;
|
|
652
|
+
[self attachDrivingEvent:@"rapidAcceleration" to:loc extra:@{@"value": @(accel)}];
|
|
653
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURRapidAccelerationNotification
|
|
654
|
+
object:self
|
|
655
|
+
userInfo:@{@"location": loc, @"value": @(accel)}];
|
|
656
|
+
}
|
|
657
|
+
if (crashImpactKmh > 0 && dt <= crashWindow) {
|
|
658
|
+
double dropKmh = (drPrevSpeed - speed) * 3.6;
|
|
659
|
+
if (dropKmh >= crashImpactKmh
|
|
660
|
+
&& speed < 1.5
|
|
661
|
+
&& drPrevSpeed * 3.6 >= crashImpactKmh
|
|
662
|
+
&& (now - drLastCrashAt) >= kCooldown) {
|
|
663
|
+
drLastCrashAt = now;
|
|
664
|
+
[self attachDrivingEvent:@"possibleCrash" to:loc extra:@{@"value": @(dropKmh), @"source": @"gps"}];
|
|
665
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURPossibleCrashNotification
|
|
666
|
+
object:self
|
|
667
|
+
userInfo:@{@"location": loc, @"value": @(dropKmh), @"source": @"gps"}];
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// Sharp turn (bearing rate)
|
|
674
|
+
if (sharpTurnDegPerSec > 0 && loc.heading != nil && speed >= 5.0 && drHasPrevBearing) {
|
|
675
|
+
NSTimeInterval dt = now - drPrevBearingAt;
|
|
676
|
+
if (dt > 0 && dt <= 5.0) {
|
|
677
|
+
double bearing = [loc.heading doubleValue];
|
|
678
|
+
double diff = fabs(bearing - drPrevBearing);
|
|
679
|
+
if (diff > 180) diff = 360 - diff;
|
|
680
|
+
double rate = diff / dt;
|
|
681
|
+
if (rate >= sharpTurnDegPerSec && (now - drLastSharpTurnAt) >= kCooldown) {
|
|
682
|
+
drLastSharpTurnAt = now;
|
|
683
|
+
[self attachDrivingEvent:@"sharpTurn" to:loc extra:@{@"value": @(rate)}];
|
|
684
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURSharpTurnNotification
|
|
685
|
+
object:self
|
|
686
|
+
userInfo:@{@"location": loc, @"value": @(rate)}];
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
drPrevBearing = [loc.heading doubleValue];
|
|
690
|
+
drPrevBearingAt = now;
|
|
691
|
+
} else if (loc.heading != nil) {
|
|
692
|
+
drPrevBearing = [loc.heading doubleValue];
|
|
693
|
+
drPrevBearingAt = now;
|
|
694
|
+
drHasPrevBearing = YES;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
drPrevSpeed = speed;
|
|
698
|
+
drPrevSpeedAt = now;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
- (double) drHaversineFromLat:(double)lat1 lon:(double)lon1 toLat:(double)lat2 lon:(double)lon2
|
|
702
|
+
{
|
|
703
|
+
const double R = 6371000.0;
|
|
704
|
+
double dLat = (lat2 - lat1) * M_PI / 180.0;
|
|
705
|
+
double dLon = (lon2 - lon1) * M_PI / 180.0;
|
|
706
|
+
double a = sin(dLat/2) * sin(dLat/2)
|
|
707
|
+
+ cos(lat1 * M_PI / 180.0) * cos(lat2 * M_PI / 180.0)
|
|
708
|
+
* sin(dLon/2) * sin(dLon/2);
|
|
709
|
+
return 2 * R * asin(sqrt(a));
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
- (void) triggerSOS:(NSDictionary *)payload
|
|
713
|
+
{
|
|
714
|
+
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
|
715
|
+
if (lastReceivedLocation != nil) userInfo[@"location"] = lastReceivedLocation;
|
|
716
|
+
userInfo[@"payload"] = payload != nil ? payload : @{};
|
|
717
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURSOSNotification
|
|
718
|
+
object:self
|
|
719
|
+
userInfo:userInfo];
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* toggle between foreground and background operation mode
|
|
724
|
+
*/
|
|
725
|
+
- (void) switchMode:(MAUROperationalMode)mode
|
|
726
|
+
{
|
|
727
|
+
DDLogInfo(@"%@ #switchMode %lu", TAG, (unsigned long)mode);
|
|
728
|
+
|
|
729
|
+
operationMode = mode;
|
|
730
|
+
|
|
731
|
+
if (!isStarted) return;
|
|
732
|
+
|
|
733
|
+
if ([self getConfig].isDebugging) {
|
|
734
|
+
AudioServicesPlaySystemSound (operationMode == MAURForegroundMode ? paceChangeYesSound : paceChangeNoSound);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
[self runOnMainThread:^{
|
|
738
|
+
[locationProvider onSwitchMode:mode];
|
|
739
|
+
}];
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
- (BOOL) locationServicesEnabled
|
|
743
|
+
{
|
|
744
|
+
if ([CLLocationManager respondsToSelector:@selector(locationServicesEnabled)]) { // iOS 4.x
|
|
745
|
+
return [CLLocationManager locationServicesEnabled];
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
return NO;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
- (MAURLocationAuthorizationStatus) authorizationStatus
|
|
752
|
+
{
|
|
753
|
+
CLAuthorizationStatus authStatus = [CLLocationManager authorizationStatus];
|
|
754
|
+
switch (authStatus) {
|
|
755
|
+
case kCLAuthorizationStatusNotDetermined:
|
|
756
|
+
return MAURLocationAuthorizationNotDetermined;
|
|
757
|
+
case kCLAuthorizationStatusRestricted:
|
|
758
|
+
case kCLAuthorizationStatusDenied:
|
|
759
|
+
return MAURLocationAuthorizationDenied;
|
|
760
|
+
case kCLAuthorizationStatusAuthorizedAlways:
|
|
761
|
+
return MAURLocationAuthorizationAlways;
|
|
762
|
+
case kCLAuthorizationStatusAuthorizedWhenInUse:
|
|
763
|
+
return MAURLocationAuthorizationForeground;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
- (BOOL) isStarted
|
|
768
|
+
{
|
|
769
|
+
return isStarted;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
- (MAURAbstractLocationProvider<MAURLocationProvider>*) getProvider:(int)providerId error:(NSError * __autoreleasing *)outError
|
|
773
|
+
{
|
|
774
|
+
NSDictionary *errorDictionary;
|
|
775
|
+
MAURAbstractLocationProvider<MAURLocationProvider> *locationProvider = nil;
|
|
776
|
+
switch (providerId) {
|
|
777
|
+
case DISTANCE_FILTER_PROVIDER:
|
|
778
|
+
locationProvider = [[MAURDistanceFilterLocationProvider alloc] init];
|
|
779
|
+
break;
|
|
780
|
+
case ACTIVITY_PROVIDER:
|
|
781
|
+
locationProvider = [[MAURActivityLocationProvider alloc] init];
|
|
782
|
+
break;
|
|
783
|
+
case RAW_PROVIDER:
|
|
784
|
+
locationProvider = [[MAURRawLocationProvider alloc] init];
|
|
785
|
+
break;
|
|
786
|
+
default:
|
|
787
|
+
if (outError != nil) {
|
|
788
|
+
errorDictionary = @{
|
|
789
|
+
NSLocalizedDescriptionKey: NSLocalizedString(@UNKNOWN_LOCATION_PROVIDER_MSG, nil),
|
|
790
|
+
};
|
|
791
|
+
*outError = [NSError errorWithDomain:BGGeolocationDomain code:MAURBGConfigureError userInfo:errorDictionary];
|
|
792
|
+
}
|
|
793
|
+
return nil;
|
|
794
|
+
}
|
|
795
|
+
[locationProvider onCreate];
|
|
796
|
+
return locationProvider;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
- (void) showAppSettings
|
|
800
|
+
{
|
|
801
|
+
[self runOnMainThread:^{
|
|
802
|
+
BOOL canGoToSettings = (UIApplicationOpenSettingsURLString != NULL);
|
|
803
|
+
if (canGoToSettings) {
|
|
804
|
+
NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
|
|
805
|
+
[[UIApplication sharedApplication] openURL:settingsURL
|
|
806
|
+
options:@{}
|
|
807
|
+
completionHandler:nil];
|
|
808
|
+
}
|
|
809
|
+
}];
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
- (void) showLocationSettings
|
|
813
|
+
{
|
|
814
|
+
// NOOP - Since Apple started rejecting apps using non public url schemes
|
|
815
|
+
// https://github.com/mauron85/cordova-plugin-background-geolocation/issues/394
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
- (MAURLocation*) getStationaryLocation
|
|
819
|
+
{
|
|
820
|
+
return stationaryLocation;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
- (NSArray<MAURLocation*>*) getLocations
|
|
824
|
+
{
|
|
825
|
+
MAURSQLiteLocationDAO* locationDAO = [MAURSQLiteLocationDAO sharedInstance];
|
|
826
|
+
return [locationDAO getAllLocations];
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
- (NSArray<MAURLocation*>*) getValidLocations
|
|
830
|
+
{
|
|
831
|
+
MAURSQLiteLocationDAO* locationDAO = [MAURSQLiteLocationDAO sharedInstance];
|
|
832
|
+
return [locationDAO getValidLocations];
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
- (NSArray<MAURLocation*>*) getValidLocationsAndDelete
|
|
836
|
+
{
|
|
837
|
+
MAURSQLiteLocationDAO* locationDAO = [MAURSQLiteLocationDAO sharedInstance];
|
|
838
|
+
return [locationDAO getLocationsForSync];
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
- (void) startSession
|
|
842
|
+
{
|
|
843
|
+
[[MAURSessionLocationDAO sharedInstance] startSession];
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
- (NSArray<MAURLocation*>*) getSessionLocations
|
|
847
|
+
{
|
|
848
|
+
return [[MAURSessionLocationDAO sharedInstance] getSessionLocations];
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
- (void) clearSession
|
|
852
|
+
{
|
|
853
|
+
[[MAURSessionLocationDAO sharedInstance] clearSession];
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
- (NSInteger) getSessionLocationsCount
|
|
857
|
+
{
|
|
858
|
+
return [[MAURSessionLocationDAO sharedInstance] getSessionLocationsCount];
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
- (BOOL) deleteLocation:(NSNumber*)locationId error:(NSError * __autoreleasing *)outError
|
|
862
|
+
{
|
|
863
|
+
MAURSQLiteLocationDAO* locationDAO = [MAURSQLiteLocationDAO sharedInstance];
|
|
864
|
+
return [locationDAO deleteLocation:locationId error:outError];
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
- (BOOL) deleteAllLocations:(NSError * __autoreleasing *)outError
|
|
868
|
+
{
|
|
869
|
+
MAURSQLiteLocationDAO* locationDAO = [MAURSQLiteLocationDAO sharedInstance];
|
|
870
|
+
return [locationDAO deleteAllLocations:outError];
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
- (MAURLocation*)getCurrentLocation:(int)timeout maximumAge:(long)maximumAge
|
|
874
|
+
enableHighAccuracy:(BOOL)enableHighAccuracy
|
|
875
|
+
error:(NSError * __autoreleasing *)outError
|
|
876
|
+
{
|
|
877
|
+
__block NSError *error = nil;
|
|
878
|
+
__block CLLocation *location = nil;
|
|
879
|
+
|
|
880
|
+
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
|
881
|
+
[self runOnMainThread:^{
|
|
882
|
+
CLLocation *currentLocation = [MAURLocationManager sharedInstance].locationManager.location;
|
|
883
|
+
if (currentLocation != nil) {
|
|
884
|
+
long locationAge = ceil(fabs([currentLocation.timestamp timeIntervalSinceNow]) * 1000);
|
|
885
|
+
if (locationAge <= maximumAge) {
|
|
886
|
+
location = currentLocation;
|
|
887
|
+
dispatch_semaphore_signal(sema);
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
INTULocationManager *locationManager = [INTULocationManager sharedInstance];
|
|
893
|
+
float timeoutInSeconds = ceil((float)timeout/1000);
|
|
894
|
+
[locationManager requestLocationWithDesiredAccuracy:enableHighAccuracy ? INTULocationAccuracyRoom : INTULocationAccuracyCity
|
|
895
|
+
timeout:timeoutInSeconds
|
|
896
|
+
delayUntilAuthorized:YES // This parameter is optional, defaults to NO if omitted
|
|
897
|
+
block:^(CLLocation *currentLocation, INTULocationAccuracy achievedAccuracy, INTULocationStatus status) {
|
|
898
|
+
if (status == INTULocationStatusSuccess) {
|
|
899
|
+
// Request succeeded, meaning achievedAccuracy is at least the requested accuracy, and
|
|
900
|
+
// currentLocation contains the device's current location.
|
|
901
|
+
location = currentLocation;
|
|
902
|
+
}
|
|
903
|
+
else if (status == INTULocationStatusTimedOut) {
|
|
904
|
+
// Wasn't able to locate the user with the requested accuracy within the timeout interval.
|
|
905
|
+
// However, currentLocation contains the best location available (if any) as of right now,
|
|
906
|
+
// and achievedAccuracy has info on the accuracy/recency of the location in currentLocation.
|
|
907
|
+
error = [NSError errorWithDomain:BGGeolocationDomain code:TIMEOUT userInfo:nil];
|
|
908
|
+
}
|
|
909
|
+
else {
|
|
910
|
+
// An error occurred, more info is available by looking at the specific status returned.
|
|
911
|
+
error = [NSError errorWithDomain:BGGeolocationDomain code:POSITION_UNAVAILABLE userInfo:nil];
|
|
912
|
+
}
|
|
913
|
+
dispatch_semaphore_signal(sema);
|
|
914
|
+
}];
|
|
915
|
+
}];
|
|
916
|
+
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
|
|
917
|
+
|
|
918
|
+
if (location != nil) {
|
|
919
|
+
return [MAURLocation fromCLLocation:location];
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
if (outError != nil) {
|
|
923
|
+
*outError = error;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
return nil;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
- (MAURConfig*) getConfig
|
|
930
|
+
{
|
|
931
|
+
if (_config == nil) {
|
|
932
|
+
MAURSQLiteConfigurationDAO* configDAO = [MAURSQLiteConfigurationDAO sharedInstance];
|
|
933
|
+
_config = [configDAO retrieveConfiguration];
|
|
934
|
+
if (_config == nil) {
|
|
935
|
+
_config = [[MAURConfig alloc] initWithDefaults];
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
return _config;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
- (NSArray*) getLogEntries:(NSInteger)limit
|
|
943
|
+
{
|
|
944
|
+
MAURLogReader *logReader = [[MAURLogReader alloc] initWithLogDirectory:[self loggerDirectory]];
|
|
945
|
+
return [logReader getEntries:limit fromLogEntryId:0 minLogLevel:DDLogFlagDebug];
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
- (NSArray*) getLogEntries:(NSInteger)limit fromLogEntryId:(NSInteger)entryId minLogLevelFromString:(NSString *)minLogLevel
|
|
949
|
+
{
|
|
950
|
+
MAURLogReader *logReader = [[MAURLogReader alloc] initWithLogDirectory:[self loggerDirectory]];
|
|
951
|
+
return [logReader getLogEntries:limit fromLogEntryId:entryId minLogLevelAsString:minLogLevel];
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
- (NSArray*) getLogEntries:(NSInteger)limit fromLogEntryId:(NSInteger)entryId minLogLevel:(DDLogFlag)minLogLevel
|
|
955
|
+
{
|
|
956
|
+
MAURLogReader *logReader = [[MAURLogReader alloc] initWithLogDirectory:[self loggerDirectory]];
|
|
957
|
+
NSArray *logs = [logReader getEntries:limit fromLogEntryId:entryId minLogLevel:minLogLevel];
|
|
958
|
+
return logs;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
- (void) forceSync
|
|
962
|
+
{
|
|
963
|
+
if (![_config syncEnabled]) {
|
|
964
|
+
DDLogDebug(@"Sync disabled in config, skipping forceSync");
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
[postLocationTask sync];
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
- (void) clearSync
|
|
971
|
+
{
|
|
972
|
+
MAURSQLiteLocationDAO *locationDAO = [MAURSQLiteLocationDAO sharedInstance];
|
|
973
|
+
NSError *error = nil;
|
|
974
|
+
if ([locationDAO deletePendingSyncLocations:&error]) {
|
|
975
|
+
DDLogDebug(@"Cleared pending sync locations");
|
|
976
|
+
} else {
|
|
977
|
+
DDLogError(@"clearSync failed: %@", error.localizedDescription);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
- (NSInteger) getPendingSyncCount
|
|
982
|
+
{
|
|
983
|
+
MAURSQLiteLocationDAO *locationDAO = [MAURSQLiteLocationDAO sharedInstance];
|
|
984
|
+
NSNumber *count = [locationDAO getLocationsForSyncCount];
|
|
985
|
+
return count != nil ? [count integerValue] : 0;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
- (void) notify:(NSString*)message
|
|
989
|
+
{
|
|
990
|
+
localNotification.fireDate = [NSDate date];
|
|
991
|
+
localNotification.alertBody = message;
|
|
992
|
+
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
-(void) runOnMainThread:(dispatch_block_t)completionHandle {
|
|
996
|
+
BOOL alreadyOnMainThread = [NSThread isMainThread];
|
|
997
|
+
// this check avoids possible deadlock resulting from
|
|
998
|
+
// calling dispatch_sync() on the same queue as current one
|
|
999
|
+
if (alreadyOnMainThread) {
|
|
1000
|
+
// execute code in place
|
|
1001
|
+
completionHandle();
|
|
1002
|
+
} else {
|
|
1003
|
+
// dispatch to main queue
|
|
1004
|
+
dispatch_sync(dispatch_get_main_queue(), completionHandle);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
- (NSString *)loggerDirectory
|
|
1009
|
+
{
|
|
1010
|
+
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
|
1011
|
+
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : NSTemporaryDirectory();
|
|
1012
|
+
|
|
1013
|
+
return [basePath stringByAppendingPathComponent:@"SQLiteLogger"];
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
- (void) onStationaryChanged:(MAURLocation *)location
|
|
1017
|
+
{
|
|
1018
|
+
DDLogDebug(@"%@ #onStationaryChanged", TAG);
|
|
1019
|
+
|
|
1020
|
+
// v4.5.2: drop stationary fixes whose accuracy is worse than the configured
|
|
1021
|
+
// maxAcceptedAccuracy threshold. Mirrors the Android filter in
|
|
1022
|
+
// AbstractLocationProvider.handleStationary.
|
|
1023
|
+
NSNumber *maxAcc = [self getConfig].maxAcceptedAccuracy;
|
|
1024
|
+
if (maxAcc != nil && maxAcc.doubleValue > 0 && location.accuracy != nil
|
|
1025
|
+
&& [location.accuracy doubleValue] > maxAcc.doubleValue) {
|
|
1026
|
+
DDLogDebug(@"%@ dropping stationary fix accuracy=%@ exceeds maxAcceptedAccuracy=%@", TAG, location.accuracy, maxAcc);
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
stationaryLocation = location;
|
|
1031
|
+
|
|
1032
|
+
// v4.5.1 — enrichment moved into MAURPostLocationTask.add: so pending events / battery
|
|
1033
|
+
// land on the post-transform instance (and survive transforms that return new instances).
|
|
1034
|
+
|
|
1035
|
+
[postLocationTask add:location];
|
|
1036
|
+
|
|
1037
|
+
MAURConfig *config = [self getConfig];
|
|
1038
|
+
if ([config isDebugging]) {
|
|
1039
|
+
double distanceFilter = [MAURLocationManager sharedInstance].distanceFilter;
|
|
1040
|
+
[self notify:[NSString stringWithFormat:@"Stationary update: %s\nSPD: %0.0f | DF: %f | ACY: %0.0f | RAD: %0.0f",
|
|
1041
|
+
((operationMode == MAURForegroundMode) ? "FG" : "BG"),
|
|
1042
|
+
[location.speed doubleValue],
|
|
1043
|
+
distanceFilter,
|
|
1044
|
+
[location.accuracy doubleValue],
|
|
1045
|
+
[location.radius doubleValue]
|
|
1046
|
+
]];
|
|
1047
|
+
|
|
1048
|
+
AudioServicesPlaySystemSound (locationSyncSound);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// Any javascript stationaryRegion event-listeners?
|
|
1052
|
+
if (self.delegate && [self.delegate respondsToSelector:@selector(onStationaryChanged:)]) {
|
|
1053
|
+
[self.delegate onStationaryChanged:location];
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
- (void) onLocationChanged:(MAURLocation *)location
|
|
1058
|
+
{
|
|
1059
|
+
DDLogDebug(@"%@ #onLocationChanged %@", TAG, location);
|
|
1060
|
+
|
|
1061
|
+
// v4.5.2: drop fixes whose accuracy is worse than maxAcceptedAccuracy.
|
|
1062
|
+
// Mirrors AbstractLocationProvider.handleLocation on Android.
|
|
1063
|
+
NSNumber *maxAcc = [self getConfig].maxAcceptedAccuracy;
|
|
1064
|
+
if (maxAcc != nil && maxAcc.doubleValue > 0 && location.accuracy != nil
|
|
1065
|
+
&& [location.accuracy doubleValue] > maxAcc.doubleValue) {
|
|
1066
|
+
DDLogDebug(@"%@ dropping fix accuracy=%@ exceeds maxAcceptedAccuracy=%@", TAG, location.accuracy, maxAcc);
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
stationaryLocation = nil;
|
|
1071
|
+
lastReceivedLocation = location; // v3.5 Phase 4: cached for heartbeat payload
|
|
1072
|
+
|
|
1073
|
+
// v4.0 Phase 6: feed driver-insights state machine. Listener may attach events to `location`.
|
|
1074
|
+
[self drivingDetectorFeed:location];
|
|
1075
|
+
// v4.2 Phase 8: keep sensor pipeline aware of the latest fix.
|
|
1076
|
+
sensorFusion.lastLocation = location;
|
|
1077
|
+
// v4.5.1 — pending events drain + battery snapshot moved into MAURPostLocationTask.add:
|
|
1078
|
+
// so they run AFTER any user-supplied locationTransform. The previous order lost them
|
|
1079
|
+
// whenever the transform returned nil.
|
|
1080
|
+
|
|
1081
|
+
[postLocationTask add:location];
|
|
1082
|
+
|
|
1083
|
+
MAURConfig *config = [self getConfig];
|
|
1084
|
+
if ([config isDebugging]) {
|
|
1085
|
+
double distanceFilter = [MAURLocationManager sharedInstance].distanceFilter;
|
|
1086
|
+
[self notify:[NSString stringWithFormat:@"Location update: %s\nSPD: %0.0f | DF: %f | ACY: %0.0f",
|
|
1087
|
+
((operationMode == MAURForegroundMode) ? "FG" : "BG"),
|
|
1088
|
+
[location.speed doubleValue],
|
|
1089
|
+
distanceFilter,
|
|
1090
|
+
[location.accuracy doubleValue]
|
|
1091
|
+
]];
|
|
1092
|
+
|
|
1093
|
+
AudioServicesPlaySystemSound (locationSyncSound);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
// Delegate to main module
|
|
1097
|
+
if (self.delegate && [self.delegate respondsToSelector:@selector(onLocationChanged:)]) {
|
|
1098
|
+
[self.delegate onLocationChanged:location];
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
- (void) onAuthorizationChanged:(MAURLocationAuthorizationStatus)authStatus
|
|
1103
|
+
{
|
|
1104
|
+
[self.delegate onAuthorizationChanged:authStatus];
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
- (void) onError:(NSError*)error
|
|
1108
|
+
{
|
|
1109
|
+
[self.delegate onError:error];
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
- (void) onLocationPause
|
|
1113
|
+
{
|
|
1114
|
+
[self.delegate onLocationPause];
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
- (void) onLocationResume
|
|
1118
|
+
{
|
|
1119
|
+
[self.delegate onLocationResume];
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
- (void) onActivityChanged:(MAURActivity *)activity
|
|
1123
|
+
{
|
|
1124
|
+
DDLogDebug(@"%@ #onActivityChanged %@", TAG, activity);
|
|
1125
|
+
|
|
1126
|
+
if ([self getConfig].isDebugging) {
|
|
1127
|
+
[self notify:[NSString stringWithFormat:@"%@ activity detected: %@ activity, confidence: %@", TAG, activity.type, activity.confidence]];
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
[self.delegate onActivityChanged:activity];
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/**@
|
|
1134
|
+
* If you don't stopMonitoring when application terminates, the app will be awoken still when a
|
|
1135
|
+
* new location arrives, essentially monitoring the user's location even when they've killed the app.
|
|
1136
|
+
* Might be desirable in certain apps.
|
|
1137
|
+
*/
|
|
1138
|
+
- (void) onAppTerminate
|
|
1139
|
+
{
|
|
1140
|
+
MAURConfig *config = [self getConfig];
|
|
1141
|
+
if ([config stopOnTerminate]) {
|
|
1142
|
+
DDLogInfo(@"%@ #onAppTerminate.", TAG);
|
|
1143
|
+
[self stop:nil];
|
|
1144
|
+
} else {
|
|
1145
|
+
[locationProvider onTerminate];
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
- (void) dealloc
|
|
1150
|
+
{
|
|
1151
|
+
DDLogDebug(@"%@ #dealloc", TAG);
|
|
1152
|
+
// currently noop
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
#pragma mark - Location transform
|
|
1156
|
+
|
|
1157
|
+
+ (void) setLocationTransform:(MAURLocationTransform _Nullable)transform
|
|
1158
|
+
{
|
|
1159
|
+
[MAURPostLocationTask setLocationTransform:transform];
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
+ (MAURLocationTransform _Nullable) locationTransform
|
|
1163
|
+
{
|
|
1164
|
+
return [MAURPostLocationTask locationTransform];
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
#pragma mark - MAURPostLocationTaskDelegate
|
|
1168
|
+
|
|
1169
|
+
- (void) postLocationTaskRequestedAbortUpdates:(MAURPostLocationTask *)task
|
|
1170
|
+
{
|
|
1171
|
+
if (_delegate && [_delegate respondsToSelector:@selector(onAbortRequested)])
|
|
1172
|
+
{
|
|
1173
|
+
// We have a delegate, tell it that there's a request.
|
|
1174
|
+
// It will decide whether to stop or not.
|
|
1175
|
+
[_delegate onAbortRequested];
|
|
1176
|
+
}
|
|
1177
|
+
else
|
|
1178
|
+
{
|
|
1179
|
+
// No delegate, we may be running in the background.
|
|
1180
|
+
// Let's just stop.
|
|
1181
|
+
[self stop:nil];
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
- (void) postLocationTaskHttpAuthorizationUpdates:(MAURPostLocationTask *)task
|
|
1186
|
+
{
|
|
1187
|
+
if (_delegate && [_delegate respondsToSelector:@selector(onHttpAuthorization)])
|
|
1188
|
+
{
|
|
1189
|
+
[_delegate onHttpAuthorization];
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
@end
|