@expofp/react-native-efp-crowdconnected 0.1.8 → 0.1.10

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 (91) hide show
  1. package/ExpofpCrowdconnected.podspec +1 -1
  2. package/README.md +67 -5
  3. package/android/build.gradle +14 -4
  4. package/android/src/main/java/com/expofpcrowdconnected/ExpoFpCrowdConnectedLocationProviderSettings.kt +24 -20
  5. package/android/src/main/java/com/expofpcrowdconnected/RCTCrowdConnectedLocationProviderModule.kt +112 -6
  6. package/ios/ExpoFpLocationProvider/ExpoFpCrowdConnectedLocationProviderSettings.swift +2 -2
  7. package/ios/ExpoFpLocationProvider/ExpoFpLocationSettings.swift +2 -2
  8. package/ios/ExpoFpLocationProvider/ExpoFpSimulationDelegate.swift +40 -0
  9. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64/CrowdConnectedCore.framework/CrowdConnectedCore +0 -0
  10. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64/CrowdConnectedCore.framework/Headers/CrowdConnectedCore-Swift.h +48 -0
  11. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64/CrowdConnectedCore.framework/Info.plist +0 -0
  12. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64/CrowdConnectedCore.framework/Modules/CrowdConnectedCore.swiftmodule/arm64-apple-ios.abi.json +731 -363
  13. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64/CrowdConnectedCore.framework/Modules/CrowdConnectedCore.swiftmodule/arm64-apple-ios.private.swiftinterface +13 -8
  14. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64/CrowdConnectedCore.framework/Modules/CrowdConnectedCore.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  15. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64/CrowdConnectedCore.framework/Modules/CrowdConnectedCore.swiftmodule/arm64-apple-ios.swiftinterface +12 -7
  16. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64/CrowdConnectedCore.framework/_CodeSignature/CodeResources +13 -13
  17. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCore.framework/CrowdConnectedCore +0 -0
  18. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCore.framework/Headers/CrowdConnectedCore-Swift.h +96 -0
  19. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCore.framework/Info.plist +0 -0
  20. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCore.framework/Modules/CrowdConnectedCore.swiftmodule/arm64-apple-ios-simulator.abi.json +731 -363
  21. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCore.framework/Modules/CrowdConnectedCore.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +13 -8
  22. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCore.framework/Modules/CrowdConnectedCore.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  23. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCore.framework/Modules/CrowdConnectedCore.swiftmodule/arm64-apple-ios-simulator.swiftinterface +12 -7
  24. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCore.framework/Modules/CrowdConnectedCore.swiftmodule/x86_64-apple-ios-simulator.abi.json +731 -363
  25. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCore.framework/Modules/CrowdConnectedCore.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +13 -8
  26. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCore.framework/Modules/CrowdConnectedCore.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  27. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCore.framework/Modules/CrowdConnectedCore.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +12 -7
  28. package/ios/Frameworks/CrowdConnectedCore.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCore.framework/_CodeSignature/CodeResources +23 -23
  29. package/ios/Frameworks/CrowdConnectedCoreBluetooth.xcframework/ios-arm64/CrowdConnectedCoreBluetooth.framework/CrowdConnectedCoreBluetooth +0 -0
  30. package/ios/Frameworks/CrowdConnectedCoreBluetooth.xcframework/ios-arm64/CrowdConnectedCoreBluetooth.framework/Modules/CrowdConnectedCoreBluetooth.swiftmodule/arm64-apple-ios.abi.json +10 -10
  31. package/ios/Frameworks/CrowdConnectedCoreBluetooth.xcframework/ios-arm64/CrowdConnectedCoreBluetooth.framework/_CodeSignature/CodeResources +4 -4
  32. package/ios/Frameworks/CrowdConnectedCoreBluetooth.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCoreBluetooth.framework/CrowdConnectedCoreBluetooth +0 -0
  33. package/ios/Frameworks/CrowdConnectedCoreBluetooth.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCoreBluetooth.framework/Modules/CrowdConnectedCoreBluetooth.swiftmodule/arm64-apple-ios-simulator.abi.json +10 -10
  34. package/ios/Frameworks/CrowdConnectedCoreBluetooth.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCoreBluetooth.framework/Modules/CrowdConnectedCoreBluetooth.swiftmodule/x86_64-apple-ios-simulator.abi.json +10 -10
  35. package/ios/Frameworks/CrowdConnectedCoreBluetooth.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedCoreBluetooth.framework/_CodeSignature/CodeResources +8 -8
  36. package/ios/Frameworks/CrowdConnectedGeo.xcframework/Info.plist +5 -5
  37. package/ios/Frameworks/CrowdConnectedGeo.xcframework/ios-arm64/CrowdConnectedGeo.framework/CrowdConnectedGeo +0 -0
  38. package/ios/Frameworks/CrowdConnectedGeo.xcframework/ios-arm64/CrowdConnectedGeo.framework/Modules/CrowdConnectedGeo.swiftmodule/arm64-apple-ios.abi.json +142 -33
  39. package/ios/Frameworks/CrowdConnectedGeo.xcframework/ios-arm64/CrowdConnectedGeo.framework/Modules/CrowdConnectedGeo.swiftmodule/arm64-apple-ios.private.swiftinterface +7 -0
  40. package/ios/Frameworks/CrowdConnectedGeo.xcframework/ios-arm64/CrowdConnectedGeo.framework/Modules/CrowdConnectedGeo.swiftmodule/arm64-apple-ios.swiftinterface +7 -0
  41. package/ios/Frameworks/CrowdConnectedGeo.xcframework/ios-arm64/CrowdConnectedGeo.framework/_CodeSignature/CodeResources +8 -8
  42. package/ios/Frameworks/CrowdConnectedGeo.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedGeo.framework/CrowdConnectedGeo +0 -0
  43. package/ios/Frameworks/CrowdConnectedGeo.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedGeo.framework/Modules/CrowdConnectedGeo.swiftmodule/arm64-apple-ios-simulator.abi.json +142 -33
  44. package/ios/Frameworks/CrowdConnectedGeo.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedGeo.framework/Modules/CrowdConnectedGeo.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +7 -0
  45. package/ios/Frameworks/CrowdConnectedGeo.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedGeo.framework/Modules/CrowdConnectedGeo.swiftmodule/arm64-apple-ios-simulator.swiftinterface +7 -0
  46. package/ios/Frameworks/CrowdConnectedGeo.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedGeo.framework/Modules/CrowdConnectedGeo.swiftmodule/x86_64-apple-ios-simulator.abi.json +142 -33
  47. package/ios/Frameworks/CrowdConnectedGeo.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedGeo.framework/Modules/CrowdConnectedGeo.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +7 -0
  48. package/ios/Frameworks/CrowdConnectedGeo.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedGeo.framework/Modules/CrowdConnectedGeo.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +7 -0
  49. package/ios/Frameworks/CrowdConnectedGeo.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedGeo.framework/_CodeSignature/CodeResources +16 -16
  50. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64/CrowdConnectedIPS.framework/CrowdConnectedIPS +0 -0
  51. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64/CrowdConnectedIPS.framework/Modules/CrowdConnectedIPS.swiftmodule/arm64-apple-ios.abi.json +251 -113
  52. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64/CrowdConnectedIPS.framework/Modules/CrowdConnectedIPS.swiftmodule/arm64-apple-ios.private.swiftinterface +7 -2
  53. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64/CrowdConnectedIPS.framework/Modules/CrowdConnectedIPS.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  54. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64/CrowdConnectedIPS.framework/Modules/CrowdConnectedIPS.swiftmodule/arm64-apple-ios.swiftinterface +7 -2
  55. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64/CrowdConnectedIPS.framework/_CodeSignature/CodeResources +10 -10
  56. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedIPS.framework/CrowdConnectedIPS +0 -0
  57. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedIPS.framework/Modules/CrowdConnectedIPS.swiftmodule/arm64-apple-ios-simulator.abi.json +251 -113
  58. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedIPS.framework/Modules/CrowdConnectedIPS.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +7 -2
  59. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedIPS.framework/Modules/CrowdConnectedIPS.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  60. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedIPS.framework/Modules/CrowdConnectedIPS.swiftmodule/arm64-apple-ios-simulator.swiftinterface +7 -2
  61. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedIPS.framework/Modules/CrowdConnectedIPS.swiftmodule/x86_64-apple-ios-simulator.abi.json +251 -113
  62. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedIPS.framework/Modules/CrowdConnectedIPS.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +7 -2
  63. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedIPS.framework/Modules/CrowdConnectedIPS.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  64. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedIPS.framework/Modules/CrowdConnectedIPS.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +7 -2
  65. package/ios/Frameworks/CrowdConnectedIPS.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedIPS.framework/_CodeSignature/CodeResources +20 -20
  66. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64/CrowdConnectedShared.framework/CrowdConnectedShared +0 -0
  67. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64/CrowdConnectedShared.framework/Modules/CrowdConnectedShared.swiftmodule/arm64-apple-ios.abi.json +904 -235
  68. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64/CrowdConnectedShared.framework/Modules/CrowdConnectedShared.swiftmodule/arm64-apple-ios.private.swiftinterface +33 -3
  69. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64/CrowdConnectedShared.framework/Modules/CrowdConnectedShared.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  70. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64/CrowdConnectedShared.framework/Modules/CrowdConnectedShared.swiftmodule/arm64-apple-ios.swiftinterface +31 -3
  71. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64/CrowdConnectedShared.framework/_CodeSignature/CodeResources +10 -10
  72. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedShared.framework/CrowdConnectedShared +0 -0
  73. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedShared.framework/Modules/CrowdConnectedShared.swiftmodule/arm64-apple-ios-simulator.abi.json +904 -235
  74. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedShared.framework/Modules/CrowdConnectedShared.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +33 -3
  75. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedShared.framework/Modules/CrowdConnectedShared.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  76. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedShared.framework/Modules/CrowdConnectedShared.swiftmodule/arm64-apple-ios-simulator.swiftinterface +31 -3
  77. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedShared.framework/Modules/CrowdConnectedShared.swiftmodule/x86_64-apple-ios-simulator.abi.json +904 -235
  78. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedShared.framework/Modules/CrowdConnectedShared.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +33 -3
  79. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedShared.framework/Modules/CrowdConnectedShared.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  80. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedShared.framework/Modules/CrowdConnectedShared.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +31 -3
  81. package/ios/Frameworks/CrowdConnectedShared.xcframework/ios-arm64_x86_64-simulator/CrowdConnectedShared.framework/_CodeSignature/CodeResources +20 -20
  82. package/ios/RCTCrowdConnectedLocationProvider.m +3 -0
  83. package/ios/RCTCrowdConnectedLocationProvider.swift +19 -1
  84. package/lib/module/crowdconnected-location-provider-native-module.js +30 -0
  85. package/lib/module/crowdconnected-location-provider-native-module.js.map +1 -1
  86. package/lib/typescript/src/crowdconnected-location-provider-native-module.d.ts.map +1 -1
  87. package/lib/typescript/src/types.d.ts +31 -2
  88. package/lib/typescript/src/types.d.ts.map +1 -1
  89. package/package.json +1 -9
  90. package/src/crowdconnected-location-provider-native-module.tsx +51 -0
  91. package/src/types.ts +33 -2
