react-native-move-sdk 2.4.2 → 2.4.3

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.
@@ -5,14 +5,19 @@ import android.content.Context
5
5
  import android.util.Log
6
6
  import com.facebook.react.bridge.Promise
7
7
  import com.facebook.react.bridge.ReadableArray
8
- import io.dolphin.move.MoveAssistanceCallStatus
8
+ import com.facebook.react.bridge.ReadableMap
9
+ import com.google.gson.Gson
10
+ import io.dolphin.move.DeviceDiscovery
9
11
  import io.dolphin.move.DrivingService
12
+ import io.dolphin.move.MoveAssistanceCallStatus
10
13
  import io.dolphin.move.MoveAuth
11
14
  import io.dolphin.move.MoveAuthState
12
15
  import io.dolphin.move.MoveConfig
13
16
  import io.dolphin.move.MoveConfigurationError
14
17
  import io.dolphin.move.MoveDetectionService
18
+ import io.dolphin.move.MoveDevice
15
19
  import io.dolphin.move.MoveOptions
20
+ import io.dolphin.move.MoveScanResult
16
21
  import io.dolphin.move.MoveSdk
17
22
  import io.dolphin.move.MoveSdkState
18
23
  import io.dolphin.move.MoveServiceFailure
@@ -20,11 +25,16 @@ import io.dolphin.move.MoveServiceWarning
20
25
  import io.dolphin.move.MoveShutdownResult
21
26
  import io.dolphin.move.MoveTripState
22
27
  import io.dolphin.move.WalkingService
28
+ import kotlinx.coroutines.CoroutineScope
29
+ import kotlinx.coroutines.Dispatchers
30
+ import kotlinx.coroutines.launch
31
+ import kotlinx.coroutines.withContext
23
32
 
24
33
  @SuppressLint("StaticFieldLeak", "MissingPermission")
25
34
  class NativeMoveSdkWrapper(private val context: Context) : MoveSdk.StateListener,
26
35
  MoveSdk.InitializeListener, MoveSdk.TripStateListener, MoveSdk.AuthStateUpdateListener,
