@mentra/bluetooth-sdk 0.1.11 → 0.1.12
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 +52 -3
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkAnalytics.kt +182 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +22 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +10 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +10 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +76 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +15 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +34 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +12 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +2 -1
- package/build/BluetoothSdk.types.d.ts +33 -1
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +1 -1
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +21 -7
- package/ios/Source/BluetoothSdkDefaults.swift +27 -0
- package/ios/Source/Bridge.swift +6 -0
- package/ios/Source/DeviceManager.swift +9 -6
- package/ios/Source/MentraBluetoothSDK.swift +160 -8
- package/ios/Source/camera/CameraModels.swift +49 -1
- package/ios/Source/events/BluetoothEvents.swift +3 -0
- package/ios/Source/internal/BluetoothAvailability.swift +20 -0
- package/ios/Source/internal/BluetoothSdkAnalytics.swift +160 -0
- package/ios/Source/sgcs/MentraLive.swift +29 -5
- package/ios/Source/sgcs/SGCManager.swift +13 -2
- package/ios/Source/types/DeviceModels.swift +5 -1
- package/package.json +1 -1
- package/plugin/build/index.d.ts +4 -0
- package/plugin/build/withAndroid.d.ts +2 -3
- package/plugin/build/withAndroid.js +46 -0
- package/plugin/build/withIos.d.ts +2 -3
- package/plugin/build/withIos.js +32 -0
- package/src/BluetoothSdk.types.ts +40 -1
- package/src/_private/BluetoothSdkModule.ts +5 -1
- package/src/index.ts +2 -0
|
@@ -31,9 +31,16 @@ protocol SGCManager {
|
|
|
31
31
|
/// Defaulted in an extension to delegate to the basic recording path; devices
|
|
32
32
|
/// that support custom settings (e.g. Mentra Live) override this.
|
|
33
33
|
func startVideoRecording(
|
|
34
|
-
requestId: String, save: Bool, flash: Bool, sound: Bool, width: Int, height: Int, fps: Int
|
|
34
|
+
requestId: String, save: Bool, flash: Bool, sound: Bool, width: Int, height: Int, fps: Int,
|
|
35
|
+
maxRecordingTimeMinutes: Int
|
|
35
36
|
)
|
|
36
37
|
func stopVideoRecording(requestId: String)
|
|
38
|
+
/// Stop recording and upload the result to `webhookUrl` (multipart) using
|
|
39
|
+
/// `authToken`. Supplied at stop time so the token is fresh when the upload
|
|
40
|
+
/// runs. Defaulted in an extension to ignore the upload target and just stop;
|
|
41
|
+
/// devices that support webhook upload (e.g. Mentra Live) override this. An
|
|
42
|
+
/// empty/nil `webhookUrl` means "keep the video on device".
|
|
43
|
+
func stopVideoRecording(requestId: String, webhookUrl: String?, authToken: String?)
|
|
37
44
|
|
|
38
45
|
// MARK: - Button Settings
|
|
39
46
|
|
|
@@ -145,11 +152,15 @@ extension SGCManager {
|
|
|
145
152
|
|
|
146
153
|
func startVideoRecording(
|
|
147
154
|
requestId: String, save: Bool, flash: Bool, sound: Bool, width _: Int, height _: Int,
|
|
148
|
-
fps _: Int
|
|
155
|
+
fps _: Int, maxRecordingTimeMinutes _: Int
|
|
149
156
|
) {
|
|
150
157
|
startVideoRecording(requestId: requestId, save: save, flash: flash, sound: sound)
|
|
151
158
|
}
|
|
152
159
|
|
|
160
|
+
func stopVideoRecording(requestId: String, webhookUrl _: String?, authToken _: String?) {
|
|
161
|
+
stopVideoRecording(requestId: requestId)
|
|
162
|
+
}
|
|
163
|
+
|
|
153
164
|
// MARK: - Dashboard (default: combined wire format; Nex implements single-field)
|
|
154
165
|
|
|
155
166
|
func setDashboardHeightOnly(_ height: Int) {
|
|
@@ -3,7 +3,11 @@ import Foundation
|
|
|
3
3
|
public struct MentraBluetoothSDKConfiguration {
|
|
4
4
|
public static let `default` = MentraBluetoothSDKConfiguration()
|
|
5
5
|
|
|
6
|
-
public
|
|
6
|
+
public let analytics: BluetoothSdkAnalyticsConfiguration
|
|
7
|
+
|
|
8
|
+
public init(analytics: BluetoothSdkAnalyticsConfiguration = BluetoothSdkAnalyticsConfiguration()) {
|
|
9
|
+
self.analytics = analytics
|
|
10
|
+
}
|
|
7
11
|
}
|
|
8
12
|
|
|
9
13
|
public enum DeviceModel: String {
|
package/package.json
CHANGED
package/plugin/build/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { type ConfigPlugin } from "expo/config-plugins";
|
|
2
2
|
export interface BluetoothSdkPluginProps {
|
|
3
3
|
node?: boolean;
|
|
4
|
+
analytics?: boolean | BluetoothSdkAnalyticsPluginProps;
|
|
5
|
+
}
|
|
6
|
+
export interface BluetoothSdkAnalyticsPluginProps {
|
|
7
|
+
enabled?: boolean;
|
|
4
8
|
}
|
|
5
9
|
declare const withBluetoothSdk: ConfigPlugin<BluetoothSdkPluginProps>;
|
|
6
10
|
export default withBluetoothSdk;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import { type ConfigPlugin } from "expo/config-plugins";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}>;
|
|
2
|
+
import { type BluetoothSdkPluginProps } from "./index";
|
|
3
|
+
export declare const withAndroidConfiguration: ConfigPlugin<BluetoothSdkPluginProps>;
|
|
@@ -7,6 +7,10 @@ exports.withAndroidConfiguration = void 0;
|
|
|
7
7
|
const child_process_1 = require("child_process");
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const config_plugins_1 = require("expo/config-plugins");
|
|
10
|
+
const META_ANALYTICS_DISABLED = "com.mentra.bluetoothsdk.analytics.disabled";
|
|
11
|
+
const STALE_META_POSTHOG_API_KEY = "com.mentra.bluetoothsdk.analytics.posthog_api_key";
|
|
12
|
+
const STALE_META_POSTHOG_HOST = "com.mentra.bluetoothsdk.analytics.posthog_host";
|
|
13
|
+
const ANALYTICS_META_NAMES = [META_ANALYTICS_DISABLED, STALE_META_POSTHOG_API_KEY, STALE_META_POSTHOG_HOST];
|
|
10
14
|
function getBluetoothSdkRoot() {
|
|
11
15
|
return path_1.default.dirname(require.resolve("../../package.json"));
|
|
12
16
|
}
|
|
@@ -133,9 +137,51 @@ function withSherpaOnnxLocalMavenRepo(config) {
|
|
|
133
137
|
return config;
|
|
134
138
|
});
|
|
135
139
|
}
|
|
140
|
+
function resolveAnalyticsProps(props) {
|
|
141
|
+
const analytics = props?.analytics;
|
|
142
|
+
let disabled;
|
|
143
|
+
if (analytics === false) {
|
|
144
|
+
disabled = true;
|
|
145
|
+
}
|
|
146
|
+
else if (analytics === true) {
|
|
147
|
+
disabled = false;
|
|
148
|
+
}
|
|
149
|
+
else if (typeof analytics === "object" && analytics.enabled !== undefined) {
|
|
150
|
+
disabled = !analytics.enabled;
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
disabled,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function upsertMetaData(application, name, value) {
|
|
157
|
+
application["meta-data"] ??= [];
|
|
158
|
+
const existing = application["meta-data"].find((item) => item.$?.["android:name"] === name);
|
|
159
|
+
const entry = existing ?? { $: { "android:name": name } };
|
|
160
|
+
entry.$["android:value"] = value;
|
|
161
|
+
if (!existing) {
|
|
162
|
+
application["meta-data"].push(entry);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function removeMetaData(application, name) {
|
|
166
|
+
application["meta-data"] = (application["meta-data"] ?? []).filter((item) => item.$?.["android:name"] !== name);
|
|
167
|
+
}
|
|
168
|
+
function withAnalyticsManifestMetadata(config, props) {
|
|
169
|
+
return (0, config_plugins_1.withAndroidManifest)(config, (config) => {
|
|
170
|
+
const analytics = resolveAnalyticsProps(props);
|
|
171
|
+
const application = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults);
|
|
172
|
+
for (const name of ANALYTICS_META_NAMES) {
|
|
173
|
+
removeMetaData(application, name);
|
|
174
|
+
}
|
|
175
|
+
if (analytics.disabled !== undefined) {
|
|
176
|
+
upsertMetaData(application, META_ANALYTICS_DISABLED, analytics.disabled ? "true" : "false");
|
|
177
|
+
}
|
|
178
|
+
return config;
|
|
179
|
+
});
|
|
180
|
+
}
|
|
136
181
|
const withAndroidConfiguration = (config, props) => {
|
|
137
182
|
config = withSettingsGradleModifications(config);
|
|
138
183
|
config = withSherpaOnnxLocalMavenRepo(config);
|
|
184
|
+
config = withAnalyticsManifestMetadata(config, props);
|
|
139
185
|
if (props?.node) {
|
|
140
186
|
config = withGradlePropertiesModifications(config);
|
|
141
187
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import { type ConfigPlugin } from "expo/config-plugins";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}>;
|
|
2
|
+
import { type BluetoothSdkPluginProps } from "./index";
|
|
3
|
+
export declare const withIosConfiguration: ConfigPlugin<BluetoothSdkPluginProps>;
|
package/plugin/build/withIos.js
CHANGED
|
@@ -10,6 +10,9 @@ const path_1 = __importDefault(require("path"));
|
|
|
10
10
|
const config_plugins_1 = require("expo/config-plugins");
|
|
11
11
|
const BLUETOOTH_SDK_EXPO_ADAPTER_ENV = "MENTRA_BLUETOOTH_SDK_INCLUDE_EXPO_ADAPTER";
|
|
12
12
|
const BLUETOOTH_SDK_EXPO_ADAPTER_LINE = `ENV['${BLUETOOTH_SDK_EXPO_ADAPTER_ENV}'] ||= '1'`;
|
|
13
|
+
const INFO_ANALYTICS_DISABLED = "MentraBluetoothSdkAnalyticsDisabled";
|
|
14
|
+
const STALE_INFO_POSTHOG_API_KEY = "MentraBluetoothSdkPostHogApiKey";
|
|
15
|
+
const STALE_INFO_POSTHOG_HOST = "MentraBluetoothSdkPostHogHost";
|
|
13
16
|
const ensureBluetoothSdkExpoAdapterPodEnv = (podfile) => {
|
|
14
17
|
if (podfile.includes(BLUETOOTH_SDK_EXPO_ADAPTER_ENV)) {
|
|
15
18
|
return podfile;
|
|
@@ -51,11 +54,40 @@ const withXcodeEnvLocal = (config) => {
|
|
|
51
54
|
},
|
|
52
55
|
]);
|
|
53
56
|
};
|
|
57
|
+
function resolveAnalyticsProps(props) {
|
|
58
|
+
const analytics = props?.analytics;
|
|
59
|
+
let disabled;
|
|
60
|
+
if (analytics === false) {
|
|
61
|
+
disabled = true;
|
|
62
|
+
}
|
|
63
|
+
else if (analytics === true) {
|
|
64
|
+
disabled = false;
|
|
65
|
+
}
|
|
66
|
+
else if (typeof analytics === "object" && analytics.enabled !== undefined) {
|
|
67
|
+
disabled = !analytics.enabled;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
disabled,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function withAnalyticsInfoPlist(config, props) {
|
|
74
|
+
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
75
|
+
const analytics = resolveAnalyticsProps(props);
|
|
76
|
+
delete config.modResults[INFO_ANALYTICS_DISABLED];
|
|
77
|
+
delete config.modResults[STALE_INFO_POSTHOG_API_KEY];
|
|
78
|
+
delete config.modResults[STALE_INFO_POSTHOG_HOST];
|
|
79
|
+
if (analytics.disabled !== undefined) {
|
|
80
|
+
config.modResults[INFO_ANALYTICS_DISABLED] = analytics.disabled;
|
|
81
|
+
}
|
|
82
|
+
return config;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
54
85
|
const withIosConfiguration = (config, props) => {
|
|
55
86
|
config = (0, config_plugins_1.withPodfile)(config, (config) => {
|
|
56
87
|
config.modResults.contents = ensureBluetoothSdkExpoAdapterPodEnv(config.modResults.contents);
|
|
57
88
|
return config;
|
|
58
89
|
});
|
|
90
|
+
config = withAnalyticsInfoPlist(config, props);
|
|
59
91
|
if (props?.node) {
|
|
60
92
|
config = withXcodeEnvLocal(config);
|
|
61
93
|
}
|
|
@@ -307,6 +307,24 @@ export type VideoRecordingSuccessStatusEvent =
|
|
|
307
307
|
| VideoRecordingStartedStatusEvent
|
|
308
308
|
| VideoRecordingStoppedStatusEvent
|
|
309
309
|
|
|
310
|
+
export type MediaUploadSuccessEvent = {
|
|
311
|
+
type: "media_success"
|
|
312
|
+
requestId: string
|
|
313
|
+
mediaUrl: string
|
|
314
|
+
mediaType: number
|
|
315
|
+
timestamp: number
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export type MediaUploadErrorEvent = {
|
|
319
|
+
type: "media_error"
|
|
320
|
+
requestId: string
|
|
321
|
+
errorMessage: string
|
|
322
|
+
mediaType: number
|
|
323
|
+
timestamp: number
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export type MediaUploadEvent = MediaUploadSuccessEvent | MediaUploadErrorEvent
|
|
327
|
+
|
|
310
328
|
export type GalleryStatusEvent = {
|
|
311
329
|
type: "gallery_status"
|
|
312
330
|
photos: number
|
|
@@ -416,6 +434,12 @@ export interface VideoRecordingSettings {
|
|
|
416
434
|
width?: number
|
|
417
435
|
height?: number
|
|
418
436
|
fps?: number
|
|
437
|
+
/**
|
|
438
|
+
* Optional auto-stop timer in minutes, sent on `start_video_recording`.
|
|
439
|
+
* `0` (the default) means record until stopped or interrupted
|
|
440
|
+
* (battery/storage/thermal/error).
|
|
441
|
+
*/
|
|
442
|
+
maxRecordingTimeMinutes?: number
|
|
419
443
|
}
|
|
420
444
|
export const DeviceModels = {
|
|
421
445
|
Simulated: "Simulated Glasses",
|
|
@@ -743,6 +767,8 @@ export type BluetoothSdkModuleEvents = {
|
|
|
743
767
|
photo_response: (event: PhotoResponseEvent) => void
|
|
744
768
|
photo_status: (event: PhotoStatusEvent) => void
|
|
745
769
|
video_recording_status: (event: VideoRecordingStatusEvent) => void
|
|
770
|
+
media_success: (event: MediaUploadSuccessEvent) => void
|
|
771
|
+
media_error: (event: MediaUploadErrorEvent) => void
|
|
746
772
|
gallery_status: (event: GalleryStatusEvent) => void
|
|
747
773
|
compatible_glasses_search_stop: (event: CompatibleGlassesSearchStopEvent) => void
|
|
748
774
|
heartbeat_sent: (event: HeartbeatSentEvent) => void
|
|
@@ -818,6 +844,8 @@ export type BluetoothSdkEventMap = {
|
|
|
818
844
|
photo_response: PhotoResponseEvent
|
|
819
845
|
photo_status: PhotoStatusEvent
|
|
820
846
|
video_recording_status: VideoRecordingStatusEvent
|
|
847
|
+
media_success: MediaUploadSuccessEvent
|
|
848
|
+
media_error: MediaUploadErrorEvent
|
|
821
849
|
gallery_status: GalleryStatusEvent
|
|
822
850
|
compatible_glasses_search_stop: CompatibleGlassesSearchStopEvent
|
|
823
851
|
swipe_volume_status: SwipeVolumeStatusEvent
|
|
@@ -898,7 +926,18 @@ export interface BluetoothSdkPublicModule {
|
|
|
898
926
|
sound: boolean,
|
|
899
927
|
settings?: VideoRecordingSettings,
|
|
900
928
|
): Promise<VideoRecordingStartedStatusEvent>
|
|
901
|
-
|
|
929
|
+
/**
|
|
930
|
+
* Stop the active recording. When {@link webhookUrl} is provided, the glasses
|
|
931
|
+
* upload the recorded video to it (multipart) using {@link authToken}. These
|
|
932
|
+
* are supplied at stop time (not start) so the token is fresh when the upload
|
|
933
|
+
* runs — a recording can last arbitrarily long. An empty/omitted webhook keeps
|
|
934
|
+
* the video on device (no upload).
|
|
935
|
+
*/
|
|
936
|
+
stopVideoRecording(
|
|
937
|
+
requestId: string,
|
|
938
|
+
webhookUrl?: string,
|
|
939
|
+
authToken?: string,
|
|
940
|
+
): Promise<VideoRecordingStoppedStatusEvent>
|
|
902
941
|
|
|
903
942
|
startStream(params: StreamStartRequest): Promise<StreamStatusEvent>
|
|
904
943
|
stopStream(): Promise<StreamStatusEvent>
|
|
@@ -148,7 +148,11 @@ declare class BluetoothSdkNativeModule extends NativeModule<BluetoothSdkModuleEv
|
|
|
148
148
|
sound: boolean,
|
|
149
149
|
settings?: VideoRecordingSettings,
|
|
150
150
|
): Promise<VideoRecordingStartedStatusEvent>
|
|
151
|
-
stopVideoRecording(
|
|
151
|
+
stopVideoRecording(
|
|
152
|
+
requestId: string,
|
|
153
|
+
webhookUrl?: string,
|
|
154
|
+
authToken?: string,
|
|
155
|
+
): Promise<VideoRecordingStoppedStatusEvent>
|
|
152
156
|
|
|
153
157
|
// Stream Commands
|
|
154
158
|
startStream(params: StreamStartRequest): Promise<StreamStatusEvent>
|
package/src/index.ts
CHANGED