react-native-move-sdk 2.13.0 → 2.14.0-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/android/build.gradle.kts +42 -0
  2. package/android/gradle.properties +16 -6
  3. package/android/settings.gradle.kts +35 -0
  4. package/android/src/{main/java/in/dolph/move/sdk → legacy}/MoveSdkModule.kt +30 -195
  5. package/android/src/main/AndroidManifest.xml +1 -11
  6. package/android/src/main/java/{in/dolph/move/sdk → com/movesdk}/DeviceScanner.kt +19 -21
  7. package/android/src/main/java/{in/dolph/move/sdk → com/movesdk}/Mapper.kt +3 -2
  8. package/android/src/main/java/com/movesdk/MoveExtensions.kt +188 -0
  9. package/android/src/main/java/{in/dolph/move/sdk/MoveSdkRepository.kt → com/movesdk/MoveSdkConfigRepository.kt} +3 -1
  10. package/android/src/main/java/com/movesdk/MoveSdkPackage.kt +32 -0
  11. package/android/src/main/java/{in/dolph/move/sdk → com/movesdk}/NativeMoveSdkWrapper.kt +201 -61
  12. package/android/src/main/java/com/movesdk/extentions/ConnectionExt.kt +52 -0
  13. package/android/src/main/java/{in/dolph/move/sdk → com/movesdk}/extentions/PermissionUtils.kt +1 -1
  14. package/android/src/main/java/{in/dolph/move/sdk → com/movesdk}/extentions/SharedPreferencesExt.kt +1 -1
  15. package/android/src/main/java/{in/dolph/move/sdk → com/movesdk}/metadata/MetadataStorage.kt +1 -1
  16. package/android/src/main/java/{in/dolph/move/sdk → com/movesdk}/metadata/MetadataStorageImpl.kt +2 -2
  17. package/android/src/main/java/com/movesdk/shared/Constants.kt +39 -0
  18. package/android/src/turbo/MoveSdkModule.kt +422 -0
  19. package/ios/MoveSdk.h +13 -0
  20. package/ios/MoveSdk.mm +717 -0
  21. package/ios/MoveSdk.swift +1022 -0
  22. package/ios/{NativeModule/MoveSdkDeviceScanner.swift → MoveSdkDeviceScanner.swift} +1 -1
  23. package/lib/commonjs/NativeMoveSdk.js +21 -0
  24. package/lib/commonjs/NativeMoveSdk.js.map +1 -0
  25. package/lib/commonjs/components/LazyMoveSdk.js +22 -41
  26. package/lib/commonjs/components/LazyMoveSdk.js.map +1 -1
  27. package/lib/commonjs/index.js +331 -25
  28. package/lib/commonjs/index.js.map +1 -1
  29. package/lib/commonjs/package.json +1 -0
  30. package/lib/module/NativeMoveSdk.js +17 -0
  31. package/lib/module/NativeMoveSdk.js.map +1 -0
  32. package/lib/module/components/LazyMoveSdk.js +15 -20
  33. package/lib/module/components/LazyMoveSdk.js.map +1 -1
  34. package/lib/module/index.js +337 -3
  35. package/lib/module/index.js.map +1 -1
  36. package/lib/module/package.json +1 -0
  37. package/lib/typescript/commonjs/example/src/App.d.ts +2 -0
  38. package/lib/typescript/commonjs/example/src/App.d.ts.map +1 -0
  39. package/lib/typescript/commonjs/package.json +1 -0
  40. package/lib/typescript/commonjs/src/NativeMoveSdk.d.ts +133 -0
  41. package/lib/typescript/commonjs/src/NativeMoveSdk.d.ts.map +1 -0
  42. package/lib/typescript/{components → commonjs/src/components}/LazyMoveSdk.d.ts +2 -1
  43. package/lib/typescript/commonjs/src/components/LazyMoveSdk.d.ts.map +1 -0
  44. package/lib/typescript/commonjs/src/index.d.ts +84 -0
  45. package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
  46. package/lib/typescript/module/example/src/App.d.ts +2 -0
  47. package/lib/typescript/module/example/src/App.d.ts.map +1 -0
  48. package/lib/typescript/module/package.json +1 -0
  49. package/lib/typescript/module/src/NativeMoveSdk.d.ts +133 -0
  50. package/lib/typescript/module/src/NativeMoveSdk.d.ts.map +1 -0
  51. package/lib/typescript/module/src/components/LazyMoveSdk.d.ts +8 -0
  52. package/lib/typescript/module/src/components/LazyMoveSdk.d.ts.map +1 -0
  53. package/lib/typescript/module/src/index.d.ts +84 -0
  54. package/lib/typescript/module/src/index.d.ts.map +1 -0
  55. package/package.json +197 -138
  56. package/react-native-move-sdk.podspec +30 -9
  57. package/src/NativeMoveSdk.ts +227 -0
  58. package/src/components/LazyMoveSdk.tsx +2 -2
  59. package/src/{MoveSdk.ts → index.tsx} +131 -199
  60. package/android/.project +0 -17
  61. package/android/build.gradle +0 -153
  62. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  63. package/android/gradle/wrapper/gradle-wrapper.properties +0 -8
  64. package/android/gradlew +0 -183
  65. package/android/gradlew.bat +0 -100
  66. package/android/src/main/java/in/dolph/move/sdk/MoveExtensions.kt +0 -72
  67. package/android/src/main/java/in/dolph/move/sdk/MoveSdkPackage.kt +0 -19
  68. package/android/src/main/java/in/dolph/move/sdk/WrapperBtConnectionsReceiver.kt +0 -99
  69. package/android/src/main/res/drawable-anydpi-v24/ic_notification.xml +0 -13
  70. package/android/src/main/res/drawable-hdpi/ic_notification.png +0 -0
  71. package/android/src/main/res/drawable-mdpi/ic_notification.png +0 -0
  72. package/android/src/main/res/drawable-xhdpi/ic_notification.png +0 -0
  73. package/android/src/main/res/drawable-xxhdpi/ic_notification.png +0 -0
  74. package/android/src/main/res/values/strings.xml +0 -9
  75. package/android/src/main/strings.xml +0 -0
  76. package/android/src/test/java/io/dolphin/move/MoveWrapperTest.kt +0 -190
  77. package/ios/MoveSdk-Bridging-Header.h +0 -3
  78. package/ios/MoveSdk.xcodeproj/project.pbxproj +0 -287
  79. package/ios/MoveSdk.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -4
  80. package/ios/MoveSdk.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  81. package/ios/NativeModule/MoveSdk.h +0 -144
  82. package/ios/NativeModule/MoveSdk.swift +0 -1359
  83. package/lib/commonjs/MoveSdk.js +0 -443
  84. package/lib/commonjs/MoveSdk.js.map +0 -1
  85. package/lib/module/MoveSdk.js +0 -432
  86. package/lib/module/MoveSdk.js.map +0 -1
  87. package/lib/typescript/MoveSdk.d.ts +0 -158
  88. package/lib/typescript/index.d.ts +0 -3
  89. package/src/index.ts +0 -3
