@mentra/bluetooth-sdk 3.2.0-dev.200 → 3.2.0-dev.203

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 (50) hide show
  1. package/README.md +46 -45
  2. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +5 -1
  3. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +63 -0
  4. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +5 -4
  5. package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedChangelogCatalog.kt +1 -1
  6. package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +5 -5
  7. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +285 -29
  8. package/android/src/main/java/com/mentra/bluetoothsdk/MessageAckPolicy.kt +4 -0
  9. package/android/src/main/java/com/mentra/bluetoothsdk/WifiRequestResolution.kt +246 -0
  10. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ClassicAudioConnectionTracker.kt +104 -0
  11. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +312 -45
  12. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +2 -0
  13. package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +14 -0
  14. package/android/src/test/java/com/mentra/bluetoothsdk/GlassesStatusClassicAudioTest.kt +14 -0
  15. package/android/src/test/java/com/mentra/bluetoothsdk/MessageAckPolicyTest.kt +16 -0
  16. package/android/src/test/java/com/mentra/bluetoothsdk/WifiRequestResolutionTest.kt +271 -0
  17. package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/ClassicAudioConnectionTrackerTest.kt +131 -0
  18. package/build/BluetoothSdk.types.d.ts +62 -1
  19. package/build/BluetoothSdk.types.d.ts.map +1 -1
  20. package/build/BluetoothSdk.types.js.map +1 -1
  21. package/build/_private/BluetoothSdkModule.d.ts +3 -2
  22. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  23. package/build/_private/BluetoothSdkModule.js.map +1 -1
  24. package/build/generated/changelogCatalog.d.ts +1 -1
  25. package/build/generated/changelogCatalog.js +1 -1
  26. package/build/generated/changelogCatalog.js.map +1 -1
  27. package/build/generated/releaseMetadata.js +5 -5
  28. package/build/generated/releaseMetadata.js.map +1 -1
  29. package/build/index.d.ts +1 -1
  30. package/build/index.d.ts.map +1 -1
  31. package/build/index.js +3 -0
  32. package/build/index.js.map +1 -1
  33. package/ios/BluetoothSdkModule.swift +8 -2
  34. package/ios/Source/BluetoothSdkDefaults.swift +1 -1
  35. package/ios/Source/Bridge.swift +63 -0
  36. package/ios/Source/DeviceManager.swift +6 -3
  37. package/ios/Source/GeneratedChangelogCatalog.swift +1 -1
  38. package/ios/Source/GeneratedReleaseMetadata.swift +5 -5
  39. package/ios/Source/MentraBluetoothSDK.swift +365 -93
  40. package/ios/Source/WifiRequestResolution.swift +251 -0
  41. package/ios/Source/sgcs/MentraLive.swift +170 -35
  42. package/ios/Source/sgcs/SGCManager.swift +13 -1
  43. package/ios/Source/status/DeviceStatus.swift +28 -0
  44. package/ios/Tests/WifiRequestResolutionTests.swift +320 -0
  45. package/package.json +1 -1
  46. package/src/BluetoothSdk.types.ts +76 -1
  47. package/src/_private/BluetoothSdkModule.ts +4 -1
  48. package/src/generated/changelogCatalog.ts +1 -1
  49. package/src/generated/releaseMetadata.ts +5 -5
  50. package/src/index.ts +11 -0
@@ -0,0 +1,104 @@
1
+ package com.mentra.bluetoothsdk.sgcs
2
+
3
+ internal enum class ClassicAudioProfile {
4
+ A2DP,
5
+ HEADSET,
6
+ }
7
+
8
+ /** A confirmed connection is sufficient; declaring disconnection requires both profile reads. */
9
+ internal fun readyClassicAudioSnapshot(states: Map<ClassicAudioProfile, Boolean>): Set<ClassicAudioProfile>? {
10
+ val connected = states.filterValues { it }.keys
11
+ return if (connected.isNotEmpty() || states.size == ClassicAudioProfile.entries.size) connected else null
12
+ }
13
+
14
+ /**
15
+ * Tracks the Android Classic audio profiles for one dual-mode glasses device.
16
+ *
17
+ * Android reports A2DP and HFP independently. Keeping both states prevents an A2DP disconnect from
18
+ * publishing a false negative while HFP is still connected. Snapshot tickets reject obsolete
19
+ * queries, including a previous session with the same MAC address. Broadcast payloads are not truth.
20
+ */
21
+ internal class ClassicAudioConnectionTracker(
22
+ private val onConnectedChanged: (Boolean) -> Unit,
23
+ ) {
24
+ private var targetAddress: String? = null
25
+ private var revision = 0L
26
+ private val connectedProfiles = mutableSetOf<ClassicAudioProfile>()
27
+
28
+ @get:Synchronized
29
+ val connected: Boolean
30
+ get() = connectedProfiles.isNotEmpty()
31
+
32
+ @Synchronized
33
+ fun setTarget(address: String) {
34
+ val normalizedAddress = address.normalizedBluetoothAddress()
35
+ revision++
36
+ if (targetAddress == normalizedAddress) return
37
+
38
+ targetAddress = normalizedAddress
39
+ clearConnectedProfiles()
40
+ }
41
+
42
+ @Synchronized
43
+ fun beginSnapshot(address: String): Long? {
44
+ if (targetAddress != address.normalizedBluetoothAddress()) return null
45
+ return ++revision
46
+ }
47
+
48
+ /** Both profile queries must belong to the newest request for this session. */
49
+ @Synchronized
50
+ fun applySnapshot(
51
+ ticket: Long,
52
+ address: String,
53
+ profiles: Set<ClassicAudioProfile>,
54
+ onAccepted: () -> Unit = {},
55
+ ): Boolean {
56
+ if (ticket != revision || targetAddress != address.normalizedBluetoothAddress()) return false
57
+ val wasConnected = this.connected
58
+ connectedProfiles.clear()
59
+ connectedProfiles.addAll(profiles)
60
+ publishIfChanged(wasConnected)
61
+ // Audio routing notifications must not race a target invalidation after validation.
62
+ onAccepted()
63
+ return true
64
+ }
65
+
66
+ @Synchronized
67
+ fun clear(address: String): Boolean {
68
+ if (targetAddress != address.normalizedBluetoothAddress()) return false
69
+ revision++
70
+ clearConnectedProfiles()
71
+ return true
72
+ }
73
+
74
+ @Synchronized
75
+ fun invalidate(address: String): Boolean {
76
+ if (targetAddress != address.normalizedBluetoothAddress()) return false
77
+
78
+ targetAddress = null
79
+ revision++
80
+ clearConnectedProfiles()
81
+ return true
82
+ }
83
+
84
+ @Synchronized
85
+ fun reset() {
86
+ targetAddress = null
87
+ revision++
88
+ clearConnectedProfiles()
89
+ }
90
+
91
+ private fun clearConnectedProfiles() {
92
+ val wasConnected = connected
93
+ connectedProfiles.clear()
94
+ publishIfChanged(wasConnected)
95
+ }
96
+
97
+ private fun publishIfChanged(wasConnected: Boolean) {
98
+ if (wasConnected != connected) {
99
+ onConnectedChanged(connected)
100
+ }
101
+ }
102
+ }
103
+
104
+ private fun String.normalizedBluetoothAddress(): String = trim().uppercase()