@mentra/sdk 2.1.26 โ 2.1.28
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/dist/app/session/api-client.d.ts.map +1 -1
- package/dist/app/session/dashboard.d.ts +5 -8
- package/dist/app/session/dashboard.d.ts.map +1 -1
- package/dist/app/session/events.d.ts +5 -2
- package/dist/app/session/events.d.ts.map +1 -1
- package/dist/app/session/index.d.ts +62 -3
- package/dist/app/session/index.d.ts.map +1 -1
- package/dist/app/session/modules/audio.d.ts +33 -4
- package/dist/app/session/modules/audio.d.ts.map +1 -1
- package/dist/app/session/modules/camera-managed-extension.d.ts +2 -3
- package/dist/app/session/modules/camera-managed-extension.d.ts.map +1 -1
- package/dist/app/session/modules/camera.d.ts +5 -5
- package/dist/app/session/modules/camera.d.ts.map +1 -1
- package/dist/app/session/modules/led.d.ts +141 -0
- package/dist/app/session/modules/led.d.ts.map +1 -0
- package/dist/app/session/modules/location.d.ts +1 -2
- package/dist/app/session/modules/location.d.ts.map +1 -1
- package/dist/app/session/modules/simple-storage.d.ts.map +1 -1
- package/dist/constants/log-messages/color.d.ts +5 -0
- package/dist/constants/log-messages/color.d.ts.map +1 -0
- package/dist/constants/log-messages/logos.d.ts +4 -0
- package/dist/constants/log-messages/logos.d.ts.map +1 -0
- package/dist/constants/{messages.d.ts โ log-messages/updates.d.ts} +2 -3
- package/dist/constants/log-messages/updates.d.ts.map +1 -0
- package/dist/constants/log-messages/warning.d.ts +8 -0
- package/dist/constants/log-messages/warning.d.ts.map +1 -0
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5347 -112
- package/dist/index.js.map +45 -0
- package/dist/logging/logger.d.ts +2 -1
- package/dist/logging/logger.d.ts.map +1 -1
- package/dist/types/capabilities.d.ts +3 -0
- package/dist/types/capabilities.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -14
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/message-types.d.ts +12 -3
- package/dist/types/message-types.d.ts.map +1 -1
- package/dist/types/messages/app-to-cloud.d.ts +48 -2
- package/dist/types/messages/app-to-cloud.d.ts.map +1 -1
- package/dist/types/messages/cloud-to-app.d.ts +25 -6
- package/dist/types/messages/cloud-to-app.d.ts.map +1 -1
- package/dist/types/messages/cloud-to-glasses.d.ts +43 -1
- package/dist/types/messages/cloud-to-glasses.d.ts.map +1 -1
- package/dist/types/messages/glasses-to-cloud.d.ts +32 -1
- package/dist/types/messages/glasses-to-cloud.d.ts.map +1 -1
- package/dist/types/rtmp-stream.d.ts +1 -1
- package/dist/types/rtmp-stream.d.ts.map +1 -1
- package/dist/types/streams.d.ts +28 -1
- package/dist/types/streams.d.ts.map +1 -1
- package/dist/utils/permissions-utils.d.ts +8 -0
- package/dist/utils/permissions-utils.d.ts.map +1 -0
- package/package.json +12 -3
- package/dist/app/index.js +0 -24
- package/dist/app/server/index.js +0 -658
- package/dist/app/session/api-client.js +0 -101
- package/dist/app/session/dashboard.js +0 -149
- package/dist/app/session/events.js +0 -305
- package/dist/app/session/index.js +0 -1571
- package/dist/app/session/layouts.js +0 -372
- package/dist/app/session/modules/audio.js +0 -321
- package/dist/app/session/modules/camera-managed-extension.js +0 -310
- package/dist/app/session/modules/camera.js +0 -603
- package/dist/app/session/modules/index.js +0 -19
- package/dist/app/session/modules/location.js +0 -58
- package/dist/app/session/modules/simple-storage.js +0 -232
- package/dist/app/session/settings.js +0 -358
- package/dist/app/token/index.js +0 -22
- package/dist/app/token/utils.js +0 -144
- package/dist/app/webview/index.js +0 -382
- package/dist/constants/index.js +0 -16
- package/dist/constants/messages.d.ts.map +0 -1
- package/dist/constants/messages.js +0 -57
- package/dist/examples/managed-rtmp-streaming-example.js +0 -158
- package/dist/examples/managed-rtmp-streaming-with-restream-example.js +0 -124
- package/dist/examples/rtmp-streaming-example.js +0 -102
- package/dist/logging/logger.js +0 -79
- package/dist/types/capabilities.js +0 -9
- package/dist/types/dashboard/index.js +0 -12
- package/dist/types/enums.js +0 -75
- package/dist/types/index.js +0 -101
- package/dist/types/layouts.js +0 -3
- package/dist/types/message-types.js +0 -207
- package/dist/types/messages/app-to-cloud.js +0 -95
- package/dist/types/messages/base.js +0 -3
- package/dist/types/messages/cloud-to-app.js +0 -78
- package/dist/types/messages/cloud-to-glasses.js +0 -68
- package/dist/types/messages/glasses-to-cloud.js +0 -139
- package/dist/types/models.js +0 -101
- package/dist/types/photo-data.js +0 -2
- package/dist/types/rtmp-stream.js +0 -3
- package/dist/types/streams.js +0 -306
- package/dist/types/token.js +0 -7
- package/dist/types/webhooks.js +0 -28
- package/dist/utils/animation-utils.js +0 -340
- package/dist/utils/bitmap-utils.js +0 -475
- package/dist/utils/resource-tracker.js +0 -153
|
@@ -1,372 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LayoutManager = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* ๐จ Layout Manager Module
|
|
6
|
-
*
|
|
7
|
-
* Manages AR display layouts for Apps. This class provides an easy-to-use interface
|
|
8
|
-
* for showing different types of content in the user's AR view.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```typescript
|
|
12
|
-
* const layouts = new LayoutManager('org.example.myapp', sendMessage);
|
|
13
|
-
*
|
|
14
|
-
* // Show a simple message
|
|
15
|
-
* layouts.showTextWall('Hello AR World!');
|
|
16
|
-
*
|
|
17
|
-
* // Show a card with title
|
|
18
|
-
* layouts.showReferenceCard('Weather', 'Sunny and 75ยฐF');
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
const bitmap_utils_1 = require("../../utils/bitmap-utils");
|
|
22
|
-
const types_1 = require("../../types");
|
|
23
|
-
class LayoutManager {
|
|
24
|
-
/**
|
|
25
|
-
* ๐ฏ Creates a new LayoutManager instance
|
|
26
|
-
*
|
|
27
|
-
* @param packageName - App package identifier
|
|
28
|
-
* @param sendMessage - Function to send display requests to MentraOS
|
|
29
|
-
*/
|
|
30
|
-
constructor(packageName, sendMessage) {
|
|
31
|
-
this.packageName = packageName;
|
|
32
|
-
this.sendMessage = sendMessage;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* ๐ฆ Creates a display event request with validation
|
|
36
|
-
*
|
|
37
|
-
* @param layout - Layout configuration to display
|
|
38
|
-
* @param view - View type (main or dashboard)
|
|
39
|
-
* @param durationMs - How long to show the layout (optional)
|
|
40
|
-
* @returns Formatted display request
|
|
41
|
-
* @throws Error if layout is invalid
|
|
42
|
-
*/
|
|
43
|
-
createDisplayEvent(layout, view = types_1.ViewType.MAIN, durationMs) {
|
|
44
|
-
try {
|
|
45
|
-
// Validate layout data before sending
|
|
46
|
-
if (!layout) {
|
|
47
|
-
throw new Error("Layout cannot be null or undefined");
|
|
48
|
-
}
|
|
49
|
-
if (!layout.layoutType) {
|
|
50
|
-
throw new Error("Layout must have a layoutType property");
|
|
51
|
-
}
|
|
52
|
-
// Layout-specific validations
|
|
53
|
-
switch (layout.layoutType) {
|
|
54
|
-
case types_1.LayoutType.TEXT_WALL:
|
|
55
|
-
if (typeof layout.text !== "string") {
|
|
56
|
-
throw new Error("TextWall layout must have a text property");
|
|
57
|
-
}
|
|
58
|
-
// Ensure text is not too long (prevent performance issues)
|
|
59
|
-
if (layout.text.length > 1000) {
|
|
60
|
-
console.warn("TextWall text is very long, this may cause performance issues");
|
|
61
|
-
}
|
|
62
|
-
break;
|
|
63
|
-
case types_1.LayoutType.DOUBLE_TEXT_WALL:
|
|
64
|
-
const doubleText = layout;
|
|
65
|
-
if (typeof doubleText.topText !== "string") {
|
|
66
|
-
throw new Error("DoubleTextWall layout must have a topText property");
|
|
67
|
-
}
|
|
68
|
-
if (typeof doubleText.bottomText !== "string") {
|
|
69
|
-
throw new Error("DoubleTextWall layout must have a bottomText property");
|
|
70
|
-
}
|
|
71
|
-
break;
|
|
72
|
-
case types_1.LayoutType.REFERENCE_CARD:
|
|
73
|
-
const refCard = layout;
|
|
74
|
-
if (typeof refCard.title !== "string") {
|
|
75
|
-
throw new Error("ReferenceCard layout must have a title property");
|
|
76
|
-
}
|
|
77
|
-
if (typeof refCard.text !== "string") {
|
|
78
|
-
throw new Error("ReferenceCard layout must have a text property");
|
|
79
|
-
}
|
|
80
|
-
break;
|
|
81
|
-
case types_1.LayoutType.DASHBOARD_CARD:
|
|
82
|
-
const dashCard = layout;
|
|
83
|
-
if (typeof dashCard.leftText !== "string") {
|
|
84
|
-
throw new Error("DashboardCard layout must have a leftText property");
|
|
85
|
-
}
|
|
86
|
-
if (typeof dashCard.rightText !== "string") {
|
|
87
|
-
throw new Error("DashboardCard layout must have a rightText property");
|
|
88
|
-
}
|
|
89
|
-
break;
|
|
90
|
-
case types_1.LayoutType.BITMAP_VIEW:
|
|
91
|
-
const bitmapView = layout;
|
|
92
|
-
if (typeof bitmapView.data !== "string") {
|
|
93
|
-
throw new Error("BitmapView layout must have a data property");
|
|
94
|
-
}
|
|
95
|
-
// Check if data is too large (prevent OOM errors)
|
|
96
|
-
if (bitmapView.data.length > 1000000) {
|
|
97
|
-
// 1MB limit
|
|
98
|
-
throw new Error("Bitmap data is too large (>1MB), please reduce size");
|
|
99
|
-
}
|
|
100
|
-
break;
|
|
101
|
-
case types_1.LayoutType.CLEAR_VIEW:
|
|
102
|
-
// ClearView has no additional validation needed
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
// Validate view type
|
|
106
|
-
if (view !== types_1.ViewType.MAIN && view !== types_1.ViewType.DASHBOARD) {
|
|
107
|
-
console.warn(`Invalid view type: ${view}, defaulting to MAIN`);
|
|
108
|
-
view = types_1.ViewType.MAIN;
|
|
109
|
-
}
|
|
110
|
-
// Validate duration if provided
|
|
111
|
-
if (durationMs !== undefined) {
|
|
112
|
-
if (typeof durationMs !== "number" || durationMs < 0) {
|
|
113
|
-
console.warn(`Invalid duration: ${durationMs}, ignoring`);
|
|
114
|
-
durationMs = undefined;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
// Create the display request with validated data
|
|
118
|
-
return {
|
|
119
|
-
timestamp: new Date(),
|
|
120
|
-
sessionId: "", // Will be filled by session
|
|
121
|
-
type: types_1.AppToCloudMessageType.DISPLAY_REQUEST,
|
|
122
|
-
packageName: this.packageName,
|
|
123
|
-
view,
|
|
124
|
-
layout,
|
|
125
|
-
durationMs,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
console.error("Error creating display event:", error);
|
|
130
|
-
throw error; // Re-throw to notify caller
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* ๐ Shows a single block of text
|
|
135
|
-
*
|
|
136
|
-
* Best for:
|
|
137
|
-
* - Simple messages
|
|
138
|
-
* - Status updates
|
|
139
|
-
* - Notifications
|
|
140
|
-
*
|
|
141
|
-
* @param text - Text content to display
|
|
142
|
-
* @param options - Optional parameters (view, duration, priority)
|
|
143
|
-
* - priority: If true, this display will not be overridden by other requests (default: false)
|
|
144
|
-
*
|
|
145
|
-
* @example
|
|
146
|
-
* ```typescript
|
|
147
|
-
* layouts.showTextWall('Connected to server');
|
|
148
|
-
* layouts.showTextWall('Onboarding!', { priority: true });
|
|
149
|
-
* ```
|
|
150
|
-
*/
|
|
151
|
-
showTextWall(text, options) {
|
|
152
|
-
try {
|
|
153
|
-
// Validate input before processing
|
|
154
|
-
if (text === undefined || text === null) {
|
|
155
|
-
text = ""; // Default to empty string instead of crashing
|
|
156
|
-
console.warn("showTextWall called with null/undefined text");
|
|
157
|
-
}
|
|
158
|
-
// Ensure text is a string
|
|
159
|
-
if (typeof text !== "string") {
|
|
160
|
-
text = String(text); // Convert to string
|
|
161
|
-
console.warn("showTextWall: Non-string input converted to string");
|
|
162
|
-
}
|
|
163
|
-
// Create layout with validated text
|
|
164
|
-
const layout = {
|
|
165
|
-
layoutType: types_1.LayoutType.TEXT_WALL,
|
|
166
|
-
text,
|
|
167
|
-
};
|
|
168
|
-
// Create and send display event with error handling
|
|
169
|
-
try {
|
|
170
|
-
const displayEvent = this.createDisplayEvent(layout, options?.view, options?.durationMs);
|
|
171
|
-
this.sendMessage(displayEvent);
|
|
172
|
-
}
|
|
173
|
-
catch (error) {
|
|
174
|
-
console.error("Failed to display text wall:", error);
|
|
175
|
-
// Don't re-throw - prevent app crashes
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
catch (error) {
|
|
179
|
-
console.error("Error in showTextWall:", error);
|
|
180
|
-
// Don't crash the App - fail gracefully
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* โ๏ธ Shows two sections of text, one above the other
|
|
185
|
-
*
|
|
186
|
-
* Best for:
|
|
187
|
-
* - Before/After content
|
|
188
|
-
* - Question/Answer displays
|
|
189
|
-
* - Two-part messages
|
|
190
|
-
* - Comparisons
|
|
191
|
-
*
|
|
192
|
-
* @param topText - Text to show in top section
|
|
193
|
-
* @param bottomText - Text to show in bottom section
|
|
194
|
-
* @param options - Optional parameters (view, duration)
|
|
195
|
-
*
|
|
196
|
-
* @example
|
|
197
|
-
* ```typescript
|
|
198
|
-
* layouts.showDoubleTextWall(
|
|
199
|
-
* 'Original: Hello',
|
|
200
|
-
* 'Translated: Bonjour'
|
|
201
|
-
* );
|
|
202
|
-
* ```
|
|
203
|
-
*/
|
|
204
|
-
showDoubleTextWall(topText, bottomText, options) {
|
|
205
|
-
const layout = {
|
|
206
|
-
layoutType: types_1.LayoutType.DOUBLE_TEXT_WALL,
|
|
207
|
-
topText,
|
|
208
|
-
bottomText,
|
|
209
|
-
};
|
|
210
|
-
this.sendMessage(this.createDisplayEvent(layout, options?.view, options?.durationMs));
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* ๐ Shows a card with a title and content
|
|
214
|
-
*
|
|
215
|
-
* Best for:
|
|
216
|
-
* - Titled content
|
|
217
|
-
* - Important information
|
|
218
|
-
* - Structured data
|
|
219
|
-
* - Notifications with context
|
|
220
|
-
*
|
|
221
|
-
* @param title - Card title
|
|
222
|
-
* @param text - Main content text
|
|
223
|
-
* @param options - Optional parameters (view, duration)
|
|
224
|
-
*
|
|
225
|
-
* @example
|
|
226
|
-
* ```typescript
|
|
227
|
-
* layouts.showReferenceCard(
|
|
228
|
-
* 'Meeting Reminder',
|
|
229
|
-
* 'Team standup in 5 minutes'
|
|
230
|
-
* );
|
|
231
|
-
* ```
|
|
232
|
-
*/
|
|
233
|
-
showReferenceCard(title, text, options) {
|
|
234
|
-
const layout = {
|
|
235
|
-
layoutType: types_1.LayoutType.REFERENCE_CARD,
|
|
236
|
-
title,
|
|
237
|
-
text,
|
|
238
|
-
};
|
|
239
|
-
this.sendMessage(this.createDisplayEvent(layout, options?.view, options?.durationMs));
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* ๐ Shows a bitmap
|
|
243
|
-
*
|
|
244
|
-
* Uses the proven animation system internally for proper L/R eye synchronization.
|
|
245
|
-
* This ensures single bitmap displays work consistently with the same
|
|
246
|
-
* hardware-optimized timing as animations.
|
|
247
|
-
*
|
|
248
|
-
* @param data - hex or base64 encoded bitmap data
|
|
249
|
-
* @param options - Optional parameters (view, duration)
|
|
250
|
-
*
|
|
251
|
-
* @example
|
|
252
|
-
* ```typescript
|
|
253
|
-
* layouts.showBitmapView(
|
|
254
|
-
* yourHexOrBase64EncodedBitmapDataString
|
|
255
|
-
* );
|
|
256
|
-
* ```
|
|
257
|
-
*/
|
|
258
|
-
async showBitmapView(base64Bitmap, options) {
|
|
259
|
-
const padding = options?.padding ?? { left: 50, top: 35 };
|
|
260
|
-
const bitmapFrame = await bitmap_utils_1.BitmapUtils.padBase64Bitmap(base64Bitmap, padding);
|
|
261
|
-
const validation = bitmap_utils_1.BitmapUtils.validateBase64Bitmap(bitmapFrame);
|
|
262
|
-
if (!validation.isValid) {
|
|
263
|
-
throw new Error(`โ Frame validation failed: ${validation.errors.join(", ")}`);
|
|
264
|
-
}
|
|
265
|
-
const layout = {
|
|
266
|
-
layoutType: types_1.LayoutType.BITMAP_VIEW,
|
|
267
|
-
data: bitmapFrame,
|
|
268
|
-
};
|
|
269
|
-
this.sendMessage(this.createDisplayEvent(layout, options?.view));
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* ๐ Shows a dashboard card with left and right text
|
|
273
|
-
*
|
|
274
|
-
* Best for:
|
|
275
|
-
* - Key-value pairs
|
|
276
|
-
* - Dashboard displays
|
|
277
|
-
* - Metrics
|
|
278
|
-
*
|
|
279
|
-
* @param leftText - Left side text (typically label/key)
|
|
280
|
-
* @param rightText - Right side text (typically value)
|
|
281
|
-
* @param options - Optional parameters (view, duration)
|
|
282
|
-
*
|
|
283
|
-
* @example
|
|
284
|
-
* ```typescript
|
|
285
|
-
* layouts.showDashboardCard('Weather', '72ยฐF');
|
|
286
|
-
* ```
|
|
287
|
-
*/
|
|
288
|
-
showDashboardCard(leftText, rightText, options) {
|
|
289
|
-
const layout = {
|
|
290
|
-
layoutType: types_1.LayoutType.DASHBOARD_CARD,
|
|
291
|
-
leftText,
|
|
292
|
-
rightText,
|
|
293
|
-
};
|
|
294
|
-
this.sendMessage(this.createDisplayEvent(layout, options?.view || types_1.ViewType.DASHBOARD, options?.durationMs));
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* ๐งน Clears the display
|
|
298
|
-
*
|
|
299
|
-
* Best for:
|
|
300
|
-
* - Clearing previous content
|
|
301
|
-
* - Resetting display state
|
|
302
|
-
* - Starting fresh
|
|
303
|
-
*
|
|
304
|
-
* @param options - Optional parameters (view)
|
|
305
|
-
*
|
|
306
|
-
* @example
|
|
307
|
-
* ```typescript
|
|
308
|
-
* layouts.clearView();
|
|
309
|
-
* layouts.clearView({ view: ViewType.DASHBOARD });
|
|
310
|
-
* ```
|
|
311
|
-
*/
|
|
312
|
-
clearView(options) {
|
|
313
|
-
const layout = {
|
|
314
|
-
layoutType: types_1.LayoutType.CLEAR_VIEW,
|
|
315
|
-
};
|
|
316
|
-
this.sendMessage(this.createDisplayEvent(layout, options?.view));
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* ๐ฌ Shows an animated sequence of bitmap images (iOS-controlled timing)
|
|
320
|
-
*
|
|
321
|
-
* Sends complete animation package to iOS for device-controlled timing.
|
|
322
|
-
* This provides superior performance and synchronization by letting
|
|
323
|
-
* the device control the display timing directly.
|
|
324
|
-
*
|
|
325
|
-
* Best for:
|
|
326
|
-
* - Smooth high-performance animations
|
|
327
|
-
* - Precise timing control
|
|
328
|
-
* - Synchronized left/right display
|
|
329
|
-
* - EvenDemo-quality performance
|
|
330
|
-
*
|
|
331
|
-
* @param bitmapDataArray - Array of bitmap data strings (hex or base64 encoded)
|
|
332
|
-
* @param intervalMs - Time between frames in milliseconds (default: 1650ms)
|
|
333
|
-
* @param repeat - Whether to loop the animation continuously (default: false)
|
|
334
|
-
* @param options - Optional parameters (view)
|
|
335
|
-
*
|
|
336
|
-
* @example
|
|
337
|
-
* ```typescript
|
|
338
|
-
* // Device-controlled animation (recommended)
|
|
339
|
-
* const frames = ['hexdata1', 'hexdata2', 'hexdata3'];
|
|
340
|
-
* layouts.showBitmapAnimation(frames, 1650, true);
|
|
341
|
-
*
|
|
342
|
-
* // Single-cycle animation
|
|
343
|
-
* layouts.showBitmapAnimation(loadingFrames, 1650, false);
|
|
344
|
-
* ```
|
|
345
|
-
*
|
|
346
|
-
* @returns Animation controller object with stop() method
|
|
347
|
-
*/
|
|
348
|
-
showBitmapAnimation(bitmapDataArray, intervalMs = 1650, repeat = false, options) {
|
|
349
|
-
// Validation
|
|
350
|
-
if (!Array.isArray(bitmapDataArray) || bitmapDataArray.length === 0) {
|
|
351
|
-
throw new Error("showBitmapAnimation requires a non-empty array of bitmap data");
|
|
352
|
-
}
|
|
353
|
-
// Send complete animation package to iOS for device-controlled timing
|
|
354
|
-
const layout = {
|
|
355
|
-
layoutType: types_1.LayoutType.BITMAP_ANIMATION,
|
|
356
|
-
frames: bitmapDataArray,
|
|
357
|
-
interval: intervalMs,
|
|
358
|
-
repeat: repeat,
|
|
359
|
-
};
|
|
360
|
-
this.sendMessage(this.createDisplayEvent(layout, options?.view));
|
|
361
|
-
console.log(`๐ฌ Sent batched animation to iOS: ${bitmapDataArray.length} frames at ${intervalMs}ms${repeat ? " (repeating)" : ""}`);
|
|
362
|
-
// Return controller for compatibility
|
|
363
|
-
return {
|
|
364
|
-
stop: () => {
|
|
365
|
-
// Send stop command to iOS
|
|
366
|
-
this.clearView();
|
|
367
|
-
console.log("๐ Animation stop requested");
|
|
368
|
-
},
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
exports.LayoutManager = LayoutManager;
|
|
@@ -1,321 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* ๐ Audio Module
|
|
4
|
-
*
|
|
5
|
-
* Audio functionality for App Sessions.
|
|
6
|
-
* Handles audio playback on connected glasses.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.AudioManager = void 0;
|
|
10
|
-
const types_1 = require("../../../types");
|
|
11
|
-
/**
|
|
12
|
-
* ๐ Audio Module Implementation
|
|
13
|
-
*
|
|
14
|
-
* Audio management for App Sessions.
|
|
15
|
-
* Provides methods for:
|
|
16
|
-
* - ๐ต Playing audio on glasses
|
|
17
|
-
* - โน๏ธ Stopping audio playback
|
|
18
|
-
* - ๐ Monitoring audio request status
|
|
19
|
-
* - ๐งน Cleanup and cancellation
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```typescript
|
|
23
|
-
* // Play audio
|
|
24
|
-
* const result = await session.audio.playAudio({
|
|
25
|
-
* audioUrl: 'https://example.com/sound.mp3',
|
|
26
|
-
* volume: 0.8
|
|
27
|
-
* });
|
|
28
|
-
*
|
|
29
|
-
* // Stop all audio
|
|
30
|
-
* session.audio.stopAudio();
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
class AudioManager {
|
|
34
|
-
/**
|
|
35
|
-
* Create a new AudioManager
|
|
36
|
-
*
|
|
37
|
-
* @param packageName - The App package name
|
|
38
|
-
* @param sessionId - The current session ID
|
|
39
|
-
* @param send - Function to send messages to the cloud
|
|
40
|
-
* @param session - Reference to the parent AppSession (optional)
|
|
41
|
-
* @param logger - Logger instance for debugging
|
|
42
|
-
*/
|
|
43
|
-
constructor(packageName, sessionId, send, session, logger) {
|
|
44
|
-
/** Map to store pending audio play request promises */
|
|
45
|
-
this.pendingAudioRequests = new Map();
|
|
46
|
-
this.packageName = packageName;
|
|
47
|
-
this.sessionId = sessionId;
|
|
48
|
-
this.send = send;
|
|
49
|
-
this.session = session;
|
|
50
|
-
this.logger = logger || console;
|
|
51
|
-
}
|
|
52
|
-
// =====================================
|
|
53
|
-
// ๐ต Audio Playback Functionality
|
|
54
|
-
// =====================================
|
|
55
|
-
/**
|
|
56
|
-
* ๐ Play audio on the connected glasses
|
|
57
|
-
* @param options - Audio playback configuration
|
|
58
|
-
* @returns Promise that resolves with playback result
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* ```typescript
|
|
62
|
-
* // Play audio from URL
|
|
63
|
-
* const result = await session.audio.playAudio({
|
|
64
|
-
* audioUrl: 'https://example.com/sound.mp3',
|
|
65
|
-
* volume: 0.8
|
|
66
|
-
* });
|
|
67
|
-
* ```
|
|
68
|
-
*/
|
|
69
|
-
async playAudio(options) {
|
|
70
|
-
return new Promise((resolve, reject) => {
|
|
71
|
-
try {
|
|
72
|
-
// Validate input
|
|
73
|
-
if (!options.audioUrl) {
|
|
74
|
-
reject("audioUrl must be provided");
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
// Generate unique request ID
|
|
78
|
-
const requestId = `audio_req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
79
|
-
// Store promise resolvers for when we get the response
|
|
80
|
-
this.pendingAudioRequests.set(requestId, { resolve, reject });
|
|
81
|
-
// Create audio play request message
|
|
82
|
-
const message = {
|
|
83
|
-
type: types_1.AppToCloudMessageType.AUDIO_PLAY_REQUEST,
|
|
84
|
-
packageName: this.packageName,
|
|
85
|
-
sessionId: this.sessionId,
|
|
86
|
-
requestId,
|
|
87
|
-
timestamp: new Date(),
|
|
88
|
-
audioUrl: options.audioUrl,
|
|
89
|
-
volume: options.volume ?? 1.0,
|
|
90
|
-
stopOtherAudio: options.stopOtherAudio ?? true,
|
|
91
|
-
};
|
|
92
|
-
// Send request to cloud
|
|
93
|
-
this.send(message);
|
|
94
|
-
// Set timeout to avoid hanging promises
|
|
95
|
-
const timeoutMs = 60000; // 60 seconds
|
|
96
|
-
if (this.session && this.session.resources) {
|
|
97
|
-
// Use session's resource tracker for automatic cleanup
|
|
98
|
-
this.session.resources.setTimeout(() => {
|
|
99
|
-
if (this.pendingAudioRequests.has(requestId)) {
|
|
100
|
-
this.pendingAudioRequests
|
|
101
|
-
.get(requestId)
|
|
102
|
-
.reject("Audio play request timed out");
|
|
103
|
-
this.pendingAudioRequests.delete(requestId);
|
|
104
|
-
this.logger.warn({ requestId }, `๐ Audio play request timed out`);
|
|
105
|
-
}
|
|
106
|
-
}, timeoutMs);
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
// Fallback to regular setTimeout if session not available
|
|
110
|
-
setTimeout(() => {
|
|
111
|
-
if (this.pendingAudioRequests.has(requestId)) {
|
|
112
|
-
this.pendingAudioRequests
|
|
113
|
-
.get(requestId)
|
|
114
|
-
.reject("Audio play request timed out");
|
|
115
|
-
this.pendingAudioRequests.delete(requestId);
|
|
116
|
-
this.logger.warn({ requestId }, `๐ Audio play request timed out`);
|
|
117
|
-
}
|
|
118
|
-
}, timeoutMs);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
123
|
-
reject(`Failed to play audio: ${errorMessage}`);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* ๐ Stop audio playback on the connected glasses
|
|
129
|
-
*
|
|
130
|
-
* @example
|
|
131
|
-
* ```typescript
|
|
132
|
-
* // Stop all currently playing audio
|
|
133
|
-
* session.audio.stopAudio();
|
|
134
|
-
* ```
|
|
135
|
-
*/
|
|
136
|
-
stopAudio() {
|
|
137
|
-
try {
|
|
138
|
-
// Create audio stop request message
|
|
139
|
-
const message = {
|
|
140
|
-
type: types_1.AppToCloudMessageType.AUDIO_STOP_REQUEST,
|
|
141
|
-
packageName: this.packageName,
|
|
142
|
-
sessionId: this.sessionId,
|
|
143
|
-
timestamp: new Date(),
|
|
144
|
-
};
|
|
145
|
-
// Send request to cloud (one-way, no response expected)
|
|
146
|
-
this.send(message);
|
|
147
|
-
this.logger.info(`๐ Audio stop request sent`);
|
|
148
|
-
}
|
|
149
|
-
catch (error) {
|
|
150
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
151
|
-
this.logger.error(`Failed to stop audio: ${errorMessage}`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* ๐ฃ๏ธ Convert text to speech and play it on the connected glasses
|
|
156
|
-
* @param text - Text to convert to speech (required)
|
|
157
|
-
* @param options - Text-to-speech configuration (optional)
|
|
158
|
-
* @returns Promise that resolves with playback result
|
|
159
|
-
*
|
|
160
|
-
* @example
|
|
161
|
-
* ```typescript
|
|
162
|
-
* // Basic text-to-speech
|
|
163
|
-
* const result = await session.audio.speak('Hello, world!');
|
|
164
|
-
*
|
|
165
|
-
* // With custom voice settings
|
|
166
|
-
* const result = await session.audio.speak('Hello, world!', {
|
|
167
|
-
* voice_id: 'your_voice_id',
|
|
168
|
-
* voice_settings: {
|
|
169
|
-
* stability: 0.5,
|
|
170
|
-
* speed: 1.2
|
|
171
|
-
* },
|
|
172
|
-
* volume: 0.8
|
|
173
|
-
* });
|
|
174
|
-
* ```
|
|
175
|
-
*/
|
|
176
|
-
async speak(text, options = {}) {
|
|
177
|
-
// Validate input
|
|
178
|
-
if (!text) {
|
|
179
|
-
throw new Error("text must be provided");
|
|
180
|
-
}
|
|
181
|
-
// Get the HTTPS server URL from the session
|
|
182
|
-
const baseUrl = this.session?.getHttpsServerUrl?.();
|
|
183
|
-
if (!baseUrl) {
|
|
184
|
-
throw new Error("Cannot determine server URL for TTS endpoint");
|
|
185
|
-
}
|
|
186
|
-
// Build query parameters for the TTS endpoint
|
|
187
|
-
const queryParams = new URLSearchParams();
|
|
188
|
-
queryParams.append("text", text);
|
|
189
|
-
if (options.voice_id) {
|
|
190
|
-
queryParams.append("voice_id", options.voice_id);
|
|
191
|
-
}
|
|
192
|
-
if (options.model_id) {
|
|
193
|
-
queryParams.append("model_id", options.model_id);
|
|
194
|
-
}
|
|
195
|
-
if (options.voice_settings) {
|
|
196
|
-
queryParams.append("voice_settings", JSON.stringify(options.voice_settings));
|
|
197
|
-
}
|
|
198
|
-
// Construct the TTS URL
|
|
199
|
-
const ttsUrl = `${baseUrl}/api/tts?${queryParams.toString()}`;
|
|
200
|
-
this.logger.info({ text, ttsUrl }, `๐ฃ๏ธ Generating speech from text`);
|
|
201
|
-
// Use the existing playAudio method to play the TTS audio
|
|
202
|
-
return this.playAudio({
|
|
203
|
-
audioUrl: ttsUrl,
|
|
204
|
-
volume: options.volume,
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
// =====================================
|
|
208
|
-
// ๐ฅ Response Handling
|
|
209
|
-
// =====================================
|
|
210
|
-
/**
|
|
211
|
-
* ๐ฅ Handle audio play response from cloud
|
|
212
|
-
*
|
|
213
|
-
* This method is called internally when an audio play response is received.
|
|
214
|
-
* It resolves the corresponding pending promise with the response data.
|
|
215
|
-
*
|
|
216
|
-
* @param response - The audio play response received
|
|
217
|
-
* @internal This method is used internally by AppSession
|
|
218
|
-
*/
|
|
219
|
-
handleAudioPlayResponse(response) {
|
|
220
|
-
const pendingRequest = this.pendingAudioRequests.get(response.requestId);
|
|
221
|
-
if (pendingRequest) {
|
|
222
|
-
// Resolve the promise with the response data
|
|
223
|
-
pendingRequest.resolve({
|
|
224
|
-
success: response.success,
|
|
225
|
-
error: response.error,
|
|
226
|
-
duration: response.duration,
|
|
227
|
-
});
|
|
228
|
-
// Clean up
|
|
229
|
-
this.pendingAudioRequests.delete(response.requestId);
|
|
230
|
-
this.logger.info({
|
|
231
|
-
requestId: response.requestId,
|
|
232
|
-
success: response.success,
|
|
233
|
-
duration: response.duration,
|
|
234
|
-
}, `๐ Audio play response received`);
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
this.logger.warn({ requestId: response.requestId }, `๐ Received audio play response for unknown request ID`);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
// =====================================
|
|
241
|
-
// ๐ Status and Management
|
|
242
|
-
// =====================================
|
|
243
|
-
/**
|
|
244
|
-
* ๐ Check if there are pending audio requests
|
|
245
|
-
* @param requestId - Optional specific request ID to check
|
|
246
|
-
* @returns True if there are pending requests (or specific request exists)
|
|
247
|
-
*/
|
|
248
|
-
hasPendingRequest(requestId) {
|
|
249
|
-
if (requestId) {
|
|
250
|
-
return this.pendingAudioRequests.has(requestId);
|
|
251
|
-
}
|
|
252
|
-
return this.pendingAudioRequests.size > 0;
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* ๐ Get the number of pending audio requests
|
|
256
|
-
* @returns Number of pending requests
|
|
257
|
-
*/
|
|
258
|
-
getPendingRequestCount() {
|
|
259
|
-
return this.pendingAudioRequests.size;
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* ๐ Get all pending request IDs
|
|
263
|
-
* @returns Array of pending request IDs
|
|
264
|
-
*/
|
|
265
|
-
getPendingRequestIds() {
|
|
266
|
-
return Array.from(this.pendingAudioRequests.keys());
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* โ Cancel a specific audio request
|
|
270
|
-
* @param requestId - The request ID to cancel
|
|
271
|
-
* @returns True if the request was found and cancelled
|
|
272
|
-
*/
|
|
273
|
-
cancelAudioRequest(requestId) {
|
|
274
|
-
const pendingRequest = this.pendingAudioRequests.get(requestId);
|
|
275
|
-
if (pendingRequest) {
|
|
276
|
-
pendingRequest.reject("Audio request cancelled");
|
|
277
|
-
this.pendingAudioRequests.delete(requestId);
|
|
278
|
-
this.logger.info({ requestId }, `๐ Audio request cancelled`);
|
|
279
|
-
return true;
|
|
280
|
-
}
|
|
281
|
-
return false;
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* ๐งน Cancel all pending audio requests
|
|
285
|
-
* @returns Number of requests that were cancelled
|
|
286
|
-
*/
|
|
287
|
-
cancelAllAudioRequests() {
|
|
288
|
-
const count = this.pendingAudioRequests.size;
|
|
289
|
-
this.pendingAudioRequests.forEach((request, requestId) => {
|
|
290
|
-
request.reject("Audio request cancelled due to cleanup");
|
|
291
|
-
this.logger.debug({ requestId }, `๐ Audio request cancelled during cleanup`);
|
|
292
|
-
});
|
|
293
|
-
this.pendingAudioRequests.clear();
|
|
294
|
-
if (count > 0) {
|
|
295
|
-
this.logger.info({ cancelledCount: count }, `๐งน Cancelled all pending audio requests`);
|
|
296
|
-
}
|
|
297
|
-
return count;
|
|
298
|
-
}
|
|
299
|
-
// =====================================
|
|
300
|
-
// ๐ง Internal Management
|
|
301
|
-
// =====================================
|
|
302
|
-
/**
|
|
303
|
-
* ๐ Update the session ID when reconnecting
|
|
304
|
-
* @param newSessionId - The new session ID
|
|
305
|
-
* @internal Used by AppSession during reconnection
|
|
306
|
-
*/
|
|
307
|
-
updateSessionId(newSessionId) {
|
|
308
|
-
this.sessionId = newSessionId;
|
|
309
|
-
this.logger.debug({ newSessionId }, `๐ Audio module session ID updated`);
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* ๐งน Cancel all pending requests (cleanup)
|
|
313
|
-
* @returns Object with count of cancelled requests
|
|
314
|
-
* @internal Used by AppSession during cleanup
|
|
315
|
-
*/
|
|
316
|
-
cancelAllRequests() {
|
|
317
|
-
const audioRequests = this.cancelAllAudioRequests();
|
|
318
|
-
return { audioRequests };
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
exports.AudioManager = AudioManager;
|