@@ -0,0 +1,1022 @@
1
+ import CoreMotion
2
+ import DolphinMoveSDK
3
+ import HealthKit
4
+
5
+ /// Move SDK instance singleton delegate.
6
+ @objc public protocol MoveSDKDelegate {
7
+ /// Send event to observer.
8
+ /// - Parameters:
9
+ /// - event: Event key.
10
+ /// - data: Event object.
11
+ @objc func send(event: String, data: Any)
12
+ }
13
+
14
+ /// Wrapper `MoveOptions` key.
15
+ @objc public enum MoveSdkOptionKey: Int {
16
+ public typealias RawValue = Int
17
+ case startDelay
18
+ case interval
19
+ case duration
20
+ case stopScanOnFirstDiscovered
21
+ case motionPermissionMandatory
22
+ case backgroundLocationPermissionMandatory
23
+ case useBackendConfig
24
+ }
25
+
26
+ /// Wrapper `MoveOptions` key value pair.
27
+ @objc public class MoveSdkOption: NSObject {
28
+ @objc let key: MoveSdkOptionKey
29
+ @objc let value: NSNumber
30
+
31
+ @objc public init(key: MoveSdkOptionKey, value: NSNumber) {
32
+ self.key = key
33
+ self.value = value
34
+ super.init()
35
+ }
36
+
37
+ override public init() {
38
+ fatalError()
39
+ }
40
+ }
41
+
42
+ /// Wrapper error object.
43
+ @objc public class MoveSdkError: NSObject {
44
+ @objc public let title: String
45
+ @objc public let text: String
46
+
47
+ init(_ title: String, text: String? = nil) {
48
+ self.title = title
49
+ self.text = text ?? title
50
+ super.init()
51
+ }
52
+
53
+ /// Address resolution failed.
54
+ static var resolveFailed = MoveSdkError("RESOLVE_FAILED")
55
+ /// Service unavailable.
56
+ static var serviceUnavailable = MoveSdkError("SERVICE_UNAVAILABLE")
57
+ /// Thershold reached.
58
+ static var thresholdReached = MoveSdkError("THRESHOLD_REACHED")
59
+ /// Geolookup error.
60
+ static var geocodeError = MoveSdkError("GEOCODE_ERROR")
61
+ /// SDK Authentication invalid.
62
+ static var authInvalid = MoveSdkError("AUTH_INVALID")
63
+ /// Initialization error.
64
+ static var initializationError = MoveSdkError("INITIALIZATION_ERROR")
65
+ /// Location error.
66
+ static var locationError = MoveSdkError("LOCATION_ERROR")
67
+ /// Network error.
68
+ static var networkError = MoveSdkError("NETWORK_ERROR")
69
+ /// Assistance call failed.
70
+ static var assistanceCallError = MoveSdkError("ASSISTANCE_CALL_ERROR")
71
+ /// Configuration error.
72
+ static var configurationError = MoveSdkError("CONFIGURATION_ERROR")
73
+ /// SDK uninitialized.
74
+ static var uninitialized = MoveSdkError("UNINITIALIZED")
75
+
76
+ /// Other error.
77
+ static func otherError(title: String, text: String) -> MoveSdkError {
78
+ MoveSdkError(title, text: text)
79
+ }
80
+
81
+ /// SDK setup error.
82
+ static func setupError(text: String) -> MoveSdkError {
83
+ MoveSdkError("SETUP_ERROR", text: text)
84
+ }
85
+ }
86
+
87
+ /// Singleton Move SDK instance wrapper.
88
+ @objc public class MoveSDKInstance: NSObject {
89
+ public typealias ErrorListItem = [String: Any]
90
+
91
+ /// Sent event identifiers to React Native.
92
+ internal enum Event: String, CaseIterable {
93
+ /// App event.
94
+ case appEvent = "MOVE_SDK_APP_EVENT"
95
+ /// SDK state change event.
96
+ case sdkState = "MOVE_SDK_STATE"
97
+ /// SDK trip state change event.
98
+ case tripState = "MOVE_SDK_TRIP_STATE"
99
+ /// SDK auth state change event.
100
+ case authState = "MOVE_SDK_AUTH_STATE"
101
+ /// SDK warning listener event.
102
+ case warning = "MOVE_SDK_WARNINGS"
103
+ /// SDK failure listener event.
104
+ case failure = "MOVE_SDK_ERRORS"
105
+ /// SDK detected devices event.
106
+ case devices = "MOVE_SDK_DEVICES"
107
+ /// Device scanning event.
108
+ case scanResult = "MOVE_SDK_SCAN_RESULT"
109
+ /// Device state event.
110
+ case deviceState = "MOVE_SDK_DEVICE_STATE"
111
+ /// SDK configuration update event.
112
+ case configUpdate = "MOVE_SDK_CONFIG_UPDATE"
113
+ /// SDK trip start event.
114
+ case tripStart = "MOVE_SDK_TRIP_START"
115
+ /// Log event.
116
+ case logEvent = "MOVE_SDK_LOG"
117
+ /// SDK health listener event.
118
+ case health = "MOVE_SDK_HEALTH"
119
+ }
120
+
121
+ /// Move SDK service identifiers.
122
+ private enum ServiceName: String {
123
+ /// Driving detection service.
124
+ case driving = "DRIVING"
125
+ /// Driving behavior subservice.
126
+ case drivingBehavior = "DRIVING_BEHAVIOUR"
127
+ /// Distraction free driving subservice.
128
+ case distractionFreeDriving = "DISTRACTION_FREE_DRIVING"
129
+ /// Device discovery subservice.
130
+ case deviceDiscovery = "DEVICE_DISCOVERY"
131
+ /// Walking detection service.
132
+ case walking = "WALKING"
133
+ /// Walking location subservice.
134
+ case walkingLocation = "WALKING_LOCATION"
135
+ /// Cycling detection service.
136
+ case cycling = "CYCLING"
137
+ /// Public transport service.
138
+ case publicTransport = "PUBLIC_TRANSPORT"
139
+ /// Places service.
140
+ case places = "PLACES"
141
+ /// Assistance call service.
142
+ case assistanceCall = "ASSISTANCE_CALL"
143
+ /// Points of interest service.
144
+ case pointsOfInterest = "POINTS_OF_INTEREST"
145
+ /// Automatic impact detection.
146
+ case automaticImpactDetection = "AUTOMATIC_IMPACT_DETECTION"
147
+ /// Health steps service.
148
+ case health = "HEALTH"
149
+ }
150
+
151
+ /// User defaults kets.
152
+ private enum DefaultsKey: String {
153
+ /// Persisted auth object.
154
+ case auth = "MOVE.WRAPPER.AUTH"
155
+ /// Persisted config object.
156
+ case config = "MOVE.WRAPPER.CONFIG"
157
+ /// Persisted trip metadata.
158
+ case metaData = "MOVE.WRAPPER.TRIP.METADATA"
159
+ }
160
+
161
+ /// Shared wrapper instance.
162
+ @objc public static let shared = MoveSDKInstance()
163
+
164
+ /// Device Scanner.
165
+ private lazy var scanner = MoveSdkDeviceScanner()
166
+
167
+ /// Motion mananger for motion permission.
168
+ private lazy var motionManager = CMMotionActivityManager()
169
+
170
+ /// Bluetooth manager for permission.
171
+ private lazy var bluetoothManager = MoveSdkBluetoothManager()
172
+
173
+ /// Move SDK Singleton.
174
+ private let sdk: MoveSDK
175
+
176
+ /// Pending events triggered before event observer registered.
177
+ private var pendingEvents: [Event: Any]
178
+
179
+ /// Healthstore to get permissions from.
180
+ lazy var healthStore = HKHealthStore()
181
+
182
+ /// Persisted trip metadata.
183
+ private var tripMetaData: [String: String] {
184
+ didSet {
185
+ MoveSDKInstance.encode(tripMetaData, forKey: .metaData)
186
+ }
187
+ }
188
+
189
+ /// Objc delegate.
190
+ @objc public weak var delegate: MoveSDKDelegate? {
191
+ didSet {
192
+ for (key, value) in pendingEvents {
193
+ delegate?.send(event: key.rawValue, data: value)
194
+ }
195
+ }
196
+ }
197
+
198
+ /// NSObject init function.
199
+ public override init() {
200
+ sdk = MoveSDK.shared
201
+ pendingEvents = [:]
202
+ delegate = nil
203
+ tripMetaData = MoveSDKInstance.decode(.metaData) ?? [:]
204
+
205
+ super.init()
206
+
207
+ sdk.setSDKStateListener { state in
208
+ let value = "\(state)".uppercased()
209
+ self.send(event: .sdkState, data: ["state": "\(value)"])
210
+ }
211
+
212
+ sdk.setTripStateListener { state in
213
+ let value = "\(state)".uppercased()
214
+ self.send(event: .tripState, data: ["state": "\(value)"])
215
+ }
216
+
217
+ sdk.setTripStartListener { date in
218
+ self.send(event: .tripStart, data: Int64(date.timeIntervalSince1970 * 1000.0))
219
+ }
220
+
221
+ sdk.setAuthStateUpdateListener { state in
222
+ let value = "\(state)".uppercased()
223
+ self.send(event: .authState, data: ["state": "\(value)"])
224
+ }
225
+
226
+ sdk.setServiceFailureListener { failures in
227
+ let data: [String: Any] = ["errors": self.convert(failures: failures)]
228
+ self.send(event: .failure, data: data)
229
+ }
230
+
231
+ sdk.setServiceWarningListener { warnings in
232
+ let data: [String: Any] = ["warnings": self.convert(warnings: warnings)]
233
+ self.send(event: .warning, data: data)
234
+ }
235
+
236
+ sdk.setDeviceDiscoveryListener { scanResults in
237
+ let data = self.convert(scanResults: scanResults)
238
+ self.send(event: .scanResult, data: data)
239
+ }
240
+
241
+ sdk.setDeviceStateListener { results in
242
+ let data = self.convert(devices: results)
243
+ self.send(event: .deviceState, data: data)
244
+ }
245
+
246
+ sdk.setRemoteConfigChangeListener { config in
247
+ let services: [String] = config.services.reduce([]) { $0 + self.convert(service: $1, base: true) }
248
+ let data = ["services": services]
249
+ self.send(event: .configUpdate, data: data)
250
+ }
251
+
252
+ sdk.setTripMetaDataListener { tripStart, tripEnd in
253
+ let metadata = self.tripMetaData
254
+ self.tripMetaData = [:]
255
+ return metadata
256
+ }
257
+
258
+ sdk.setLogListener { (event, value) in
259
+ DispatchQueue.main.async {
260
+ var data: [String: Any] = ["message": event]
261
+
262
+ if let value = value {
263
+ data["value"] = value
264
+ }
265
+
266
+ self.send(event: .logEvent, data: data)
267
+ }
268
+ }
269
+
270
+ sdk.setHealthScoreListener { items in
271
+ let data: [[String: String]] = items.map {
272
+ ["reason": $0.reason.rawValue.snakeCased(), "description": $0.description]
273
+ }
274
+ self.send(event: .health, data: data)
275
+ }
276
+ }
277
+
278
+ /// Convert service strings to config subroutine.
279
+ /// - Parameters:
280
+ /// - timelineDetectionServices: Timeline services strings.
281
+ /// - drivingServices: Driving services strings.
282
+ /// - walkingServices: Walking services strings
283
+ /// - Returns: `MoveConfig` for setup.
284
+ private func convert(timelineDetectionServices: Array<String>,
285
+ drivingServices: Array<String>,
286
+ walkingServices: Array<String>) -> MoveConfig {
287
+
288
+ let transformedServices: [MoveConfig.DrivingService] = drivingServices.compactMap {
289
+ switch $0 {
290
+ case ServiceName.distractionFreeDriving.rawValue:
291
+ return .distractionFreeDriving
292
+ case ServiceName.drivingBehavior.rawValue:
293
+ return .drivingBehavior
294
+ case ServiceName.deviceDiscovery.rawValue:
295
+ return .deviceDiscovery
296
+ default:
297
+ return nil
298
+ }
299
+ }
300
+
301
+ let transformedWalkingServices: [MoveConfig.WalkingService] = walkingServices.compactMap {
302
+ if $0 == ServiceName.walkingLocation.rawValue {
303
+ return .location
304
+ }
305
+ return nil
306
+ }
307
+
308
+ let transformedDetectionServices: [MoveConfig.DetectionService] = timelineDetectionServices.compactMap {
309
+ switch $0 {
310
+ case ServiceName.driving.rawValue:
311
+ return .driving(transformedServices)
312
+ case ServiceName.walking.rawValue:
313
+ return .walking(transformedWalkingServices)
314
+ case ServiceName.cycling.rawValue:
315
+ return .cycling
316
+ case ServiceName.publicTransport.rawValue:
317
+ return .publicTransport
318
+ case ServiceName.assistanceCall.rawValue:
319
+ return .assistanceCall
320
+ case ServiceName.pointsOfInterest.rawValue:
321
+ return .pointsOfInterest
322
+ case ServiceName.places.rawValue:
323
+ return .places
324
+ case ServiceName.automaticImpactDetection.rawValue:
325
+ return .automaticImpactDetection
326
+ case ServiceName.health.rawValue:
327
+ return .health
328
+ default:
329
+ return nil
330
+ }
331
+ }
332
+
333
+ return MoveConfig(detectionService: transformedDetectionServices)
334
+ }
335
+
336
+ /// Convert options json dict to `MoveOptions` object.
337
+ /// - Returns: Converted options object.
338
+ private func convert(options: [MoveSdkOption]) -> MoveOptions {
339
+ let moveOptions = MoveOptions()
340
+
341
+ for option in options {
342
+ switch option.key {
343
+ case .startDelay:
344
+ moveOptions.deviceDiscovery.startDelay = option.value.doubleValue
345
+ case .interval:
346
+ moveOptions.deviceDiscovery.interval = option.value.doubleValue
347
+ case .duration:
348
+ moveOptions.deviceDiscovery.duration = option.value.doubleValue
349
+ case .stopScanOnFirstDiscovered:
350
+ moveOptions.deviceDiscovery.stopScanOnFirstDiscovered = option.value.boolValue
351
+ case .motionPermissionMandatory:
352
+ moveOptions.motionPermissionMandatory = option.value.boolValue
353
+ case .backgroundLocationPermissionMandatory:
354
+ moveOptions.backgroundLocationPermissionMandatory = option.value.boolValue
355
+ case .useBackendConfig:
356
+ moveOptions.useBackendConfig = option.value.boolValue
357
+ }
358
+ }
359
+
360
+ return moveOptions
361
+ }
362
+
363
+
364
+ /// Serialize SDK service failures.
365
+ /// - Parameters:
366
+ /// - failures: Move service failures to be serialized.
367
+ ///
368
+ /// - Returns: List of error list items.
369
+ ///
370
+ /// Used in `MoveSDK.getServiceFailures()` and service failure listener.
371
+ fileprivate func convert(failures: [DolphinMoveSDK.MoveServiceFailure]) -> [ErrorListItem] {
372
+ var converted: [ErrorListItem] = []
373
+
374
+ for failure in failures {
375
+ var reasons: [String] = []
376
+ switch failure.reason {
377
+ case let .missingPermission(permissions):
378
+ let content: [String] = permissions.map { self.permissionString($0) }
379
+ reasons = content
380
+ case .unauthorized:
381
+ reasons = ["Unauthorized"]
382
+ }
383
+ let services = convert(service: failure.service)
384
+ for service in services {
385
+ converted.append(["service": service, "reasons": reasons])
386
+ }
387
+ }
388
+
389
+ return converted
390
+ }
391
+
392
+ /// Serialize detection service.
393
+ /// - Parameters:
394
+ /// - service: Move service to be serialized.
395
+ ///
396
+ /// - Returns: List of one or more service strings for service and subservices.
397
+ fileprivate func convert(service: DolphinMoveSDK.MoveConfig.DetectionService, base: Bool = false) -> [String] {
398
+ switch service {
399
+ case let .driving(sub):
400
+ var services: [String] = base || sub.isEmpty ? [ServiceName.driving.rawValue] : []
401
+ for subservice in sub {
402
+ switch subservice {
403
+ case .drivingBehavior:
404
+ services.append(ServiceName.drivingBehavior.rawValue)
405
+ case .distractionFreeDriving:
406
+ services.append(ServiceName.distractionFreeDriving.rawValue)
407
+ case .deviceDiscovery:
408
+ services.append(ServiceName.deviceDiscovery.rawValue)
409
+ default: break
410
+ }
411
+ }
412
+ return services
413
+ case .cycling:
414
+ return [ServiceName.cycling.rawValue]
415
+ case let .walking(sub):
416
+ var services: [String] = base || sub.isEmpty ? [ServiceName.walking.rawValue] : []
417
+ for subservice in sub {
418
+ switch subservice {
419
+ case .location:
420
+ services.append(ServiceName.walkingLocation.rawValue)
421
+ default: break
422
+ }
423
+ }
424
+ return services
425
+ case .places:
426
+ return [ServiceName.places.rawValue]
427
+ case .publicTransport:
428
+ return [ServiceName.publicTransport.rawValue]
429
+ case .pointsOfInterest:
430
+ return [ServiceName.pointsOfInterest.rawValue]
431
+ case .automaticImpactDetection:
432
+ return [ServiceName.automaticImpactDetection.rawValue]
433
+ case .assistanceCall:
434
+ return [ServiceName.assistanceCall.rawValue]
435
+ case .health:
436
+ return [ServiceName.health.rawValue]
437
+ @unknown default:
438
+ return [service.debugDescription]
439
+ }
440
+ }
441
+
442
+ /// Serialize SDK service warnings.
443
+ /// - Parameters:
444
+ /// - failures: Move service warnings to be serialized.
445
+ ///
446
+ /// - Returns: List of error list items.
447
+ ///
448
+ /// Used in `MoveSDK.getServiceWarnings()` and service warning listener.
449
+ fileprivate func convert(warnings: [DolphinMoveSDK.MoveServiceWarning]) -> [ErrorListItem] {
450
+ var converted: [ErrorListItem] = []
451
+
452
+ for warning in warnings {
453
+ var reasons: [String] = []
454
+ switch warning.reason {
455
+ case let .missingPermission(permissions):
456
+ let content: [String] = permissions.compactMap {
457
+ self.permissionString($0)
458
+ }
459
+ reasons = content
460
+ }
461
+ let services = convert(service: warning.service)
462
+ for service in services {
463
+ converted.append(["service": service, "reasons": reasons])
464
+ }
465
+ }
466
+
467
+ return converted
468
+ }
469
+
470
+ /// Serialize device objects for React Native events.
471
+ /// - Parameters:
472
+ /// - devices: `MoveDevice` list.
473
+ /// - Returns: List of serialized device objects.
474
+ ///
475
+ /// Used from `getRegisteredDevices()` or from device scanner.
476
+ fileprivate func convert(devices: [MoveDevice]) -> [[String:String]] {
477
+ var deviceList: [[String: String]] = []
478
+ for device in devices {
479
+ let encoder = JSONEncoder()
480
+ do {
481
+ let data = try encoder.encode(device)
482
+ let str = String(data: data, encoding: .utf8) ?? ""
483
+ let info: [String: String] = ["name": device.name, "data": str, "id": device.id, "isConnected": "\(device.isConnected)"]
484
+ deviceList.append(info)
485
+ } catch {
486
+ print(error.localizedDescription)
487
+ }
488
+ }
489
+
490
+ return deviceList
491
+ }
492
+
493
+ /// Deserialize device objects from React Native function arguments.
494
+ /// - Parameters:
495
+ /// - devices: React Native function arguments list.
496
+ ///
497
+ /// - Returns: List of deserialized `MoveDevice` objects.
498
+ ///
499
+ /// Passed to `register(devices)` or `unregister(devices)`.
500
+ fileprivate func convert(devices: [Any]) -> [MoveDevice] {
501
+ return devices.compactMap { values in
502
+ guard let values = values as? [String: String] else { return nil }
503
+ let encoded: String = values["data"] ?? ""
504
+
505
+ let decoder = JSONDecoder()
506
+ guard let data = encoded.data(using: .utf8) else { return nil }
507
+ do {
508
+ let device = try decoder.decode(MoveDevice.self, from: data)
509
+ /* keep device name from info */
510
+ return device
511
+ } catch {
512
+ return nil
513
+ }
514
+ }
515
+ }
516
+
517
+ /// Serialize device scan results for React Native events.
518
+ /// - Parameters:
519
+ /// - scanResults: `MoveScanResult` list.
520
+ ///
521
+ /// - Returns: List of serialized device objects.
522
+ ///
523
+ /// Used from device detection listener.
524
+ fileprivate func convert(scanResults: [MoveScanResult]) -> [[String: Any]] {
525
+ var deviceList: [[String: Any]] = []
526
+ for result in scanResults {
527
+ let encoder = JSONEncoder()
528
+ do {
529
+ let data = try encoder.encode(result.device)
530
+ let str = String(data: data, encoding: .utf8) ?? ""
531
+ let info: [String: Any] = ["name": result.device.name, "device": str, "id": result.device.id, "isDiscovered": result.isDiscovered]
532
+ deviceList.append(info)
533
+ } catch {
534
+ print(error.localizedDescription)
535
+ }
536
+ }
537
+
538
+ return deviceList
539
+ }
540
+
541
+ /// Decode from user defaults.
542
+ /// - Parameters:
543
+ /// - key: User defaults string key.
544
+ ///
545
+ /// - Returns: Decoded object.
546
+ private static func decode<T>(_ key: DefaultsKey) -> T? where T: Decodable {
547
+ let decoder = PropertyListDecoder()
548
+
549
+ if let data = UserDefaults.standard.object(forKey: key.rawValue) as? Data {
550
+ return try? decoder.decode(T.self, from: data)
551
+ }
552
+
553
+ return nil
554
+ }
555
+
556
+ /// Encode to user defaults.
557
+ /// - Parameters:
558
+ /// - key: User defaults string key.
559
+ private static func encode<T>(_ encodable: T?, forKey key: DefaultsKey) where T: Encodable {
560
+ if let encodable = encodable {
561
+ let encoder = PropertyListEncoder()
562
+ if let data = try? encoder.encode(encodable) {
563
+ UserDefaults.standard.set(data, forKey: key.rawValue)
564
+ }
565
+ } else {
566
+ UserDefaults.standard.removeObject(forKey: key.rawValue)
567
+ }
568
+ }
569
+
570
+ /// Serialize move permission object into string.
571
+ /// - Parameters:
572
+ /// - permission: Permission object to serialize.
573
+ ///
574
+ /// - Returns: Service permission string passed to React Native.
575
+ fileprivate func permissionString(_ permission: DolphinMoveSDK.MovePermission) -> String {
576
+ switch permission {
577
+ case .location:
578
+ return "LOCATION_PERMISSION_MISSING"
579
+ case .backgroundLocation:
580
+ return "BACKGROUND_LOCATION_PERMISSION_MISSING"
581
+ case .preciseLocation:
582
+ return "PRECISE_LOCATION_PERMISSION_MISSING"
583
+ case .motionActivity:
584
+ return "MOTION_PERMISSION_MISSING"
585
+ case .gyroscope:
586
+ return "GYROSCOPE_MISSING"
587
+ case .accelerometer:
588
+ return "ACCELEROMETER_MISSING"
589
+ case .bluetooth:
590
+ return "BLUETOOTH_TURNED_OFF"
591
+ case .bluetoothScan:
592
+ return "BLUETOOTH_PERMISSION_MISSING"
593
+ case .steps:
594
+ return "HEALTH_CONNECT_READ_STEPS_PERMISSION_MISSING"
595
+ @unknown default:
596
+ return "UNKNOWN"
597
+ }
598
+ }
599
+
600
+ /// Send event to observer.
601
+ /// - Parameters:
602
+ /// - event: Event key.
603
+ /// - data: Event object.
604
+ ///
605
+ /// Events are buffered when there is no observer.
606
+ private func send(event: Event, data: Any) {
607
+ if let delegate {
608
+ delegate.send(event: event.rawValue, data: data)
609
+ }
610
+
611
+ pendingEvents[event] = data
612
+ }
613
+ }
614
+
615
+ extension MoveSDKInstance: MoveSdkDeviceScannerDelegate {
616
+ func didDiscover(devices: [DolphinMoveSDK.MoveDevice]) {
617
+ send(event: .devices, data: convert(devices: devices))
618
+ }
619
+ }
620
+
621
+ /// Wrapper extensions for ObjC interface.
622
+ @objc public extension MoveSDKInstance {
623
+ /// Event emitter supported events.
624
+ static var supportedEvents: [String] {
625
+ Event.allCases.map { $0.rawValue }
626
+ }
627
+
628
+ /// Global initialization function.
629
+ func initialize() {
630
+ sdk.initialize(launchOptions: nil)
631
+ }
632
+
633
+ /// Module wrapper.
634
+ var sdkState: String {
635
+ "\(sdk.getSDKState())".uppercased()
636
+ }
637
+
638
+ /// Module wrapper.
639
+ var tripState: String {
640
+ "\(sdk.getTripState())".uppercased()
641
+ }
642
+
643
+ /// Module wrapper.
644
+ var authState: String {
645
+ return "\(sdk.getAuthState())".uppercased()
646
+ }
647
+
648
+ /// Module wrapper.
649
+ var warnings: [ErrorListItem] {
650
+ convert(warnings: sdk.getServiceWarnings())
651
+ }
652
+
653
+ /// Module wrapper.
654
+ var failures: [ErrorListItem] {
655
+ convert(failures: sdk.getServiceFailures())
656
+ }
657
+
658
+ /// Wrapper for `MoveSDK.setup(auth:config:options)`.
659
+ /// - Parameters:
660
+ /// - contractId: Authentication user ID.
661
+ /// - accessToken: Authentication user token.
662
+ /// - refreshToken: Authentication refresh token.
663
+ /// - productId: Authentication project ID.
664
+ /// - timelineDetectionServices: List of timeline detection services.
665
+ /// - drivingServices: List of driving services.
666
+ /// - walkingServices: List of walking services.
667
+ /// - options: Options dictionary object.
668
+ func setup(_ contractId: String,
669
+ accessToken: String,
670
+ refreshToken: String,
671
+ productId: Int64,
672
+ timelineDetectionServices: [String],
673
+ drivingServices: [String],
674
+ walkingServices: [String],
675
+ options: [MoveSdkOption]) {
676
+
677
+ let sdkConfig = convert(timelineDetectionServices: timelineDetectionServices, drivingServices: drivingServices, walkingServices: walkingServices)
678
+ let moveOptions = convert(options: options)
679
+
680
+ let auth = MoveAuth(userToken: accessToken, refreshToken: refreshToken, userID: contractId, projectID: productId)
681
+
682
+ sdk.setup(auth: auth, config: sdkConfig, options: moveOptions)
683
+ }
684
+
685
+ /// Wrapper for `MoveSDK.setup(authCode:config:options:callback)`.
686
+ /// - Parameters:
687
+ /// - authCode: Authentication code.
688
+ /// - timelineDetectionServices: List of timeline detection services.
689
+ /// - drivingServices: List of driving services.
690
+ /// - walkingServices: List of walking services.
691
+ /// - options: Options dictionary object.
692
+ /// - callback: Error callback.
693
+ func setup(_ authCode: String,
694
+ timelineDetectionServices: [String],
695
+ drivingServices: [String],
696
+ walkingServices: [String],
697
+ options: [MoveSdkOption],
698
+ callback: @escaping(MoveSdkError?)->Void) {
699
+
700
+ let sdkConfig = convert(timelineDetectionServices: timelineDetectionServices, drivingServices: drivingServices, walkingServices: walkingServices)
701
+ let moveOptions = convert(options: options)
702
+
703
+ sdk.setup(authCode: authCode, config: sdkConfig, options: moveOptions) { result in
704
+ switch result {
705
+ case .success:
706
+ callback(nil)
707
+ case .networkError:
708
+ callback(.networkError)
709
+ case let .invalidCode(error):
710
+ callback(.setupError(text: "\(error)"))
711
+ }
712
+ }
713
+ }
714
+
715
+ /// Wrapper for `MoveSDK.update(config:)`.
716
+ /// - Parameters:
717
+ /// - timelineDetectionServices: Timeline detection services strings.
718
+ /// - drivingServices: Driving services strings.
719
+ /// - walkingServices: Walking services strings.
720
+ func updateConfig(_ timelineDetectionServices: Array<String>,
721
+ drivingServices: Array<String>,
722
+ walkingServices: Array<String>,
723
+ options: [MoveSdkOption]) {
724
+
725
+ let sdkConfig = convert(timelineDetectionServices: timelineDetectionServices, drivingServices: drivingServices, walkingServices: walkingServices)
726
+ let moveOptions = convert(options: options)
727
+ sdk.update(config: sdkConfig, options: moveOptions)
728
+ }
729
+
730
+ /// Wrapper for `MoveSDK.shutDown()`.
731
+ /// - Parameters:
732
+ /// - force: Force shutdown with possible data loss.
733
+ /// - callback: Error callback.
734
+ func shutdown(_ force: Bool, callback: @escaping(MoveSdkError?)->Void) {
735
+ sdk.shutDown() { status in
736
+ switch status {
737
+ case .success:
738
+ callback(nil)
739
+ case .uninitialized:
740
+ callback(.uninitialized)
741
+ case .networkError:
742
+ callback(.networkError)
743
+ }
744
+ }
745
+ }
746
+
747
+ /// Wrapper for `MoveSDK.updateAuth(auth)`.
748
+ /// - Parameters:
749
+ /// - contractId: Authentication user ID.
750
+ /// - accessToken: Authentication user token.
751
+ /// - refreshToken: Authentication refresh token.
752
+ /// - productId: Authentication project ID.
753
+ /// - callback: Error callback.
754
+ func updateAuth(
755
+ _ contractId: String,
756
+ accessToken: String,
757
+ refreshToken: String,
758
+ productId: Int64,
759
+ callback: @escaping(MoveSdkError?)->Void
760
+ ) {
761
+ let auth = MoveAuth(userToken: accessToken, refreshToken: refreshToken, userID: contractId, projectID: productId)
762
+
763
+ sdk.update(auth: auth) { configurationError in
764
+ if let configurationError = configurationError {
765
+ switch configurationError {
766
+ case .authInvalid:
767
+ callback(.authInvalid)
768
+ default:
769
+ callback(.configurationError)
770
+ }
771
+ } else {
772
+ callback(nil)
773
+ }
774
+ }
775
+ }
776
+
777
+ /// Wrapper for `MoveSDK.geocode(latitude:longitude)`.
778
+ /// - Parameters:
779
+ /// - latitude: Geocoord degrees latitude.
780
+ /// - longitude: Geocoord degrees longitude.
781
+ /// - callback: Error callback.
782
+ func geocode(latitude: Double, longitude: Double, callback: @escaping(String?, MoveSdkError?)->Void) {
783
+ sdk.geocode(latitude: latitude, longitude: longitude) { result in
784
+ switch result {
785
+ case let .success(value):
786
+ callback(value, nil)
787
+ case let .failure(err):
788
+ switch err {
789
+ case .resolveFailed:
790
+ callback(nil, .resolveFailed)
791
+ case .serviceUnreachable:
792
+ callback(nil, .serviceUnavailable)
793
+ case .thresholdReached:
794
+ callback(nil, .thresholdReached)
795
+ }
796
+ }
797
+ }
798
+ }
799
+
800
+ /// Wrapper for `MoveSDK.initiateAssistanceCall()`.
801
+ /// - callback: Error callback.
802
+ func initiateAssistanceCall(callback: @escaping(MoveSdkError?)->Void) {
803
+ sdk.initiateAssistanceCall { status in
804
+ switch status {
805
+ case .success:
806
+ callback(nil)
807
+ case .initializationError:
808
+ callback(.initializationError)
809
+ case .networkError:
810
+ callback(.networkError)
811
+ }
812
+ }
813
+ }
814
+
815
+ /// Wrapper for `MoveSDK.isAuthValid()`.
816
+ /// - Returns: Is authentication valid.
817
+ ///
818
+ /// Authentication is invalid incase of `.invalid` and `.expired`.
819
+ func isAuthValid() -> Bool {
820
+ switch sdk.getAuthState() {
821
+ case .invalid, .expired:
822
+ return false
823
+ default:
824
+ return true
825
+ }
826
+ }
827
+
828
+ /// Wrapper for `MoveSDK.setLiveLocationTag(:)`.
829
+ /// - Parameter tag: Tag. `NULL` = clear.
830
+ /// - Returns: Success.
831
+ func setLiveLocationTag(_ tag: String?) -> Bool {
832
+ sdk.setLiveLocationTag(tag)
833
+ }
834
+
835
+ /// Wrapper for `MoveSDK.startAutomaticDetection()`.
836
+ /// - Returns: Success.
837
+ func startAutomaticDetection() -> Bool {
838
+ sdk.startAutomaticDetection()
839
+ }
840
+
841
+ /// Wrapper for `MoveSDK.stopAutomaticDetection()`
842
+ /// - Returns: Success.
843
+ func stopAutomaticDetection() -> Bool {
844
+ sdk.stopAutomaticDetection()
845
+ }
846
+
847
+ /// Wrapper for `MoveSDK.startTrip(metadata:)`.
848
+ /// - Parameter metadata: Trip metadata.
849
+ /// - Returns: Success.
850
+ func startTrip(_ metadata: [String: String]?) -> Bool {
851
+ sdk.startTrip(metadata: metadata ?? [:])
852
+ }
853
+
854
+ /// Wrapper for `MoveSDK.synchronizeUserData()`.
855
+ /// - Parameters:
856
+ /// - callback: Error callback.
857
+ func synchronizeUserData(_ callback: @escaping(Bool) -> Void) {
858
+ sdk.synchronizeUserData() { success in
859
+ callback(success)
860
+ }
861
+ }
862
+
863
+ /// Start device scanner.
864
+ /// - Parameters:
865
+ /// - filter: Device type filter.
866
+ /// - uuid: iBeacon UUID.
867
+ func startScanningDevices(_ filter: [String], uuid: String?) {
868
+ DispatchQueue.main.async {
869
+ self.scanner.delegate = self
870
+ self.scanner.startScanning(filter: filter, uuid: uuid)
871
+ }
872
+ }
873
+
874
+ /// Stop device scanner.
875
+ func stopScanningDevices() {
876
+ DispatchQueue.main.async {
877
+ self.scanner.stopScanning()
878
+ }
879
+ }
880
+
881
+ func requestHealthPermissions(_ callback: @escaping(Bool, MoveSdkError?)->Void) {
882
+ let stepCountSampleType = HKObjectType.quantityType(forIdentifier: .stepCount)!
883
+
884
+ healthStore.requestAuthorization(toShare: [], read: [stepCountSampleType]) { (success, error) in
885
+ if let error {
886
+ callback(false, .otherError(title: "PERMISSION_ERROR", text: error.localizedDescription))
887
+ } else if success {
888
+ callback(success, nil)
889
+ }
890
+ }
891
+ }
892
+
893
+ /// Request bluetooth permission.
894
+ func requestBluetoothAlwaysUsagePermission() {
895
+ bluetoothManager.getBluetoothState() { _ in }
896
+ }
897
+
898
+ /// Request motion permission.
899
+ func requestMotionPermission() {
900
+ motionManager.queryActivityStarting(from: Date(), to: Date(), to: OperationQueue.main) { _, error in
901
+ }
902
+ }
903
+
904
+ /// Get bluetooth permission.
905
+ /// - Parameter resolve: Is bluetooth permitted.
906
+ func getBluetoothPermission(_ resolve: @escaping (Bool)->Void) {
907
+ bluetoothManager.getBluetoothState() { state in
908
+ switch state {
909
+ case .unknown:
910
+ resolve(false)
911
+ case .resetting:
912
+ resolve(false)
913
+ case .unsupported:
914
+ resolve(false)
915
+ case .unauthorized:
916
+ resolve(false)
917
+ case .poweredOff:
918
+ resolve(true)
919
+ case .poweredOn:
920
+ resolve(true)
921
+ @unknown default:
922
+ resolve(false)
923
+ }
924
+ }
925
+ }
926
+
927
+ /// Get bluetooth state.
928
+ /// - Parameter resolve: Is bluetooth on.
929
+ ///
930
+ /// Undefined without permission.
931
+ func getBluetoothState(_ resolve: @escaping (Bool)->Void) {
932
+ bluetoothManager.getBluetoothState() { state in
933
+ switch state {
934
+ case .unknown:
935
+ resolve(false)
936
+ case .resetting:
937
+ resolve(false)
938
+ case .unsupported:
939
+ resolve(false)
940
+ case .unauthorized:
941
+ resolve(false)
942
+ case .poweredOff:
943
+ resolve(false)
944
+ case .poweredOn:
945
+ resolve(true)
946
+ @unknown default:
947
+ resolve(false)
948
+ }
949
+ }
950
+ }
951
+
952
+ /// Wrapper for `MoveSDK.ignoreCurrentTrip()`.
953
+ func ignoreCurrentTrip() {
954
+ sdk.ignoreCurrentTrip()
955
+ }
956
+
957
+ /// Wrapper for `MoveSDK.finishCurrentTrip()`.
958
+ func finishCurrentTrip() {
959
+ sdk.finishCurrentTrip()
960
+ }
961
+
962
+ /// Wrapper for `MoveSDK.resolveSDKStateError()`.
963
+ func resolveError() {
964
+ sdk.resolveSDKStateError()
965
+ }
966
+
967
+ /// Wrapper for `MoveSDK.setAssistanceMetaData(_)`
968
+ func setAssistanceMetaData(_ metadata: String) {
969
+ sdk.setAssistanceMetaData(metadata)
970
+ }
971
+
972
+ /// Wrapper for `MoveSDK.getDeviceQualifier()`.
973
+ /// Identifier uniquely identifying device, for when device data is transferred to a new phone.
974
+ func getDeviceQualifier() -> String {
975
+ sdk.getDeviceQualifier()
976
+ }
977
+
978
+ /// Wrapper for `MoveSDK.getRegisteredDevices()`.
979
+ func getRegisteredDevices() -> [[String:String]] {
980
+ return convert(devices: sdk.getRegisteredDevices())
981
+ }
982
+
983
+ /// Wrapper for `MoveSDK.register(devices)`.
984
+ func registerDevices(_ devices: [Any]) {
985
+ let devices = convert(devices: devices)
986
+ sdk.register(devices: devices)
987
+ }
988
+
989
+ /// Wrapper for `MoveSDK.unregister(devices)`.
990
+ func unregisterDevices(_ devices: [Any]) {
991
+ let devices = convert(devices: devices)
992
+ sdk.unregister(devices: devices)
993
+ }
994
+
995
+ /// Add trip metadata.
996
+ /// - Parameter metadata: Meta data strings dictionary.
997
+ func setTrip(metadata: [String: String]) {
998
+ var data = self.tripMetaData
999
+ for (key, value) in metadata {
1000
+ data[key] = value
1001
+ }
1002
+ self.tripMetaData = data
1003
+ }
1004
+
1005
+ /// Wrapper for `MoveSDK.forceTripRecognition()`.
1006
+ func forceTripRecognition() {
1007
+ sdk.forceTripRecognition()
1008
+ }
1009
+ }
1010
+
1011
+ extension String {
1012
+ func snakeCased() -> String {
1013
+ let pattern = "([a-z0-9])([A-Z])"
1014
+
1015
+ if let regex = try? NSRegularExpression(pattern: pattern, options: []) {
1016
+ let range = NSRange(location: 0, length: count)
1017
+ return regex.stringByReplacingMatches(in: self, options: [], range: range, withTemplate: "$1_$2").uppercased()
1018
+ }
1019
+
1020
+ return uppercased()
1021
+ }
1022
+ }