27
- MoveSdk.MoveLogCallback, MoveSdk.MoveErrorListener, MoveSdk.MoveWarningListener {
36
+ MoveSdk.MoveLogCallback, MoveSdk.MoveErrorListener, MoveSdk.MoveWarningListener,
37
+ MoveSdk.DeviceDiscoveryListener {
28
38
 
29
39
  companion object {
30
40
 
@@ -40,8 +50,12 @@ class NativeMoveSdkWrapper(private val context: Context) : MoveSdk.StateListener
40
50
  }
41
51
 
42
52
  private val configRepository = MoveSdkConfigRepository(context)
53
+ private val deviceScanner = DeviceScanner(context)
43
54
 
44
55
  private var delegate: MoveSdkModule? = null
56
+ private val mainScope = CoroutineScope(Dispatchers.Main)
57
+ private val ioContext = Dispatchers.IO
58
+ private val gson = Gson()
45
59
 
46
60
  fun init(context: Context) {
47
61
  val moveSdk = MoveSdk.init(context.applicationContext)
@@ -55,6 +69,8 @@ class NativeMoveSdkWrapper(private val context: Context) : MoveSdk.StateListener
55
69
  moveSdk.setServiceErrorListener(this)
56
70
  moveSdk.setServiceWarningListener(this)
57
71
 
72
+ moveSdk.deviceDiscoveryListener(this)
73
+
58
74
  // Initialize notifications by default (might be overwritten by setup)
59
75
  val config: MoveSdkConfig = configRepository.loadConfig()
60
76
  try {
@@ -87,7 +103,7 @@ class NativeMoveSdkWrapper(private val context: Context) : MoveSdk.StateListener
87
103
  timelineDetectionServices: ReadableArray,
88
104
  drivingServices: ReadableArray,
89
105
  walkingServices: ReadableArray,
90
- options: ReadableArray,
106
+ options: ReadableMap?,
91
107
  // Platform config
92
108
  recognitionNotificationTitle: String,
93
109
  recognitionNotificationText: String,
@@ -149,7 +165,27 @@ class NativeMoveSdkWrapper(private val context: Context) : MoveSdk.StateListener
149
165
  // TODO - just use recognition notification for now
150
166
  val walkingNotification = recognitionNotification
151
167
 
152
- val featureOptions = options.toArrayList().map { it.toString().snakeToUpperCamelCaseParam() }
168
+ val featureOptions = options?.toHashMap()
169
+ val moveOptions = try {
170
+ val motionPermissionMandatory = featureOptions?.get("motionPermissionMandatory") as? Boolean
171
+ val deviceDiscovery = featureOptions?.get("deviceDiscovery") as? Map<String, Any>
172
+ val startDelay = deviceDiscovery?.get("startDelay") as? Double
173
+ val duration = deviceDiscovery?.get("duration") as? Double
174
+ val interval = deviceDiscovery?.get("interval") as? Double
175
+ val stopScanOnFirstDiscovered = deviceDiscovery?.get("stopScanOnFirstDiscovered") as? Boolean
176
+ MoveOptions(
177
+ motionPermissionRequired = motionPermissionMandatory ?: false,
178
+ deviceDiscovery = DeviceDiscovery(
179
+ startDelay = startDelay?.toLong(),
180
+ duration = duration?.toLong(),
181
+ interval = interval?.toLong(),
182
+ stopScanOnFirstDiscovered = stopScanOnFirstDiscovered ?: false,
183
+ ),
184
+ )
185
+ } catch (e: Exception) {
186
+ Log.e(MoveSdkModule.LOG_TAG, "setup: ${e.message}", e)
187
+ null
188
+ }
153
189
 
154
190
  try {
155
191
  Log.d(MoveSdkModule.LOG_TAG, "MOVE SDK Version " + MoveSdk.version)
@@ -166,11 +202,6 @@ class NativeMoveSdkWrapper(private val context: Context) : MoveSdk.StateListener
166
202
  it.walkingLocationNotification(walkingNotification.toAndroidNotification(context))
167
203
  }
168
204
  val moveConfig = MoveConfig(timelineDetectionServicesToUse)
169
- val moveOptions = MoveOptions(
170
- motionPermissionRequired = featureOptions.contains(
171
- MoveOptions::class.java.declaredFields[0].name
172
- ),
173
- )
174
205
  MoveSdk.setup(
175
206
  auth = moveAuth,
176
207
  config = moveConfig,
@@ -212,7 +243,7 @@ class NativeMoveSdkWrapper(private val context: Context) : MoveSdk.StateListener
212
243
  configRepository.clear()
213
244
 
214
245
  MoveSdk.get()?.shutdown(force) { result ->
215
- when(result) {
246
+ when (result) {
216
247
  MoveShutdownResult.SUCCESS -> promise?.resolve(result.name.toSnakeCase())
217
248
  MoveShutdownResult.NETWORK_ERROR,
218
249
  MoveShutdownResult.UNINITIALIZED -> promise?.reject(Exception(result.name.toSnakeCase()))
@@ -387,7 +418,11 @@ class NativeMoveSdkWrapper(private val context: Context) : MoveSdk.StateListener
387
418
  delegate?.emitDeviceEvent(eventName, eventData)
388
419
  }
389
420
 
390
- private fun emitDeviceEvent(eventName: String, eventData: Array<Any>) {
421
+ fun emitDeviceEvent(eventName: String, eventData: Array<Any>) {
422
+ delegate?.emitDeviceEvent(eventName, eventData)
423
+ }
424
+
425
+ fun emitDeviceEvent(eventName: String, eventData: String) {
391
426
  delegate?.emitDeviceEvent(eventName, eventData)
392
427
  }
393
428
 
@@ -411,12 +446,66 @@ class NativeMoveSdkWrapper(private val context: Context) : MoveSdk.StateListener
411
446
  }
412
447
 
413
448
  fun getDeviceQualifier(): String? {
414
- return MoveSdk.get()?.getDeviceQualifier()
449
+ return MoveSdk.get()?.getDeviceQualifier()
415
450
  }
416
451
 
417
452
  fun isAuthValid(): Boolean {
418
453
  return MoveSdk.get()?.getAuthState() is MoveAuthState.VALID
419
454
  }
455
+
456
+ override fun onScanResult(results: List<MoveScanResult>) {
457
+ emitDeviceEvent(EVENT_MOVE_SCAN_RESULT, results.toScanResultArray())
458
+ }
459
+
460
+ fun registerDevices(rawData: ArrayList<Map<String, String>>?) {
461
+ val devices = rawData?.mapNotNull {
462
+ try {
463
+ gson.fromJson(it["data"], MoveDevice::class.java)
464
+ } catch (e: Exception) {
465
+ null
466
+ }
467
+ }.orEmpty()
468
+ MoveSdk.get()?.registerDevices(devices)
469
+ }
470
+
471
+ fun unregisterDevices(rawData: ArrayList<Map<String, String>>?) {
472
+ val devices = rawData?.mapNotNull {
473
+ try {
474
+ gson.fromJson(it["data"], MoveDevice::class.java)
475
+ } catch (e: Exception) {
476
+ null
477
+ }
478
+ }.orEmpty()
479
+ MoveSdk.get()?.unregisterDevices(devices)
480
+ }
481
+
482
+ fun getRegisteredDevices(promise: Promise) {
483
+ mainScope.launch {
484
+ val devices = mutableListOf<MoveDevice>()
485
+ withContext(ioContext) {
486
+ MoveSdk.get()?.getRegisteredDevices()?.let(devices::addAll)
487
+ }
488
+ promise.resolve(devices.toMoveDeviceTypedArray().toWritableArray())
489
+ }
490
+ }
491
+
492
+ fun startScanningDevices(filters: List<String>, manufacturerId: Int?, uuid: String?) {
493
+ deviceScanner.startScanning(
494
+ filters,
495
+ manufacturerId.takeIf { it != -1 },
496
+ uuid,
497
+ onNewDevices = {
498
+ emitDeviceEvent(EVENT_MOVE_DEVICES, it.toMoveDeviceTypedArray())
499
+ },
500
+ onNewEvent = { eventName, value ->
501
+ emitDeviceEvent(EVENT_MOVE_SDK_APP, "$eventName ${value ?: ""}".trim())
502
+ },
503
+ )
504
+ }
505
+
506
+ fun stopScanningDevices() {
507
+ deviceScanner.stopScanning()
508
+ }
420
509
  }
421
510
 
422
511
  fun List<MoveServiceFailure>.failuresToArray(): Array<Any> {
@@ -20,7 +20,7 @@ RCT_EXTERN_METHOD(setup: (NSString *)contractId
20
20
  timelineDetectionServices: (NSArray<NSString> *)timelineDetectionServices
21
21
  drivingServices: (NSArray<NSString> *)drivingServices
22
22
  walkingServices: (NSArray<NSString> *)walkingServices
23
- options: (NSArray<NSString> *)options
23
+ options: (id)options
24
24
  resolve: (RCTPromiseResolveBlock)resolve
25
25
  reject: (RCTPromiseRejectBlock)reject)
26
26
 
@@ -62,6 +62,19 @@ RCT_EXTERN_METHOD(ignoreCurrentTrip)
62
62
 
63
63
  RCT_EXTERN_METHOD(finishCurrentTrip)
64
64
 
65
+ RCT_EXTERN_METHOD(startScanningDevices: (NSArray*) filter
66
+ uuid: (NSString*) uuid
67
+ manufacturerId: (id)manufacturerId)
68
+
69
+ RCT_EXTERN_METHOD(stopScanningDevices)
70
+
71
+ RCT_EXTERN_METHOD(registerDevices: (NSArray*>)devices)
72
+
73
+ RCT_EXTERN_METHOD(unregisterDevices: (NSArray*>)devices)
74
+
75
+ RCT_EXTERN_METHOD(getRegisteredDevices: (RCTPromiseResolveBlock)resolve
76
+ rejecter: (RCTPromiseRejectBlock)reject)
77
+
65
78
  RCT_EXTERN_METHOD(getAuthState: (RCTPromiseResolveBlock)resolve
66
79
  rejecter: (RCTPromiseRejectBlock)reject)
67
80
 
@@ -3,7 +3,7 @@ import Foundation
3
3
  import DolphinMoveSDK
4
4
 
5
5
  internal protocol MoveSDKLauncherDelegate: AnyObject {
6
- func send(event: MoveSDKLauncher.Event, data: [String: Any])
6
+ func send(event: MoveSDKLauncher.Event, data: Any)
7
7
  }
8
8
 
9
9
  typealias MoveSDKErrorListItem = [String:Any]
@@ -33,6 +33,7 @@ class RCTMoveSdk: RCTEventEmitter {
33
33
  }
34
34
 
35
35
  let motionManager = CMMotionActivityManager()
36
+ var scanner: MoveSdkDeviceScanner?
36
37
 
37
38
  override func startObserving() {
38
39
  MoveSDKLauncher.shared.delegate = self
@@ -46,7 +47,7 @@ class RCTMoveSdk: RCTEventEmitter {
46
47
  timelineDetectionServices: Array<String>,
47
48
  drivingServices: Array<String>,
48
49
  walkingServices: Array<String>,
49
- options: Array<String>,
50
+ options: Any,
50
51
  resolve: @escaping RCTPromiseResolveBlock,
51
52
  reject: @escaping RCTPromiseRejectBlock) {
52
53
 
@@ -94,10 +95,7 @@ class RCTMoveSdk: RCTEventEmitter {
94
95
  // TODO: Fix wrapper and add config option from RN for walking services (actually only 1 now)
95
96
  let sdkConfig = MoveConfig(detectionService: transformedDetectionServices)
96
97
  let auth = MoveAuth(userToken: accessToken, refreshToken: refreshToken, userID: contractId, projectID: productId)
97
- var moveOptions = MoveOptions()
98
- if options.contains("MOTION_PERMISSION_REQUIRE") {
99
- moveOptions.insert(.motionPermissionMandatory)
100
- }
98
+ let moveOptions = convert(options: options)
101
99
 
102
100
  MoveSDKLauncher.shared.setup(auth: auth, config: sdkConfig, options: moveOptions)
103
101
  resolve("OK")
@@ -106,6 +104,36 @@ class RCTMoveSdk: RCTEventEmitter {
106
104
  }
107
105
  }
108
106
 
107
+ private func convert(options: Any) -> MoveOptions {
108
+ let moveOptions = MoveOptions()
109
+
110
+ if let opts = options as? [String: Any] {
111
+ if let value = opts["motionPermissionMandatory"] as? Bool {
112
+ moveOptions.motionPermissionMandatory = value
113
+ }
114
+
115
+ if let deviceDiscovery = opts["deviceDiscovery"] as? [String: Any] {
116
+ if let value = deviceDiscovery["startDelay"] as? Double {
117
+ moveOptions.deviceDiscovery.startDelay = value
118
+ }
119
+
120
+ if let value = deviceDiscovery["duration"] as? Double {
121
+ moveOptions.deviceDiscovery.duration = value
122
+ }
123
+
124
+ if let value = deviceDiscovery["interval"] as? Double {
125
+ moveOptions.deviceDiscovery.interval = value
126
+ }
127
+
128
+ if let value = deviceDiscovery["stopScanOnFirstDiscovered"] as? Bool {
129
+ moveOptions.deviceDiscovery.stopScanOnFirstDiscovered = value
130
+ }
131
+ }
132
+ }
133
+
134
+ return moveOptions
135
+ }
136
+
109
137
  @objc
110
138
  func getState(
111
139
  _ resolve: RCTPromiseResolveBlock,
@@ -124,12 +152,26 @@ class RCTMoveSdk: RCTEventEmitter {
124
152
  resolve(value)
125
153
  }
126
154
 
155
+ @objc
156
+ func getRegisteredDevices(
157
+ _ resolve: RCTPromiseResolveBlock,
158
+ rejecter reject: RCTPromiseRejectBlock
159
+ ) {
160
+ let value = MoveSDKLauncher.shared.convert(devices: MoveSDK.shared.getRegisteredDevices())
161
+ resolve(value)
162
+ }
163
+
127
164
  @objc
128
165
  func isAuthValid(
129
166
  _ resolve: RCTPromiseResolveBlock,
130
167
  rejecter reject: RCTPromiseRejectBlock
131
168
  ) {
132
- resolve(MoveSDK.shared.getAuthState() != .expired)
169
+ switch MoveSDK.shared.getAuthState() {
170
+ case .invalid, .expired:
171
+ resolve(false)
172
+ default:
173
+ resolve(true)
174
+ }
133
175
  }
134
176
 
135
177
  @objc
@@ -283,6 +325,32 @@ class RCTMoveSdk: RCTEventEmitter {
283
325
  MoveSDKLauncher.shared.sdk.finishCurrentTrip()
284
326
  }
285
327
 
328
+ @objc
329
+ func startScanningDevices(_ filter: [String], uuid: String?, manufacturerId: Any?) {
330
+ if scanner == nil {
331
+ scanner = MoveSdkDeviceScanner()
332
+ }
333
+ scanner?.delegate = self
334
+ scanner?.startScanning(filter: filter, uuid: uuid)
335
+ }
336
+
337
+ @objc
338
+ func registerDevices(_ devices: [Any]) {
339
+ let devices = MoveSDKLauncher.shared.convert(devices: devices)
340
+ MoveSDKLauncher.shared.sdk.register(devices: devices)
341
+ }
342
+
343
+ @objc
344
+ func unregisterDevices(_ devices: [Any]) {
345
+ let devices = MoveSDKLauncher.shared.convert(devices: devices)
346
+ MoveSDKLauncher.shared.sdk.unregister(devices: devices)
347
+ }
348
+
349
+ @objc
350
+ func stopScanningDevices() {
351
+ scanner?.stopScanning()
352
+ }
353
+
286
354
  @objc
287
355
  func resolveError() {
288
356
  MoveSDKLauncher.shared.sdk.resolveSDKStateError()
@@ -322,8 +390,14 @@ class RCTMoveSdk: RCTEventEmitter {
322
390
  }
323
391
  }
324
392
 
393
+ extension RCTMoveSdk: MoveSdkDeviceScannerDelegate {
394
+ func didDiscover(devices: [MoveSdkDeviceScanner.DeviceEntry]) {
395
+ send(event: .devices, data: MoveSDKLauncher.shared.convert(devices: devices))
396
+ }
397
+ }
398
+
325
399
  extension RCTMoveSdk: MoveSDKLauncherDelegate {
326
- func send(event: MoveSDKLauncher.Event, data: [String: Any]) {
400
+ func send(event: MoveSDKLauncher.Event, data: Any) {
327
401
  sendEvent(withName: event.rawValue, body: data)
328
402
  }
329
403
  }
@@ -336,6 +410,8 @@ internal class MoveSDKLauncher {
336
410
  case authState = "MOVE_SDK_AUTH_STATE"
337
411
  case warning = "MOVE_SDK_WARNINGS"
338
412
  case failure = "MOVE_SDK_ERRORS"
413
+ case devices = "MOVE_SDK_DEVICES"
414
+ case scanResult = "MOVE_SDK_SCAN_RESULT"
339
415
  }
340
416
 
341
417
  private enum DefaultsKey: String {
@@ -375,6 +451,11 @@ internal class MoveSDKLauncher {
375
451
  let data: [String: Any] = ["warnings": self.convert(warnings: warnings)]
376
452
  self.delegate?.send(event: .warning, data: data)
377
453
  }
454
+
455
+ sdk.setDeviceDiscoveryListener { scanResults in
456
+ let data = self.convert(scanResults: scanResults)
457
+ self.delegate?.send(event: .scanResult, data: data)
458
+ }
378
459
  }
379
460
 
380
461
  func getServiceWarnings() -> [MoveSDKErrorListItem] {
@@ -385,6 +466,58 @@ internal class MoveSDKLauncher {
385
466
  return convert(failures: sdk.getServiceFailures())
386
467
  }
387
468
 
469
+ func convert(devices: [Any]) -> [MoveDevice] {
470
+ return devices.compactMap { values in
471
+ guard let values = values as? [String: String] else { return nil }
472
+ // let name: String = values["name"] ?? ""
473
+ let encoded: String = values["data"] ?? ""
474
+
475
+ let decoder = JSONDecoder()
476
+ guard let data = encoded.data(using: .utf8) else { return nil }
477
+ do {
478
+ let device = try decoder.decode(MoveDevice.self, from: data)
479
+ /* keep device name from info */
480
+ return device
481
+ } catch {
482
+ return nil
483
+ }
484
+ }
485
+ }
486
+
487
+ func convert(devices: [MoveSdkDeviceScanner.DeviceEntry]) -> [[String:String]] {
488
+ var deviceList: [[String: String]] = []
489
+ for device in devices {
490
+ let encoder = JSONEncoder()
491
+ do {
492
+ let data = try encoder.encode(device.device)
493
+ let str = String(data: data, encoding: .utf8) ?? ""
494
+ let info: [String: String] = ["name":device.name, "data": str]
495
+ deviceList.append(info)
496
+ } catch {
497
+ print(error.localizedDescription)
498
+ }
499
+ }
500
+
501
+ return deviceList
502
+ }
503
+
504
+ func convert(scanResults: [MoveScanResult]) -> [[String:Any]] {
505
+ var deviceList: [[String: Any]] = []
506
+ for result in scanResults {
507
+ let encoder = JSONEncoder()
508
+ do {
509
+ let data = try encoder.encode(result.device)
510
+ let str = String(data: data, encoding: .utf8) ?? ""
511
+ let info: [String: Any] = ["name":result.device.name, "device": str, "isDiscovered": result.isDiscovered]
512
+ deviceList.append(info)
513
+ } catch {
514
+ print(error.localizedDescription)
515
+ }
516
+ }
517
+
518
+ return deviceList
519
+ }
520
+
388
521
  func convert(service: DolphinMoveSDK.MoveConfig.DetectionService) -> String {
389
522
  return service.debugDescription
390
523
  // switch service {
@@ -428,8 +561,6 @@ internal class MoveSDKLauncher {
428
561
  reasons = content
429
562
  case .unauthorized:
430
563
  reasons = ["Unauthorized"]
431
- @unknown default:
432
- break
433
564
  }
434
565
  converted.append(["service": convert(service: failure.service), "reasons": reasons])
435
566
  }
@@ -446,8 +577,6 @@ internal class MoveSDKLauncher {
446
577
  case let .missingPermission(permissions):
447
578
  let content: [String] = permissions.map { self.permissionString($0) }
448
579
  reasons = content
449
- @unknown default:
450
- break
451
580
  }
452
581
  converted.append(["service": convert(service: warning.service), "reasons": reasons])
453
582
  }
@@ -0,0 +1,127 @@
1
+ import AVFoundation
2
+ import CoreLocation
3
+ import Foundation
4
+ import DolphinMoveSDK
5
+
6
+ internal protocol MoveSdkDeviceScannerDelegate: AnyObject {
7
+ func didDiscover(devices: [MoveSdkDeviceScanner.DeviceEntry])
8
+ }
9
+
10
+ internal class MoveSdkDeviceScanner: NSObject {
11
+ struct DeviceEntry {
12
+ let device: MoveDevice
13
+ let name: String
14
+ }
15
+
16
+ private let allowedPorts: [AVAudioSession.Port] = [.bluetoothA2DP, .bluetoothHFP, .bluetoothLE, .carAudio]
17
+
18
+ private let session = AVAudioSession.sharedInstance()
19
+ private let locationManager = CLLocationManager()
20
+ private var devices: [DeviceEntry] = []
21
+ private var timer: Timer?
22
+ private var beaconRegion: CLBeaconRegion?
23
+ internal var delegate: MoveSdkDeviceScannerDelegate?
24
+
25
+ override init() {
26
+ super.init()
27
+ locationManager.delegate = self
28
+ do {
29
+ try session.setCategory(.playAndRecord, options: [.allowAirPlay, .allowBluetoothA2DP, .allowBluetooth])
30
+ } catch {
31
+ print("\(error)")
32
+ }
33
+ }
34
+
35
+ func scanAudioPorts() {
36
+ let devices: [DeviceEntry] = (session.availableInputs ?? []).compactMap {
37
+ if !allowedPorts.contains($0.portType) { return nil }
38
+ let device = MoveDevice(name: $0.uid, id: $0.uid)
39
+ return DeviceEntry(device: device, name: "\($0.portName)")
40
+ }
41
+
42
+ add(devices: devices)
43
+ }
44
+
45
+ func scanBeacons(uuid: UUID) {
46
+ let beaconRegion = CLBeaconRegion(proximityUUID: uuid, identifier: "beacons [\(uuid)]")
47
+ locationManager.startRangingBeacons(in: beaconRegion)
48
+ locationManager.requestState(for: beaconRegion)
49
+ self.beaconRegion = beaconRegion
50
+ }
51
+
52
+ func add(devices: [DeviceEntry]) {
53
+ let devices = devices.filter { device in
54
+ !self.devices.contains { $0.device == device.device }
55
+ }
56
+
57
+ if !devices.isEmpty {
58
+ self.devices += devices
59
+ delegate?.didDiscover(devices: devices)
60
+ }
61
+ }
62
+
63
+ func startScanning(filter: [String], uuid uuidString: String? = nil) {
64
+ devices = []
65
+
66
+ if filter.contains("paired") {
67
+ self.scanAudioPorts()
68
+
69
+ let timer = Timer(timeInterval: 1.0, repeats: true) { _ in
70
+ self.scanAudioPorts()
71
+ }
72
+
73
+ self.timer = timer
74
+
75
+ RunLoop.main.add(timer, forMode: .default)
76
+ }
77
+
78
+ if filter.contains("beacon"), let uuidString = uuidString, let uuid = UUID(uuidString: uuidString) {
79
+ scanBeacons(uuid: uuid)
80
+ }
81
+ }
82
+
83
+ func stopScanning() {
84
+ if let beaconRegion = self.beaconRegion {
85
+ locationManager.stopRangingBeacons(in: beaconRegion)
86
+ }
87
+ timer?.invalidate()
88
+ timer = nil
89
+ }
90
+ }
91
+
92
+ extension MoveSdkDeviceScanner: CLLocationManagerDelegate {
93
+ func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
94
+
95
+ let devices = beacons.map {
96
+ let name = "Beacon \($0.major):\($0.minor)"
97
+ let device = MoveDevice(name: name, proximityUUID: $0.proximityUUID, major: UInt16(truncating: $0.major), minor: UInt16(truncating: $0.minor))
98
+ return DeviceEntry(device: device, name: name)
99
+ }
100
+
101
+ add(devices: devices)
102
+ }
103
+
104
+ func locationManager(_ manager: CLLocationManager, rangingBeaconsDidFailFor region: CLBeaconRegion, withError error: Error) {
105
+ print("rangingBeaconsDidFailFor: \(region)")
106
+ }
107
+
108
+ func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
109
+ print("fail: \(error)")
110
+ }
111
+
112
+ func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
113
+ print("monitoringDidFailFor: \(String(describing: region)) \(error)")
114
+ }
115
+
116
+ func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
117
+ print("didEnterRegion: \(region)")
118
+ }
119
+
120
+ func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
121
+ print("didExitRegion: \(region)")
122
+ }
123
+
124
+ func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
125
+ print("didDetermineState: \(region) : \(state)")
126
+ }
127
+ }
@@ -70,7 +70,7 @@ class MoveSdk {
70
70
  }
71
71
 
72
72
  return await NativeMoveSdk.setup(userId, accessToken, refreshToken, projectId, // Config
73
- timelineDetectionServices, drivingServices, walkingServices, options || [], // Platform config
73
+ timelineDetectionServices, drivingServices, walkingServices, options, // Platform config
74
74
  ...platformParams);
75
75
  }
76
76
 
@@ -262,6 +262,42 @@ class MoveSdk {
262
262
  } // **** PERMISSIONS MODULE END *****
263
263
 
264
264
 
265
+ static startScanningDevices(sdkDevicesDetected) {
266
+ let filter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ["paired"];
267
+ let uuid = arguments.length > 2 ? arguments[2] : undefined;
268
+ let manufacturerId = arguments.length > 3 ? arguments[3] : undefined;
269
+ const subscription = eventEmitter.addListener('MOVE_SDK_DEVICES', devices => {
270
+ sdkDevicesDetected(devices);
271
+ });
272
+ const subscriptionRemove = subscription.remove;
273
+
274
+ subscription.remove = () => {
275
+ NativeMoveSdk.stopScanningDevices();
276
+ subscriptionRemove();
277
+ };
278
+
279
+ NativeMoveSdk.startScanningDevices(filter, uuid, manufacturerId !== null && manufacturerId !== void 0 ? manufacturerId : -1);
280
+ return subscription;
281
+ }
282
+
283
+ static async getRegisteredDevices() {
284
+ return await NativeMoveSdk.getRegisteredDevices();
285
+ }
286
+
287
+ static registerDevices(devices) {
288
+ NativeMoveSdk.registerDevices(devices);
289
+ }
290
+
291
+ static unregisterDevices(devices) {
292
+ NativeMoveSdk.registerDevices(devices);
293
+ }
294
+
295
+ static addDeviceDiscoveryListener(onScanResult) {
296
+ return eventEmitter.addListener('MOVE_SDK_SCAN_RESULT', results => {
297
+ onScanResult(results);
298
+ });
299
+ }
300
+
265
301
  }
266
302
 
267
303
  exports.default = MoveSdk;