@mentra/bluetooth-sdk 0.1.13 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +48 -48
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +5 -20
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +360 -269
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +3 -7
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +152 -72
- package/android/src/main/java/com/mentra/bluetoothsdk/OtaManifest.kt +12 -12
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +25 -11
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +1 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +1 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +1 -22
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +51 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +1184 -800
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.kt +1 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +423 -117
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +4 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Nimo.kt +2532 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +17 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +3 -7
- package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +0 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +16 -33
- package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +5 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +2 -1
- package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +60 -1
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
- package/build/BluetoothSdk.types.d.ts +50 -24
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js +1 -0
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_internal.d.ts +4 -4
- package/build/_internal.js +4 -4
- package/build/_internal.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +14 -8
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +4 -3
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/cameraRequestPayload.d.ts +4 -0
- package/build/_private/cameraRequestPayload.d.ts.map +1 -0
- package/build/_private/cameraRequestPayload.js +25 -0
- package/build/_private/cameraRequestPayload.js.map +1 -0
- package/build/_private/photoRequestPayload.d.ts.map +1 -1
- package/build/_private/photoRequestPayload.js +11 -3
- package/build/_private/photoRequestPayload.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +70 -61
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +51 -28
- package/ios/Source/Bridge.swift +4 -16
- package/ios/Source/DeviceManager.swift +76 -22
- package/ios/Source/DeviceStore.swift +0 -4
- package/ios/Source/MentraBluetoothSDK.swift +144 -78
- package/ios/Source/OtaManifest.swift +13 -13
- package/ios/Source/camera/CameraModels.swift +82 -20
- package/ios/Source/controllers/ControllerManager.swift +1 -2
- package/ios/Source/controllers/R1.swift +1 -2
- package/ios/Source/errors/{BluetoothError.swift → BluetoothSdkError.swift} +1 -1
- package/ios/Source/events/BluetoothEvents.swift +5 -27
- package/ios/Source/internal/BluetoothAvailability.swift +5 -5
- package/ios/Source/sgcs/Frame.swift +0 -2
- package/ios/Source/sgcs/G1.swift +52 -4
- package/ios/Source/sgcs/G2.swift +643 -204
- package/ios/Source/sgcs/Mach1.swift +1 -3
- package/ios/Source/sgcs/MentraLive.swift +64 -77
- package/ios/Source/sgcs/MentraNex.swift +1 -3
- package/ios/Source/sgcs/Nimo.swift +1928 -0
- package/ios/Source/sgcs/SGCManager.swift +16 -10
- package/ios/Source/sgcs/Simulated.swift +2 -6
- package/ios/Source/status/DeviceStatus.swift +0 -8
- package/ios/Source/streaming/StreamModels.swift +7 -32
- package/ios/Source/stt/STTTools.swift +1 -1
- package/ios/Source/types/DeviceModels.swift +5 -0
- package/ios/Source/utils/Constants.swift +2 -0
- package/package.json +6 -1
- package/src/BluetoothSdk.types.ts +54 -28
- package/src/_internal.ts +4 -4
- package/src/_private/BluetoothSdkModule.ts +20 -11
- package/src/_private/cameraRequestPayload.ts +29 -0
- package/src/_private/photoRequestPayload.ts +12 -3
- package/src/index.ts +77 -67
|
@@ -20,7 +20,7 @@ abstract class SGCManager {
|
|
|
20
20
|
abstract fun startStream(message: MutableMap<String, Any>)
|
|
21
21
|
abstract fun stopStream()
|
|
22
22
|
abstract fun sendStreamKeepAlive(message: MutableMap<String, Any>)
|
|
23
|
-
abstract fun startVideoRecording(requestId: String, save: Boolean,
|
|
23
|
+
abstract fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean)
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Start video recording with optional per-recording resolution/fps. A width,
|
|
@@ -32,14 +32,13 @@ abstract class SGCManager {
|
|
|
32
32
|
open fun startVideoRecording(
|
|
33
33
|
requestId: String,
|
|
34
34
|
save: Boolean,
|
|
35
|
-
flash: Boolean,
|
|
36
35
|
sound: Boolean,
|
|
37
36
|
width: Int,
|
|
38
37
|
height: Int,
|
|
39
38
|
fps: Int,
|
|
40
39
|
maxRecordingTimeMinutes: Int,
|
|
41
40
|
) {
|
|
42
|
-
startVideoRecording(requestId, save,
|
|
41
|
+
startVideoRecording(requestId, save, sound)
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
abstract fun stopVideoRecording(requestId: String)
|
|
@@ -59,7 +58,6 @@ abstract class SGCManager {
|
|
|
59
58
|
abstract fun sendButtonPhotoSettings()
|
|
60
59
|
abstract fun sendButtonVideoRecordingSettings()
|
|
61
60
|
abstract fun sendButtonMaxRecordingTime()
|
|
62
|
-
abstract fun sendButtonCameraLedSetting()
|
|
63
61
|
abstract fun sendCameraFovSetting()
|
|
64
62
|
|
|
65
63
|
// Display Control
|
|
@@ -79,6 +77,20 @@ abstract class SGCManager {
|
|
|
79
77
|
width: Int? = null,
|
|
80
78
|
height: Int? = null
|
|
81
79
|
): Boolean
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Show text in a positioned container with an optional rounded border.
|
|
83
|
+
* G2-only capability; default no-op so other glasses ignore it.
|
|
84
|
+
*/
|
|
85
|
+
open fun sendPositionedText(
|
|
86
|
+
text: String,
|
|
87
|
+
x: Int,
|
|
88
|
+
y: Int,
|
|
89
|
+
width: Int,
|
|
90
|
+
height: Int,
|
|
91
|
+
borderWidth: Int = 0,
|
|
92
|
+
borderRadius: Int = 0
|
|
93
|
+
) {}
|
|
82
94
|
abstract fun showDashboard()
|
|
83
95
|
abstract fun setDashboardPosition(height: Int, depth: Int)
|
|
84
96
|
|
|
@@ -173,7 +185,7 @@ abstract class SGCManager {
|
|
|
173
185
|
// Voice Activity Detection
|
|
174
186
|
open fun sendVoiceActivityDetectionSetting() {}
|
|
175
187
|
|
|
176
|
-
// Start/stop LC3 audio playback from glasses based on the
|
|
188
|
+
// Start/stop LC3 audio playback from glasses based on the nex_lc3_audio_playback flag.
|
|
177
189
|
open fun applyNexAudioPlaybackSetting() {}
|
|
178
190
|
|
|
179
191
|
// Version info
|
|
@@ -34,7 +34,7 @@ class Simulated : SGCManager() {
|
|
|
34
34
|
|
|
35
35
|
// Camera & Media
|
|
36
36
|
override fun requestPhoto(request: PhotoRequest) {
|
|
37
|
-
Bridge.log("requestPhoto
|
|
37
|
+
Bridge.log("requestPhoto save=${request.save}, sound=${request.sound}")
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
override fun startStream(message: MutableMap<String, Any>) {
|
|
@@ -49,8 +49,8 @@ class Simulated : SGCManager() {
|
|
|
49
49
|
Bridge.log("sendStreamKeepAlive")
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
override fun startVideoRecording(requestId: String, save: Boolean,
|
|
53
|
-
Bridge.log("startVideoRecording
|
|
52
|
+
override fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean) {
|
|
53
|
+
Bridge.log("startVideoRecording sound=$sound")
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
override fun stopVideoRecording(requestId: String) {
|
|
@@ -70,10 +70,6 @@ class Simulated : SGCManager() {
|
|
|
70
70
|
Bridge.log("sendButtonMaxRecordingTime")
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
override fun sendButtonCameraLedSetting() {
|
|
74
|
-
Bridge.log("sendButtonCameraLedSetting")
|
|
75
|
-
}
|
|
76
|
-
|
|
77
73
|
override fun sendCameraFovSetting() {
|
|
78
74
|
Bridge.log("sendCameraFovSetting")
|
|
79
75
|
}
|
|
@@ -248,7 +248,6 @@ internal data class BluetoothStatus(
|
|
|
248
248
|
val contextualDashboard: Boolean,
|
|
249
249
|
val galleryModeEnabled: Boolean,
|
|
250
250
|
val buttonPhotoSize: ButtonPhotoSize,
|
|
251
|
-
val buttonCameraLed: Boolean,
|
|
252
251
|
val buttonMaxRecordingTime: Int,
|
|
253
252
|
val buttonVideoWidth: Int,
|
|
254
253
|
val buttonVideoHeight: Int,
|
|
@@ -301,7 +300,6 @@ internal data class BluetoothStatus(
|
|
|
301
300
|
"contextual_dashboard" to contextualDashboard,
|
|
302
301
|
"galleryModeEnabled" to galleryModeEnabled,
|
|
303
302
|
"button_photo_size" to buttonPhotoSize.value,
|
|
304
|
-
"button_camera_led" to buttonCameraLed,
|
|
305
303
|
"button_max_recording_time" to buttonMaxRecordingTime,
|
|
306
304
|
"button_video_width" to buttonVideoWidth,
|
|
307
305
|
"button_video_height" to buttonVideoHeight,
|
|
@@ -349,7 +347,6 @@ internal data class BluetoothStatus(
|
|
|
349
347
|
galleryModeEnabled =
|
|
350
348
|
boolValue(values, "gallery_mode") ?: boolValue(values, "galleryModeEnabled") ?: true,
|
|
351
349
|
buttonPhotoSize = ButtonPhotoSize.fromValue(stringValue(values, "button_photo_size")),
|
|
352
|
-
buttonCameraLed = boolValue(values, "button_camera_led") ?: true,
|
|
353
350
|
buttonMaxRecordingTime = numberValue(values, "button_max_recording_time") ?: 10,
|
|
354
351
|
buttonVideoWidth = numberValue(values, "button_video_width") ?: 1280,
|
|
355
352
|
buttonVideoHeight = numberValue(values, "button_video_height") ?: 720,
|
|
@@ -554,7 +551,6 @@ internal data class BluetoothStatusUpdate(
|
|
|
554
551
|
val contextualDashboard: Boolean? = null,
|
|
555
552
|
val galleryModeEnabled: Boolean? = null,
|
|
556
553
|
val buttonPhotoSize: ButtonPhotoSize? = null,
|
|
557
|
-
val buttonCameraLed: Boolean? = null,
|
|
558
554
|
val buttonMaxRecordingTime: Int? = null,
|
|
559
555
|
val buttonVideoWidth: Int? = null,
|
|
560
556
|
val buttonVideoHeight: Int? = null,
|
|
@@ -597,7 +593,6 @@ internal data class BluetoothStatusUpdate(
|
|
|
597
593
|
putIfNotNull("contextual_dashboard", contextualDashboard)
|
|
598
594
|
putIfNotNull("galleryModeEnabled", galleryModeEnabled)
|
|
599
595
|
buttonPhotoSize?.let { put("button_photo_size", it.value) }
|
|
600
|
-
putIfNotNull("button_camera_led", buttonCameraLed)
|
|
601
596
|
putIfNotNull("button_max_recording_time", buttonMaxRecordingTime)
|
|
602
597
|
putIfNotNull("button_video_width", buttonVideoWidth)
|
|
603
598
|
putIfNotNull("button_video_height", buttonVideoHeight)
|
|
@@ -648,7 +643,6 @@ internal data class BluetoothStatusUpdate(
|
|
|
648
643
|
optionalBoolValue(values, "gallery_mode") ?: optionalBoolValue(values, "galleryModeEnabled"),
|
|
649
644
|
buttonPhotoSize =
|
|
650
645
|
optionalStringValue(values, "button_photo_size")?.let(ButtonPhotoSize::fromValue),
|
|
651
|
-
buttonCameraLed = optionalBoolValue(values, "button_camera_led"),
|
|
652
646
|
buttonMaxRecordingTime = optionalNumberValue(values, "button_max_recording_time"),
|
|
653
647
|
buttonVideoWidth = optionalNumberValue(values, "button_video_width"),
|
|
654
648
|
buttonVideoHeight = optionalNumberValue(values, "button_video_height"),
|
|
@@ -11,6 +11,7 @@ data class StreamVideoConfig @JvmOverloads constructor(
|
|
|
11
11
|
width?.let { "width" to it },
|
|
12
12
|
height?.let { "height" to it },
|
|
13
13
|
bitrate?.let { "bitrate" to it },
|
|
14
|
+
// ASG stream parsers shipped with the BLE key named "frameRate".
|
|
14
15
|
fps?.let { "frameRate" to it },
|
|
15
16
|
).toMap()
|
|
16
17
|
|
|
@@ -170,28 +171,19 @@ data class StreamResolvedConfig @JvmOverloads constructor(
|
|
|
170
171
|
data class StreamRequest @JvmOverloads constructor(
|
|
171
172
|
val streamUrl: String,
|
|
172
173
|
val streamId: String = "",
|
|
173
|
-
val keepAlive: Boolean = true,
|
|
174
|
-
val keepAliveIntervalSeconds: Int = 5,
|
|
175
174
|
val sound: Boolean = true,
|
|
176
175
|
val video: StreamVideoConfig? = null,
|
|
177
176
|
val audio: StreamAudioConfig? = null,
|
|
178
|
-
val extraValues: Map<String, Any> = emptyMap(),
|
|
179
177
|
) {
|
|
180
|
-
fun toMap(): Map<String, Any>
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
values["flash"] = true
|
|
190
|
-
values["sound"] = sound
|
|
191
|
-
video?.toMap()?.takeIf { it.isNotEmpty() }?.let { values["video"] = it }
|
|
192
|
-
audio?.toMap()?.takeIf { it.isNotEmpty() }?.let { values["audio"] = it }
|
|
193
|
-
return values
|
|
194
|
-
}
|
|
178
|
+
fun toMap(): Map<String, Any> =
|
|
179
|
+
buildMap {
|
|
180
|
+
put("type", "start_stream")
|
|
181
|
+
put("streamUrl", streamUrl)
|
|
182
|
+
put("streamId", streamId)
|
|
183
|
+
put("sound", sound)
|
|
184
|
+
video?.toMap()?.takeIf { it.isNotEmpty() }?.let { put("video", it) }
|
|
185
|
+
audio?.toMap()?.takeIf { it.isNotEmpty() }?.let { put("audio", it) }
|
|
186
|
+
}
|
|
195
187
|
|
|
196
188
|
companion object {
|
|
197
189
|
@JvmStatic
|
|
@@ -201,31 +193,23 @@ data class StreamRequest @JvmOverloads constructor(
|
|
|
201
193
|
(values["streamUrl"] ?: values["rtmpUrl"] ?: values["srtUrl"] ?: values["whipUrl"]) as? String
|
|
202
194
|
?: "",
|
|
203
195
|
streamId = values["streamId"] as? String ?: "",
|
|
204
|
-
keepAlive = values["keepAlive"] as? Boolean ?: true,
|
|
205
|
-
keepAliveIntervalSeconds = (values["keepAliveIntervalSeconds"] as? Number)?.toInt() ?: 5,
|
|
206
196
|
sound = values["sound"] as? Boolean ?: true,
|
|
207
197
|
video = StreamVideoConfig.fromMap(stringMapValue(values["video"])),
|
|
208
198
|
audio = StreamAudioConfig.fromMap(stringMapValue(values["audio"])),
|
|
209
|
-
extraValues = values,
|
|
210
199
|
)
|
|
211
200
|
}
|
|
212
201
|
}
|
|
213
202
|
|
|
214
|
-
internal fun StreamRequest.isExternallyManagedKeepAlive(): Boolean =
|
|
215
|
-
stringValue(extraValues, "keepAliveMode") == "external"
|
|
216
|
-
|
|
217
203
|
internal data class StreamKeepAliveRequest @JvmOverloads constructor(
|
|
218
204
|
val streamId: String,
|
|
219
205
|
val ackId: String,
|
|
220
|
-
val extraValues: Map<String, Any> = emptyMap(),
|
|
221
206
|
) {
|
|
222
|
-
fun toMap(): Map<String, Any>
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
207
|
+
fun toMap(): Map<String, Any> =
|
|
208
|
+
mapOf(
|
|
209
|
+
"type" to "keep_stream_alive",
|
|
210
|
+
"streamId" to streamId,
|
|
211
|
+
"ackId" to ackId,
|
|
212
|
+
)
|
|
229
213
|
|
|
230
214
|
companion object {
|
|
231
215
|
@JvmStatic
|
|
@@ -233,7 +217,6 @@ internal data class StreamKeepAliveRequest @JvmOverloads constructor(
|
|
|
233
217
|
StreamKeepAliveRequest(
|
|
234
218
|
streamId = values["streamId"] as? String ?: "",
|
|
235
219
|
ackId = values["ackId"] as? String ?: "",
|
|
236
|
-
extraValues = values,
|
|
237
220
|
)
|
|
238
221
|
}
|
|
239
222
|
}
|
|
@@ -2,17 +2,18 @@ package com.mentra.bluetoothsdk
|
|
|
2
2
|
|
|
3
3
|
import com.mentra.bluetoothsdk.utils.ControllerTypes
|
|
4
4
|
import com.mentra.bluetoothsdk.utils.DeviceTypes
|
|
5
|
+
import expo.modules.kotlin.exception.CodedException
|
|
5
6
|
|
|
6
7
|
data class MentraBluetoothSdkConfig @JvmOverloads constructor(
|
|
7
8
|
val deliverCallbacksOnMainThread: Boolean = true,
|
|
8
9
|
val analytics: BluetoothSdkAnalyticsConfig = BluetoothSdkAnalyticsConfig(),
|
|
9
10
|
)
|
|
10
11
|
|
|
11
|
-
class
|
|
12
|
-
|
|
12
|
+
class BluetoothSdkException(
|
|
13
|
+
code: String,
|
|
13
14
|
message: String,
|
|
14
15
|
cause: Throwable? = null,
|
|
15
|
-
) :
|
|
16
|
+
) : CodedException(code, message, cause)
|
|
16
17
|
|
|
17
18
|
enum class DeviceModel(val deviceType: String) {
|
|
18
19
|
G1(DeviceTypes.G1),
|
|
@@ -22,6 +23,7 @@ enum class DeviceModel(val deviceType: String) {
|
|
|
22
23
|
MACH1(DeviceTypes.MACH1),
|
|
23
24
|
Z100(DeviceTypes.Z100),
|
|
24
25
|
FRAME(DeviceTypes.FRAME),
|
|
26
|
+
NIMO(DeviceTypes.NIMO),
|
|
25
27
|
SIMULATED(DeviceTypes.SIMULATED),
|
|
26
28
|
R1(ControllerTypes.R1);
|
|
27
29
|
|
|
@@ -9,7 +9,8 @@ object DeviceTypes {
|
|
|
9
9
|
const val Z100 = "Vuzix Z100"
|
|
10
10
|
const val FRAME = "Brilliant Frame"
|
|
11
11
|
const val G2 = "Even Realities G2"
|
|
12
|
-
val
|
|
12
|
+
const val NIMO = "NIMO"
|
|
13
|
+
val ALL = arrayOf(SIMULATED, G1, G2, MACH1, LIVE, Z100, FRAME, NEX, NIMO)
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
object ControllerTypes {
|
|
@@ -1,16 +1,33 @@
|
|
|
1
1
|
package com.mentra.bluetoothsdk.camera
|
|
2
2
|
|
|
3
|
+
import com.mentra.bluetoothsdk.PhotoCompression
|
|
4
|
+
import com.mentra.bluetoothsdk.PhotoRequest
|
|
5
|
+
import com.mentra.bluetoothsdk.PhotoSize
|
|
3
6
|
import kotlin.test.Test
|
|
7
|
+
import kotlin.test.assertEquals
|
|
4
8
|
import kotlin.test.assertNull
|
|
9
|
+
import kotlin.test.assertTrue
|
|
5
10
|
|
|
6
11
|
class PhotoRequestTest {
|
|
12
|
+
@Test
|
|
13
|
+
fun `constructor generates requestId when omitted`() {
|
|
14
|
+
val request =
|
|
15
|
+
PhotoRequest(
|
|
16
|
+
size = PhotoSize.MEDIUM,
|
|
17
|
+
webhookUrl = "https://example.com/upload",
|
|
18
|
+
compress = PhotoCompression.NONE,
|
|
19
|
+
sound = true,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
assertTrue(request.requestId.startsWith("photo-"))
|
|
23
|
+
}
|
|
24
|
+
|
|
7
25
|
@Test
|
|
8
26
|
fun `fromMap defaults exposureTimeNs null`() {
|
|
9
27
|
val request =
|
|
10
28
|
PhotoRequest.fromMap(
|
|
11
29
|
mapOf(
|
|
12
30
|
"requestId" to "photo-1",
|
|
13
|
-
"appId" to "com.test.app",
|
|
14
31
|
"size" to "medium",
|
|
15
32
|
"webhookUrl" to "https://example.com/upload",
|
|
16
33
|
"compress" to "none",
|
|
@@ -20,4 +37,46 @@ class PhotoRequestTest {
|
|
|
20
37
|
|
|
21
38
|
assertNull(request.exposureTimeNs)
|
|
22
39
|
}
|
|
40
|
+
|
|
41
|
+
@Test
|
|
42
|
+
fun `fromMap generates requestId when omitted or blank`() {
|
|
43
|
+
val withoutRequestId =
|
|
44
|
+
PhotoRequest.fromMap(
|
|
45
|
+
mapOf(
|
|
46
|
+
"size" to "medium",
|
|
47
|
+
"webhookUrl" to "https://example.com/upload",
|
|
48
|
+
"compress" to "none",
|
|
49
|
+
"sound" to true,
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
val blankRequestId =
|
|
53
|
+
PhotoRequest.fromMap(
|
|
54
|
+
mapOf(
|
|
55
|
+
"requestId" to " ",
|
|
56
|
+
"size" to "medium",
|
|
57
|
+
"webhookUrl" to "https://example.com/upload",
|
|
58
|
+
"compress" to "none",
|
|
59
|
+
"sound" to true,
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
assertTrue(withoutRequestId.requestId.startsWith("photo-"))
|
|
64
|
+
assertTrue(blankRequestId.requestId.startsWith("photo-"))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@Test
|
|
68
|
+
fun `fromMap preserves explicit requestId`() {
|
|
69
|
+
val request =
|
|
70
|
+
PhotoRequest.fromMap(
|
|
71
|
+
mapOf(
|
|
72
|
+
"requestId" to "photo-1",
|
|
73
|
+
"size" to "medium",
|
|
74
|
+
"webhookUrl" to "https://example.com/upload",
|
|
75
|
+
"compress" to "none",
|
|
76
|
+
"sound" to true,
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
assertEquals("photo-1", request.requestId)
|
|
81
|
+
}
|
|
23
82
|
}
|