@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
10
10
  s.license = package["license"]
11
11
  s.authors = package["author"]
12
12
 
13
- s.platforms = { :ios => min_ios_version_supported }
13
+ s.platforms = { :ios => '16.0' }
14
14
  s.source = { :git => "https://github.com/expofp/react-native-crowdconnected.git", :tag => "#{s.version}" }
15
15
 
16
16
  s.source_files = "ios/**/*.{h,m,mm,cpp,swift}"
package/README.md CHANGED
@@ -19,7 +19,7 @@ React Native Turbo Module for CrowdConnected location tracking and indoor positi
19
19
  ## Requirements
20
20
 
21
21
  - Android 7.0+ (API level 24+)
22
- - iOS minimum version follows your React Native installation
22
+ - iOS 16.0+ (required by the bundled CrowdConnected SDK)
23
23
 
24
24
  ## Installation
25
25
 
@@ -59,8 +59,8 @@ import { CrowdConnectedLocationProvider } from '@expofp/react-native-efp-crowdco
59
59
  // Initialize
60
60
  await CrowdConnectedLocationProvider.setup({
61
61
  appKey: 'your-app-key',
62
- token: 'your-token',
63
- secret: 'your-secret',
62
+ clientId: 'your-client-id',
63
+ clientSecret: 'your-client-secret',
64
64
  navigationType: 'all', // 'all' | 'GEO' | 'IPS'
65
65
  isBackgroundUpdateEnabled: false,
66
66
  isBluetoothEnabled: true,
@@ -100,8 +100,8 @@ Initializes the CrowdConnected SDK with configuration.
100
100
 
101
101
  **Parameters:**
102
102
  - `appKey` - CrowdConnected application key
103
- - `token` - Authentication token
104
- - `secret` - Authentication secret
103
+ - `clientId` - CrowdConnected client ID
104
+ - `clientSecret` - CrowdConnected client secret
105
105
  - `navigationType` - Positioning type: `'all'` (GPS + IPS), `'GEO'` (GPS only), `'IPS'` (indoor only)
106
106
  - `isBackgroundUpdateEnabled` - Enable background location updates
107
107
  - `isBluetoothEnabled` - Enable Bluetooth-based positioning
@@ -156,6 +156,68 @@ Subscribes to location errors.
156
156
 
157
157
  **Returns:** Unsubscribe function
158
158
 
159
+ ## Simulation Mode
160
+
161
+ Simulation mode replays GPS/IPS paths on the device without physical movement. Simulated positions arrive through the regular `onLocationChange` listener.
162
+
163
+ ```typescript
164
+ const simulationToken = '<signed JWT for the simulation session>';
165
+
166
+ // The SDK must already be started (startUpdatingLocation) before activating.
167
+ const unsubConnected = CrowdConnectedLocationProvider.onSimulationConnected(() => {
168
+ console.log('Simulation connected');
169
+ });
170
+ const unsubDisconnected = CrowdConnectedLocationProvider.onSimulationDisconnected(() => {
171
+ console.log('Simulation disconnected');
172
+ });
173
+ const unsubError = CrowdConnectedLocationProvider.onSimulationError((error) => {
174
+ console.error('Simulation error:', error.message);
175
+ });
176
+
177
+ CrowdConnectedLocationProvider.activateSimulation(simulationToken);
178
+
179
+ // ...later
180
+ CrowdConnectedLocationProvider.deactivateSimulation();
181
+
182
+ // Cleanup
183
+ unsubConnected();
184
+ unsubDisconnected();
185
+ unsubError();
186
+ ```
187
+
188
+ ### `activateSimulation(token: string): void`
189
+
190
+ Starts a simulation session. Fire-and-forget: the outcome is reported via the `onSimulationConnected` / `onSimulationError` events.
191
+
192
+ - `token` - A signed JWT issued for the simulation session (contains the `sid` session ID and `exp` claims)
193
+ - The SDK must already be started via `startUpdatingLocation()`
194
+ - Calling it again replaces the current session
195
+ - Deactivate the simulation before calling `stopUpdatingLocation()` when a session is active
196
+
197
+ ---
198
+
199
+ ### `deactivateSimulation(): void`
200
+
201
+ Stops the active simulation session. Safe no-op when no session is active.
202
+
203
+ ---
204
+
205
+ ### `onSimulationConnected(callback: () => void): Unsubscribe`
206
+
207
+ Fires when the simulation session is established.
208
+
209
+ ---
210
+
211
+ ### `onSimulationDisconnected(callback: () => void): Unsubscribe`
212
+
213
+ Fires when the simulation session ends.
214
+
215
+ ---
216
+
217
+ ### `onSimulationError(callback: (error: Error) => void): Unsubscribe`
218
+
219
+ Fires when the simulation session fails (e.g. invalid or expired token).
220
+
159
221
  ## Development
160
222
 
161
223
  ### Setup
@@ -21,6 +21,16 @@ apply plugin: "kotlin-android"
21
21
 
22
22
  apply plugin: "com.facebook.react"
23
23
 
24
+ // This module is a legacy bridge (no TurboModule/Component spec), so package.json declares no
25
+ // `codegenConfig` — that keeps it out of iOS codegen discovery and out of New-Architecture C++
26
+ // autolinking (no dangling `react_codegen_*` target). Without `codegenConfig`, the library's
27
+ // codegen task would otherwise default its scan root to the whole package (`../`) and, in a
28
+ // workspace, aggregate other libraries' codegen (duplicate-class dex-merge failures). Pin the
29
+ // scan to `src` (which has no specs) so the generated schema stays empty.
30
+ react {
31
+ jsRootDir = file("../src")
32
+ }
33
+
24
34
  def getExtOrIntegerDefault(name) {
25
35
  return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["ExpofpCrowdconnected_" + name]).toInteger()
26
36
  }
@@ -80,8 +90,8 @@ dependencies {
80
90
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2"
81
91
  implementation "androidx.fragment:fragment-ktx:1.6.2"
82
92
 
83
- implementation "net.crowdconnected.android.core:android-core:3.0.0"
84
- implementation "net.crowdconnected.android.ips:android-ips:3.0.0"
85
- implementation "net.crowdconnected.android.geo:android-geo:3.0.0"
86
- implementation "net.crowdconnected.android.background:android-background:3.0.0"
93
+ implementation "net.crowdconnected.android.core:android-core:3.1.0"
94
+ implementation "net.crowdconnected.android.ips:android-ips:3.1.0"
95
+ implementation "net.crowdconnected.android.geo:android-geo:3.1.0"
96
+ implementation "net.crowdconnected.android.background:android-background:3.1.0"
87
97
  }
@@ -25,23 +25,22 @@ enum class ExpoFpCrowdConnectedNavigationType {
25
25
 
26
26
  /**
27
27
  * Settings for CrowdConnected location provider
28
- * Maps to ExpoFpLocationSettings TypeScript interface.
29
- *
30
- * Note: since CrowdConnected SDK v3.0.0, the authentication scheme changed.
31
- * The JS-facing fields `token` and `secret` are kept for API compatibility, but
32
- * internally they are passed to the v3 SDK as `clientId` and `clientSecret`
33
- * via `Configuration.builder().withCredentials(appKey, token, secret)`.
28
+ * Maps 1:1 to the ExpoFpLocationSettings TypeScript interface (src/types.ts);
29
+ * credentials are passed to the v3 SDK via
30
+ * `Configuration.builder().withCredentials(appKey, clientId, clientSecret)`.
34
31
  */
35
32
  data class ExpoFpCrowdConnectedLocationProviderSettings(
36
33
  val appKey: String,
37
- /** Passed to CrowdConnected v3 SDK as client ID (exposed as `token` in JS for backward compatibility). */
38
- val token: String,
39
- /** Passed to CrowdConnected v3 SDK as client secret (exposed as `secret` in JS for backward compatibility). */
40
- val secret: String,
34
+ /** CrowdConnected v3 client ID. */
35
+ val clientId: String,
36
+ /** CrowdConnected v3 client secret. */
37
+ val clientSecret: String,
41
38
  val navigationType: ExpoFpCrowdConnectedNavigationType = ExpoFpCrowdConnectedNavigationType.ALL,
42
39
  val isBackgroundUpdateEnabled: Boolean = false,
43
40
  val isBluetoothEnabled: Boolean = false,
44
41
  val isHeadingEnabled: Boolean = false,
42
+ /** When true, the SDK is switched to low-power GPS mode right after start. */
43
+ val isLowPowerModeEnabled: Boolean = false,
45
44
  val aliases: Map<String, String> = emptyMap()
46
45
  ) {
47
46
  /**
@@ -53,11 +52,11 @@ data class ExpoFpCrowdConnectedLocationProviderSettings(
53
52
  if (appKey.isBlank()) {
54
53
  throw IllegalArgumentException("appKey cannot be blank")
55
54
  }
56
- if (token.isBlank()) {
57
- throw IllegalArgumentException("token cannot be blank")
55
+ if (clientId.isBlank()) {
56
+ throw IllegalArgumentException("clientId cannot be blank")
58
57
  }
59
- if (secret.isBlank()) {
60
- throw IllegalArgumentException("secret cannot be blank")
58
+ if (clientSecret.isBlank()) {
59
+ throw IllegalArgumentException("clientSecret cannot be blank")
61
60
  }
62
61
 
63
62
  // Validate background requirements
@@ -86,12 +85,12 @@ data class ExpoFpCrowdConnectedLocationProviderSettings(
86
85
  val appKey = map.getString("appKey")
87
86
  ?.trim()
88
87
  ?: throw IllegalArgumentException("appKey is required")
89
- val token = map.getString("token")
88
+ val clientId = map.getString("clientId")
90
89
  ?.trim()
91
- ?: throw IllegalArgumentException("token is required")
92
- val secret = map.getString("secret")
90
+ ?: throw IllegalArgumentException("clientId is required")
91
+ val clientSecret = map.getString("clientSecret")
93
92
  ?.trim()
94
- ?: throw IllegalArgumentException("secret is required")
93
+ ?: throw IllegalArgumentException("clientSecret is required")
95
94
 
96
95
  // Optional fields
97
96
  val navigationType = ExpoFpCrowdConnectedNavigationType.fromString(
@@ -109,6 +108,10 @@ data class ExpoFpCrowdConnectedLocationProviderSettings(
109
108
  map.getBoolean("isHeadingEnabled")
110
109
  } else false
111
110
 
111
+ val isLowPowerModeEnabled = if (map.hasKey("isLowPowerModeEnabled")) {
112
+ map.getBoolean("isLowPowerModeEnabled")
113
+ } else false
114
+
112
115
  // Parse aliases map
113
116
  val aliases = if (map.hasKey("aliases")) {
114
117
  val aliasesMap = map.getMap("aliases")
@@ -125,12 +128,13 @@ data class ExpoFpCrowdConnectedLocationProviderSettings(
125
128
 
126
129
  val settings = ExpoFpCrowdConnectedLocationProviderSettings(
127
130
  appKey = appKey,
128
- token = token,
129
- secret = secret,
131
+ clientId = clientId,
132
+ clientSecret = clientSecret,
130
133
  navigationType = navigationType,
131
134
  isBackgroundUpdateEnabled = isBackgroundUpdateEnabled,
132
135
  isBluetoothEnabled = isBluetoothEnabled,
133
136
  isHeadingEnabled = isHeadingEnabled,
137
+ isLowPowerModeEnabled = isLowPowerModeEnabled,
134
138
  aliases = aliases
135
139
  )
136
140
 
@@ -32,6 +32,7 @@ import net.crowdconnected.android.core.Configuration
32
32
  import net.crowdconnected.android.core.CrowdConnected
33
33
  import net.crowdconnected.android.core.ErrorCode
34
34
  import net.crowdconnected.android.core.Location
35
+ import net.crowdconnected.android.core.SimulationCallback
35
36
  import net.crowdconnected.android.background.BackgroundModule
36
37
  import net.crowdconnected.android.geo.GeoModule
37
38
  import net.crowdconnected.android.ips.IPSModule
@@ -47,8 +48,10 @@ class PermissionDeniedException(
47
48
 
48
49
  /**
49
50
  * React Native module for CrowdConnected location provider
50
- * Provides cross-platform API: setup, startUpdatingLocation, stopUpdatingLocation, isLocationUpdating
51
- * Emits events: onLocationChange, onLocationError
51
+ * Provides cross-platform API: setup, startUpdatingLocation, stopUpdatingLocation,
52
+ * isLocationUpdating, activateSimulation, deactivateSimulation
53
+ * Emits events: onLocationChange, onLocationError,
54
+ * onSimulationConnected, onSimulationDisconnected, onSimulationError
52
55
  */
53
56
  class RCTCrowdConnectedLocationProviderModule(
54
57
  reactContext: ReactApplicationContext
@@ -59,6 +62,9 @@ class RCTCrowdConnectedLocationProviderModule(
59
62
  private const val TAG = "RCTCrowdConnected"
60
63
  private const val EVENT_LOCATION_CHANGE = "onLocationChange"
61
64
  private const val EVENT_LOCATION_ERROR = "onLocationError"
65
+ private const val EVENT_SIMULATION_CONNECTED = "onSimulationConnected"
66
+ private const val EVENT_SIMULATION_DISCONNECTED = "onSimulationDisconnected"
67
+ private const val EVENT_SIMULATION_ERROR = "onSimulationError"
62
68
  private const val TIMEOUT_DURATION_MS = 65_000L // 65 seconds timeout for SDK startup
63
69
 
64
70
  // Foreground service notification
@@ -140,6 +146,28 @@ class RCTCrowdConnectedLocationProviderModule(
140
146
  @Volatile private var azimuthProvider: ExpoFpAzimuthProvider? = null
141
147
  @Volatile private var isStarted = false
142
148
 
149
+ /**
150
+ * Receives simulation lifecycle events from the CrowdConnected SDK.
151
+ * The SDK may invoke these on a background thread, so each event hops to the
152
+ * main thread via moduleScope before emitting to JavaScript.
153
+ */
154
+ private val simulationCallback = object : SimulationCallback {
155
+ override fun onSimulationConnected() {
156
+ Log.d(TAG, "SimulationCallback.onSimulationConnected")
157
+ sendSimulationEvent(EVENT_SIMULATION_CONNECTED, null)
158
+ }
159
+
160
+ override fun onSimulationDisconnected() {
161
+ Log.d(TAG, "SimulationCallback.onSimulationDisconnected")
162
+ sendSimulationEvent(EVENT_SIMULATION_DISCONNECTED, null)
163
+ }
164
+
165
+ override fun onSimulationError(reason: String) {
166
+ Log.e(TAG, "SimulationCallback.onSimulationError: $reason")
167
+ sendSimulationEvent(EVENT_SIMULATION_ERROR, reason)
168
+ }
169
+ }
170
+
143
171
  override fun getName(): String = MODULE_NAME
144
172
 
145
173
  /**
@@ -252,6 +280,53 @@ class RCTCrowdConnectedLocationProviderModule(
252
280
  }
253
281
  }
254
282
 
283
+ /**
284
+ * Start a simulation session. The SDK must already be started
285
+ * (startUpdatingLocation); otherwise onSimulationError is emitted and the
286
+ * call is a no-op. Calling again replaces the current session.
287
+ * Fire-and-forget: results are reported via onSimulationConnected /
288
+ * onSimulationError events.
289
+ */
290
+ @ReactMethod
291
+ fun activateSimulation(token: String) {
292
+ Log.d(TAG, "activateSimulation")
293
+ // Without a running SDK instance the call is silently swallowed by the
294
+ // SDK — surface it to JS instead.
295
+ if (CrowdConnected.getInstance() == null) {
296
+ sendSimulationEvent(EVENT_SIMULATION_ERROR, "CrowdConnected is not running")
297
+ return
298
+ }
299
+ // runCatching is a crash barrier only: an uncaught exception in a
300
+ // @ReactMethod takes down the app; failures surface as onSimulationError.
301
+ runCatching { CrowdConnected.activateSimulation(token, simulationCallback) }
302
+ .onFailure {
303
+ Log.e(TAG, "activateSimulation failed", it)
304
+ sendSimulationEvent(EVENT_SIMULATION_ERROR, it.message ?: "Failed to activate simulation")
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Stop the active simulation session. Safe no-op when none is active.
310
+ */
311
+ @ReactMethod
312
+ fun deactivateSimulation() {
313
+ Log.d(TAG, "deactivateSimulation")
314
+ runCatching { CrowdConnected.deactivateSimulation() }
315
+ .onFailure { Log.e(TAG, "deactivateSimulation failed", it) }
316
+ }
317
+
318
+ /**
319
+ * Manually toggle low-power GPS mode at runtime: true drops to low-frequency,
320
+ * battery-saving mode; false restores high-frequency navigation mode.
321
+ * Safe no-op when the SDK is not currently running (getInstance() == null).
322
+ */
323
+ @ReactMethod
324
+ fun setLowPowerMode(enabled: Boolean) {
325
+ Log.d(TAG, "setLowPowerMode: $enabled")
326
+ runCatching { CrowdConnected.getInstance()?.setLowPowerMode(enabled) }
327
+ .onFailure { Log.e(TAG, "setLowPowerMode failed", it) }
328
+ }
329
+
255
330
  /**
256
331
  * Ensures all required permissions are granted, requesting them from the user if necessary.
257
332
  * Must be called OUTSIDE the mutex to avoid blocking other module operations
@@ -423,6 +498,15 @@ class RCTCrowdConnectedLocationProviderModule(
423
498
  }
424
499
  }
425
500
  }
501
+
502
+ // Force low-power mode on start when requested. Only act when enabled —
503
+ // when false we leave the SDK's automatic frequency management untouched.
504
+ // Reachable here for the same reason as setAlias: getInstance() is null before start.
505
+ if (currentSettings.isLowPowerModeEnabled) {
506
+ Log.d(TAG, "Applying low-power mode on start")
507
+ runCatching { CrowdConnected.getInstance()?.setLowPowerMode(true) }
508
+ .onFailure { Log.w(TAG, "setLowPowerMode(startup) failed: ${it.message}") }
509
+ }
426
510
  }
427
511
 
428
512
  override fun onStartFailure(errorCode: ErrorCode) {
@@ -462,12 +546,10 @@ class RCTCrowdConnectedLocationProviderModule(
462
546
  val app = reactApplicationContext.applicationContext as Application
463
547
  val builder = Configuration.builder()
464
548
  .withContext(app)
465
- // v3 SDK credentials — JS-level `token`/`secret` fields are passed through
466
- // as the SDK's clientId/clientSecret for backward-compatible API.
467
549
  .withCredentials(
468
550
  currentSettings.appKey,
469
- currentSettings.token,
470
- currentSettings.secret
551
+ currentSettings.clientId,
552
+ currentSettings.clientSecret
471
553
  )
472
554
  .withCallback(callback)
473
555
 
@@ -533,6 +615,12 @@ class RCTCrowdConnectedLocationProviderModule(
533
615
  isStarted = false
534
616
  providerInfo = providerInfo.copy(isLocationUpdating = false)
535
617
 
618
+ // End any active simulation session before stopping: the SDK retains
619
+ // simulation callbacks for the lifetime of the session even across
620
+ // stop(). Safe no-op when no session is active.
621
+ runCatching { CrowdConnected.deactivateSimulation() }
622
+ .onFailure { Log.w(TAG, "stopUpdatingLocationInternal: deactivateSimulation failed", it) }
623
+
536
624
  // Stop CrowdConnected SDK (v3: static, idempotent — wrap for safety)
537
625
  Log.d(TAG, "stopUpdatingLocationInternal: Stopping CrowdConnected SDK")
538
626
  runCatching { CrowdConnected.stop() }
@@ -584,6 +672,24 @@ class RCTCrowdConnectedLocationProviderModule(
584
672
  }
585
673
  }
586
674
 
675
+ /**
676
+ * Send a simulation lifecycle event to JavaScript.
677
+ * Body is null for connected/disconnected and the raw reason string for
678
+ * errors. Not routed through sendErrorEvent, which appends a Device ID
679
+ * suffix and emits onLocationError.
680
+ */
681
+ private fun sendSimulationEvent(eventName: String, body: String?) {
682
+ moduleScope.launch {
683
+ try {
684
+ reactApplicationContext
685
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
686
+ ?.emit(eventName, body)
687
+ } catch (e: Exception) {
688
+ Log.e(TAG, "Failed to send $eventName event to JavaScript", e)
689
+ }
690
+ }
691
+ }
692
+
587
693
  /**
588
694
  * Start timeout job that will stop SDK if it doesn't respond in time
589
695
  * Uses 65 second timeout for SDK startup
@@ -34,10 +34,10 @@ struct ExpoFpCrowdConnectedLocationProviderSettings {
34
34
  let appKey = settings.appKey.replacingOccurrences(of: " ", with: "")
35
35
  if appKey.isEmpty { throw ExpoFpError.locationProviderError(message: Self.message(for: .missingAppKey)) }
36
36
 
37
- let clientId = settings.token.replacingOccurrences(of: " ", with: "")
37
+ let clientId = settings.clientId.replacingOccurrences(of: " ", with: "")
38
38
  if clientId.isEmpty { throw ExpoFpError.locationProviderError(message: Self.message(for: .missingToken)) }
39
39
 
40
- let clientSecret = settings.secret.replacingOccurrences(of: " ", with: "")
40
+ let clientSecret = settings.clientSecret.replacingOccurrences(of: " ", with: "")
41
41
  if clientSecret.isEmpty { throw ExpoFpError.locationProviderError(message: Self.message(for: .missingSecret)) }
42
42
 
43
43
  self.credentials = Credentials(appKey: appKey, clientId: clientId, clientSecret: clientSecret)
@@ -8,8 +8,8 @@
8
8
 
9
9
  struct ExpoFpLocationSettings: Codable {
10
10
  let appKey: String
11
- let token: String
12
- let secret: String
11
+ let clientId: String
12
+ let clientSecret: String
13
13
  let navigationType: ExpoFpCrowdConnectedNavigationType
14
14
  let isBackgroundUpdateEnabled: Bool
15
15
  let isBluetoothEnabled: Bool
@@ -0,0 +1,40 @@
1
+ //
2
+ // ExpoFpSimulationDelegate.swift
3
+ // ExpofpCrowdconnected
4
+ //
5
+ // Created by Nikita Kolesnikov on 09.07.2026.
6
+ //
7
+
8
+ import CrowdConnectedCore
9
+ import Foundation
10
+
11
+ /// Receives simulation lifecycle events from the CrowdConnected SDK.
12
+ ///
13
+ /// The SDK invokes these callbacks on a background thread, so every event is
14
+ /// marshalled onto the main actor before touching the (`@MainActor`) provider
15
+ /// and emitting a React Native event.
16
+ final class ExpoFpSimulationDelegate: SimulationCallback {
17
+
18
+ weak var provider: RCTCrowdConnectedLocationProvider?
19
+
20
+ func onSimulationConnected() {
21
+ Task { @MainActor in
22
+ guard let provider else { return }
23
+ provider.sendEvent(withName: provider.onSimulationConnectedName, body: nil)
24
+ }
25
+ }
26
+
27
+ func onSimulationDisconnected() {
28
+ Task { @MainActor in
29
+ guard let provider else { return }
30
+ provider.sendEvent(withName: provider.onSimulationDisconnectedName, body: nil)
31
+ }
32
+ }
33
+
34
+ func onSimulationError(reason: String) {
35
+ Task { @MainActor in
36
+ guard let provider else { return }
37
+ provider.sendEvent(withName: provider.onSimulationErrorName, body: reason)
38
+ }
39
+ }
40
+ }
@@ -631,6 +631,35 @@ SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, strong) CrowdConnect
631
631
  - (Configuration * _Nullable)getCurrentConfiguration SWIFT_WARN_UNUSED_RESULT;
632
632
  @end
633
633
 
634
+ @protocol SimulationCallback;
635
+ @interface CrowdConnected (SWIFT_EXTENSION(CrowdConnectedCore))
636
+ /// Activates a simulation session using the provided JWT token.
637
+ /// The SDK must already be started before calling this method.
638
+ /// A second call replaces any existing session (the previous one is deactivated first).
639
+ /// since:
640
+ /// 3.1.0
641
+ /// \param token A signed JWT containing the <code>sid</code> (session ID) and <code>exp</code> claims.
642
+ ///
643
+ - (void)activateSimulationWithToken:(NSString * _Nonnull)token;
644
+ /// Activates a simulation session with an optional callback for session events.
645
+ /// The SDK must already be started before calling this method.
646
+ /// A second call replaces any existing session (the previous one is deactivated first).
647
+ /// All callbacks fire on a background thread — marshal to the main thread if you need to update UI.
648
+ /// since:
649
+ /// 3.1.0
650
+ /// \param token A signed JWT containing the <code>sid</code> (session ID) and <code>exp</code> claims.
651
+ ///
652
+ /// \param callback An optional object that receives connection, error, and disconnection events.
653
+ ///
654
+ - (void)activateSimulationWithToken:(NSString * _Nonnull)token callback:(id <SimulationCallback> _Nullable)callback;
655
+ /// Stops any active simulation session.
656
+ /// Safe to call at any time, including when no session is active.
657
+ /// <code>onSimulationDisconnected</code> is <em>not</em> called when you stop the session explicitly.
658
+ /// since:
659
+ /// 3.1.0
660
+ - (void)deactivateSimulation;
661
+ @end
662
+
634
663
  enum ErrorCode : NSInteger;
635
664
  /// A delegate protocol for receiving callbacks from the CrowdConnected SDK.
636
665
  /// Implement this protocol to receive location updates, success notifications, and error callbacks from the SDK.
@@ -753,6 +782,25 @@ typedef SWIFT_ENUM(NSInteger, Module, open) {
753
782
  ModuleCoreBluetooth = 2,
754
783
  };
755
784
 
785
+ /// Receives lifecycle events from an active simulation session.
786
+ /// All methods are called on a background thread.
787
+ /// Marshal to the main thread if you need to update UI.
788
+ /// since:
789
+ /// 3.1.0
790
+ SWIFT_PROTOCOL("_TtP18CrowdConnectedCore18SimulationCallback_")
791
+ @protocol SimulationCallback
792
+ /// The AppSync subscription is live and simulation events will begin flowing.
793
+ - (void)onSimulationConnected;
794
+ /// A connection or subscription problem occurred (network error, expired token, invalid session, etc.).
795
+ /// \param reason A human-readable description of the failure.
796
+ ///
797
+ - (void)onSimulationErrorWithReason:(NSString * _Nonnull)reason;
798
+ /// The session ended — either the server sent <code>SESSION_ENDED</code>, the subscription completed,
799
+ /// or the WebSocket closed unexpectedly.
800
+ /// Not called when you explicitly call <code>deactivateSimulation()</code>.
801
+ - (void)onSimulationDisconnected;
802
+ @end
803
+
756
804
  #endif
757
805
  #if __has_attribute(external_source_symbol)
758
806
  # pragma clang attribute pop