@glassly/engine 0.3.2-dev.617 → 0.3.3-beta.633
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/build/facades/glasses.d.ts +1 -0
- package/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +1 -0
- package/build/facades/glasses.js.map +1 -1
- package/build/generated/releaseMetadata.js +4 -4
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/services/GestureDotsService.d.ts +2 -0
- package/build/services/GestureDotsService.d.ts.map +1 -1
- package/build/services/GestureDotsService.js +25 -9
- package/build/services/GestureDotsService.js.map +1 -1
- package/build/services/GlassesTouchInput.d.ts.map +1 -1
- package/build/services/GlassesTouchInput.js +4 -2
- package/build/services/GlassesTouchInput.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +1 -0
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +14 -8
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/MiniappIcons.d.ts +16 -0
- package/build/services/MiniappIcons.d.ts.map +1 -0
- package/build/services/MiniappIcons.js +54 -0
- package/build/services/MiniappIcons.js.map +1 -0
- package/build/services/effectiveCapabilities.d.ts +2 -0
- package/build/services/effectiveCapabilities.d.ts.map +1 -1
- package/build/services/effectiveCapabilities.js +54 -6
- package/build/services/effectiveCapabilities.js.map +1 -1
- package/build/stores/glasses.d.ts.map +1 -1
- package/build/stores/glasses.js +1 -0
- package/build/stores/glasses.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +38 -0
- package/build/stores/settings.js.map +1 -1
- package/build/types/hardware.d.ts +1 -0
- package/build/types/hardware.d.ts.map +1 -1
- package/build/types/hardware.js.map +1 -1
- package/package.json +8 -7
- package/src/facades/glasses.ts +1 -0
- package/src/generated/releaseMetadata.ts +4 -4
- package/src/index.ts +1 -0
- package/src/services/GestureDotsService.ts +20 -7
- package/src/services/GlassesTouchInput.ts +4 -2
- package/src/services/LocalMiniappRuntime.ts +26 -13
- package/src/services/MiniappIcons.ts +50 -0
- package/src/services/effectiveCapabilities.ts +64 -5
- package/src/stores/glasses.ts +1 -0
- package/src/stores/settings.ts +38 -0
- package/src/types/hardware.ts +4 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type IconApp = {
|
|
2
|
+
packageName: string;
|
|
3
|
+
version?: string;
|
|
4
|
+
logoUrl: string;
|
|
5
|
+
};
|
|
6
|
+
/** Small thumbnails keep the inter-miniapp bridge and every grid frame lightweight. */
|
|
7
|
+
declare function thumbnail(uri: string): Promise<string | undefined>;
|
|
8
|
+
/** Cache by installed version and icon URL; failed icons never prevent listing apps. */
|
|
9
|
+
export declare class MiniappIcons {
|
|
10
|
+
private readonly load;
|
|
11
|
+
private cache;
|
|
12
|
+
constructor(load?: typeof thumbnail);
|
|
13
|
+
read(app: IconApp): Promise<string | undefined>;
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=MiniappIcons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MiniappIcons.d.ts","sourceRoot":"","sources":["../../src/services/MiniappIcons.ts"],"names":[],"mappings":"AAGA,KAAK,OAAO,GAAG;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAAA;AAEvE,uFAAuF;AACvF,iBAAe,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAqBjE;AAED,wFAAwF;AACxF,qBAAa,YAAY;IAGX,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,KAAK,CAAuE;gBAEvD,IAAI,mBAAY;IAE7C,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAchD"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { File } from "expo-file-system";
|
|
2
|
+
import { ImageManipulator, SaveFormat } from "expo-image-manipulator";
|
|
3
|
+
/** Small thumbnails keep the inter-miniapp bridge and every grid frame lightweight. */
|
|
4
|
+
async function thumbnail(uri) {
|
|
5
|
+
const context = ImageManipulator.manipulate(uri);
|
|
6
|
+
let image;
|
|
7
|
+
let savedUri;
|
|
8
|
+
try {
|
|
9
|
+
context.resize({ width: 32, height: 32 });
|
|
10
|
+
image = await context.renderAsync();
|
|
11
|
+
const saved = await image.saveAsync({ format: SaveFormat.PNG, base64: true });
|
|
12
|
+
savedUri = saved.uri;
|
|
13
|
+
return saved.base64;
|
|
14
|
+
}
|
|
15
|
+
finally {
|
|
16
|
+
image?.release();
|
|
17
|
+
context.release();
|
|
18
|
+
if (savedUri) {
|
|
19
|
+
try {
|
|
20
|
+
new File(savedUri).delete();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
/* Already removed by cache cleanup. */
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** Cache by installed version and icon URL; failed icons never prevent listing apps. */
|
|
29
|
+
export class MiniappIcons {
|
|
30
|
+
load;
|
|
31
|
+
cache = new Map();
|
|
32
|
+
constructor(load = thumbnail) {
|
|
33
|
+
this.load = load;
|
|
34
|
+
}
|
|
35
|
+
read(app) {
|
|
36
|
+
if (!app.logoUrl)
|
|
37
|
+
return Promise.resolve(undefined);
|
|
38
|
+
const key = JSON.stringify([app.version, app.logoUrl]);
|
|
39
|
+
const cached = this.cache.get(app.packageName);
|
|
40
|
+
if (cached?.key === key)
|
|
41
|
+
return cached.value;
|
|
42
|
+
const value = this.load(app.logoUrl).catch(() => {
|
|
43
|
+
if (this.cache.get(app.packageName)?.value === value)
|
|
44
|
+
this.cache.delete(app.packageName);
|
|
45
|
+
return undefined;
|
|
46
|
+
});
|
|
47
|
+
this.cache.set(app.packageName, { key, value });
|
|
48
|
+
// Bound memory even if apps are repeatedly installed and removed.
|
|
49
|
+
if (this.cache.size > 64)
|
|
50
|
+
this.cache.delete(this.cache.keys().next().value);
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=MiniappIcons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MiniappIcons.js","sourceRoot":"","sources":["../../src/services/MiniappIcons.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAA;AACrC,OAAO,EAAC,gBAAgB,EAAE,UAAU,EAAC,MAAM,wBAAwB,CAAA;AAInE,uFAAuF;AACvF,KAAK,UAAU,SAAS,CAAC,GAAW;IAClC,MAAM,OAAO,GAAG,gBAAgB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;IAChD,IAAI,KAAkE,CAAA;IACtE,IAAI,QAA4B,CAAA;IAChC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,EAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAC,CAAC,CAAA;QACvC,KAAK,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAA;QACnC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,EAAC,MAAM,EAAE,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAC,CAAC,CAAA;QAC3E,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAA;QACpB,OAAO,KAAK,CAAC,MAAM,CAAA;IACrB,CAAC;YAAS,CAAC;QACT,KAAK,EAAE,OAAO,EAAE,CAAA;QAChB,OAAO,CAAC,OAAO,EAAE,CAAA;QACjB,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAA;YAC7B,CAAC;YAAC,MAAM,CAAC;gBACP,uCAAuC;YACzC,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,wFAAwF;AACxF,MAAM,OAAO,YAAY;IAGM;IAFrB,KAAK,GAAG,IAAI,GAAG,EAA6D,CAAA;IAEpF,YAA6B,OAAO,SAAS;QAAhB,SAAI,GAAJ,IAAI,CAAY;IAAG,CAAC;IAEjD,IAAI,CAAC,GAAY;QACf,IAAI,CAAC,GAAG,CAAC,OAAO;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAA;QACtD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QAC9C,IAAI,MAAM,EAAE,GAAG,KAAK,GAAG;YAAE,OAAO,MAAM,CAAC,KAAK,CAAA;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YAC9C,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,KAAK,KAAK,KAAK;gBAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;YACxF,OAAO,SAAS,CAAA;QAClB,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,EAAC,GAAG,EAAE,KAAK,EAAC,CAAC,CAAA;QAC7C,kEAAkE;QAClE,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE;YAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAM,CAAC,CAAA;QAC5E,OAAO,KAAK,CAAA;IACd,CAAC;CACF","sourcesContent":["import {File} from \"expo-file-system\"\nimport {ImageManipulator, SaveFormat} from \"expo-image-manipulator\"\n\ntype IconApp = {packageName: string; version?: string; logoUrl: string}\n\n/** Small thumbnails keep the inter-miniapp bridge and every grid frame lightweight. */\nasync function thumbnail(uri: string): Promise<string | undefined> {\n const context = ImageManipulator.manipulate(uri)\n let image: Awaited<ReturnType<typeof context.renderAsync>> | undefined\n let savedUri: string | undefined\n try {\n context.resize({width: 32, height: 32})\n image = await context.renderAsync()\n const saved = await image.saveAsync({format: SaveFormat.PNG, base64: true})\n savedUri = saved.uri\n return saved.base64\n } finally {\n image?.release()\n context.release()\n if (savedUri) {\n try {\n new File(savedUri).delete()\n } catch {\n /* Already removed by cache cleanup. */\n }\n }\n }\n}\n\n/** Cache by installed version and icon URL; failed icons never prevent listing apps. */\nexport class MiniappIcons {\n private cache = new Map<string, {key: string; value: Promise<string | undefined>}>()\n\n constructor(private readonly load = thumbnail) {}\n\n read(app: IconApp): Promise<string | undefined> {\n if (!app.logoUrl) return Promise.resolve(undefined)\n const key = JSON.stringify([app.version, app.logoUrl])\n const cached = this.cache.get(app.packageName)\n if (cached?.key === key) return cached.value\n const value = this.load(app.logoUrl).catch(() => {\n if (this.cache.get(app.packageName)?.value === value) this.cache.delete(app.packageName)\n return undefined\n })\n this.cache.set(app.packageName, {key, value})\n // Bound memory even if apps are repeatedly installed and removed.\n if (this.cache.size > 64) this.cache.delete(this.cache.keys().next().value!)\n return value\n }\n}\n"]}
|
|
@@ -4,5 +4,7 @@ export interface SceneCanvasSize {
|
|
|
4
4
|
height: number;
|
|
5
5
|
}
|
|
6
6
|
export declare function runtimeSceneCanvas(): SceneCanvasSize | null;
|
|
7
|
+
/** The connected firmware gates microphone streaming on volume (G2 Glassly CFW revision 40+). */
|
|
8
|
+
export declare function runtimeVolumeGate(): boolean;
|
|
7
9
|
export declare function getEffectiveCapabilities(model: DeviceTypes): Capabilities;
|
|
8
10
|
//# sourceMappingURL=effectiveCapabilities.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effectiveCapabilities.d.ts","sourceRoot":"","sources":["../../src/services/effectiveCapabilities.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"effectiveCapabilities.d.ts","sourceRoot":"","sources":["../../src/services/effectiveCapabilities.ts"],"names":[],"mappings":"AAoBA,OAAO,EAEL,WAAW,EAGX,KAAK,YAAY,EAClB,MAAM,UAAU,CAAA;AAQjB,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,IAAI,CAI3D;AAMD,iGAAiG;AACjG,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAEzE"}
|
|
@@ -11,11 +11,16 @@
|
|
|
11
11
|
* lifted alongside the canvas, bounded only to keep frames sane. Firmware
|
|
12
12
|
* that draws vector shapes keeps 128 scene slots, so shape-heavy frames
|
|
13
13
|
* (a game board, a chart) get a larger shared text/shape budget.
|
|
14
|
+
*
|
|
15
|
+
* A connected controller that swipes in two dimensions (the Meta Neural Band)
|
|
16
|
+
* adds its surface to `button.buttons` as a `swipe2d` entry, so a miniapp can
|
|
17
|
+
* tell four-way swipes from the temples' and the R1 ring's up/down.
|
|
14
18
|
*/
|
|
15
19
|
import { useGlassesStore } from "../stores/glasses";
|
|
16
20
|
import { SETTINGS, useSettingsStore } from "../stores/settings";
|
|
17
|
-
import { ALL_DISPLAY_SHAPES, getModelCapabilities } from "../types";
|
|
21
|
+
import { ALL_DISPLAY_SHAPES, getModelCapabilities, } from "../types";
|
|
18
22
|
import { resolveNativeNavigation } from "./nativeNavigationGate";
|
|
23
|
+
import { input2dFrom } from "./settingsBridge";
|
|
19
24
|
const CFW_MAX_TEXT_ELEMENTS = 24;
|
|
20
25
|
const CFW_MAX_IMAGE_ELEMENTS = 16;
|
|
21
26
|
const CFW_VECTOR_MAX_ELEMENTS = 120;
|
|
@@ -28,18 +33,28 @@ export function runtimeSceneCanvas() {
|
|
|
28
33
|
function forceNativeNavigationOnCfw() {
|
|
29
34
|
return useSettingsStore.getState().getSetting(SETTINGS.cfw_native_navigation.key) === true;
|
|
30
35
|
}
|
|
36
|
+
/** The connected firmware gates microphone streaming on volume (G2 Glassly CFW revision 40+). */
|
|
37
|
+
export function runtimeVolumeGate() {
|
|
38
|
+
return useGlassesStore.getState().volumeGate === true;
|
|
39
|
+
}
|
|
31
40
|
export function getEffectiveCapabilities(model) {
|
|
41
|
+
return withControllerInput(glassesCapabilities(model));
|
|
42
|
+
}
|
|
43
|
+
function glassesCapabilities(model) {
|
|
32
44
|
const base = getModelCapabilities(model);
|
|
33
|
-
// Only models with a Glassly CFW publish a canvas
|
|
34
|
-
// reshape another model.
|
|
45
|
+
// Only models with a Glassly CFW publish a canvas or a volume gate; never
|
|
46
|
+
// let a stale value reshape another model.
|
|
35
47
|
const canvas = base.hasCustomFirmware ? runtimeSceneCanvas() : null;
|
|
36
|
-
|
|
37
|
-
|
|
48
|
+
const volumeGate = base.hasCustomFirmware && base.hasMicrophone && runtimeVolumeGate();
|
|
49
|
+
if (!canvas)
|
|
50
|
+
return volumeGate ? withVolumeGate(base) : base;
|
|
51
|
+
if (!base.display)
|
|
52
|
+
return volumeGate ? withVolumeGate(base) : base;
|
|
38
53
|
// Glassly CFW supports the complete vector contract, including SVG and rotation.
|
|
39
54
|
// Upstream g2flash publishes a raster canvas with sceneVector=false.
|
|
40
55
|
const vector = useGlassesStore.getState().sceneVector === true;
|
|
41
56
|
return {
|
|
42
|
-
...base,
|
|
57
|
+
...(volumeGate ? withVolumeGate(base) : base),
|
|
43
58
|
hasNativeNavigation: resolveNativeNavigation(base.hasNativeNavigation, true, forceNativeNavigationOnCfw()),
|
|
44
59
|
display: {
|
|
45
60
|
...base.display,
|
|
@@ -54,4 +69,37 @@ export function getEffectiveCapabilities(model) {
|
|
|
54
69
|
},
|
|
55
70
|
};
|
|
56
71
|
}
|
|
72
|
+
/** The Neural Band's thumb swipes, pinches and dial. */
|
|
73
|
+
const NEURAL_BAND_SURFACE = {
|
|
74
|
+
type: "swipe2d",
|
|
75
|
+
events: [
|
|
76
|
+
"TAP",
|
|
77
|
+
"DOUBLE_TAP",
|
|
78
|
+
"PRESS_HOLD",
|
|
79
|
+
"SWIPE_UP",
|
|
80
|
+
"SWIPE_DOWN",
|
|
81
|
+
"SWIPE_LEFT",
|
|
82
|
+
"SWIPE_RIGHT",
|
|
83
|
+
"DIAL_UP",
|
|
84
|
+
"DIAL_DOWN",
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
function withControllerInput(caps) {
|
|
88
|
+
const controller = useSettingsStore.getState().getSetting(SETTINGS.default_controller.key);
|
|
89
|
+
if (!input2dFrom(useGlassesStore.getState(), controller))
|
|
90
|
+
return caps;
|
|
91
|
+
const buttons = caps.button?.buttons ?? [];
|
|
92
|
+
return {
|
|
93
|
+
...caps,
|
|
94
|
+
hasButton: true,
|
|
95
|
+
button: {
|
|
96
|
+
...caps.button,
|
|
97
|
+
count: (caps.button?.count ?? buttons.length) + 1,
|
|
98
|
+
buttons: [...buttons, NEURAL_BAND_SURFACE],
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function withVolumeGate(base) {
|
|
103
|
+
return { ...base, microphone: { ...(base.microphone ?? {}), volumeGate: true } };
|
|
104
|
+
}
|
|
57
105
|
//# sourceMappingURL=effectiveCapabilities.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effectiveCapabilities.js","sourceRoot":"","sources":["../../src/services/effectiveCapabilities.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"effectiveCapabilities.js","sourceRoot":"","sources":["../../src/services/effectiveCapabilities.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Effective device capabilities — the static model profile plus what the\n * connected firmware reports at runtime. The runtime override is the scene\n * canvas: Glassly CFW on the G2 rasterizes scenes phone-side straight into\n * the 640×480 panel, so the drawable canvas grows past stock's 576×288; on\n * the RayNeo iO it opens a 540×180 page that stock firmware has no\n * equivalent of. Any model whose profile declares `hasCustomFirmware` reads it.\n * Native publishes the canvas into the glasses status (0 = none). Because\n * that path is phone-side rasterization, the stock firmware's container\n * pools (6 text / 4 image) don't bound a CFW frame either — element caps are\n * lifted alongside the canvas, bounded only to keep frames sane. Firmware\n * that draws vector shapes keeps 128 scene slots, so shape-heavy frames\n * (a game board, a chart) get a larger shared text/shape budget.\n *\n * A connected controller that swipes in two dimensions (the Meta Neural Band)\n * adds its surface to `button.buttons` as a `swipe2d` entry, so a miniapp can\n * tell four-way swipes from the temples' and the R1 ring's up/down.\n */\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {SETTINGS, useSettingsStore} from \"../stores/settings\"\nimport {\n ALL_DISPLAY_SHAPES,\n DeviceTypes,\n getModelCapabilities,\n type ButtonCapabilities,\n type Capabilities,\n} from \"../types\"\nimport {resolveNativeNavigation} from \"./nativeNavigationGate\"\nimport {input2dFrom} from \"./settingsBridge\"\n\nconst CFW_MAX_TEXT_ELEMENTS = 24\nconst CFW_MAX_IMAGE_ELEMENTS = 16\nconst CFW_VECTOR_MAX_ELEMENTS = 120\n\nexport interface SceneCanvasSize {\n width: number\n height: number\n}\n\nexport function runtimeSceneCanvas(): SceneCanvasSize | null {\n const {sceneCanvasWidth, sceneCanvasHeight} = useGlassesStore.getState()\n if (!sceneCanvasWidth || !sceneCanvasHeight || sceneCanvasWidth <= 0 || sceneCanvasHeight <= 0) return null\n return {width: sceneCanvasWidth, height: sceneCanvasHeight}\n}\n\nfunction forceNativeNavigationOnCfw(): boolean {\n return useSettingsStore.getState().getSetting(SETTINGS.cfw_native_navigation.key) === true\n}\n\n/** The connected firmware gates microphone streaming on volume (G2 Glassly CFW revision 40+). */\nexport function runtimeVolumeGate(): boolean {\n return useGlassesStore.getState().volumeGate === true\n}\n\nexport function getEffectiveCapabilities(model: DeviceTypes): Capabilities {\n return withControllerInput(glassesCapabilities(model))\n}\n\nfunction glassesCapabilities(model: DeviceTypes): Capabilities {\n const base = getModelCapabilities(model)\n // Only models with a Glassly CFW publish a canvas or a volume gate; never\n // let a stale value reshape another model.\n const canvas = base.hasCustomFirmware ? runtimeSceneCanvas() : null\n const volumeGate = base.hasCustomFirmware && base.hasMicrophone && runtimeVolumeGate()\n if (!canvas) return volumeGate ? withVolumeGate(base) : base\n if (!base.display) return volumeGate ? withVolumeGate(base) : base\n // Glassly CFW supports the complete vector contract, including SVG and rotation.\n // Upstream g2flash publishes a raster canvas with sceneVector=false.\n const vector = useGlassesStore.getState().sceneVector === true\n return {\n ...(volumeGate ? withVolumeGate(base) : base),\n hasNativeNavigation: resolveNativeNavigation(base.hasNativeNavigation, true, forceNativeNavigationOnCfw()),\n display: {\n ...base.display,\n width: canvas.width,\n height: canvas.height,\n maxTextElements: Math.max(\n base.display.maxTextElements ?? 0,\n vector ? CFW_VECTOR_MAX_ELEMENTS : CFW_MAX_TEXT_ELEMENTS,\n ),\n // Vector firmware draws an image as one cached slot, the same as a shape.\n maxImageElements: Math.max(\n base.display.maxImageElements ?? 0,\n vector ? CFW_VECTOR_MAX_ELEMENTS : CFW_MAX_IMAGE_ELEMENTS,\n ),\n ...(vector\n ? {shapes: [...ALL_DISPLAY_SHAPES, \"svg\" as const], animation: true, rotation: true, depth: true}\n : {}),\n },\n }\n}\n\ntype InputSurface = NonNullable<ButtonCapabilities[\"buttons\"]>[number]\n\n/** The Neural Band's thumb swipes, pinches and dial. */\nconst NEURAL_BAND_SURFACE: InputSurface = {\n type: \"swipe2d\",\n events: [\n \"TAP\",\n \"DOUBLE_TAP\",\n \"PRESS_HOLD\",\n \"SWIPE_UP\",\n \"SWIPE_DOWN\",\n \"SWIPE_LEFT\",\n \"SWIPE_RIGHT\",\n \"DIAL_UP\",\n \"DIAL_DOWN\",\n ],\n}\n\nfunction withControllerInput(caps: Capabilities): Capabilities {\n const controller = useSettingsStore.getState().getSetting(SETTINGS.default_controller.key)\n if (!input2dFrom(useGlassesStore.getState(), controller)) return caps\n const buttons = caps.button?.buttons ?? []\n return {\n ...caps,\n hasButton: true,\n button: {\n ...caps.button,\n count: (caps.button?.count ?? buttons.length) + 1,\n buttons: [...buttons, NEURAL_BAND_SURFACE],\n },\n }\n}\n\nfunction withVolumeGate(base: Capabilities): Capabilities {\n return {...base, microphone: {...(base.microphone ?? {}), volumeGate: true}}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glasses.d.ts","sourceRoot":"","sources":["../../src/stores/glasses.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EAEvB,WAAW,EACX,SAAS,EACT,aAAa,EACb,mBAAmB,EAEpB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAC,kBAAkB,EAAE,sBAAsB,EAAE,cAAc,EAAC,MAAM,8BAA8B,CAAA;AAQvG,OAAO,EAAC,kBAAkB,EAAE,sBAAsB,EAAE,cAAc,EAAC,CAAA;AAEnE,eAAO,MAAM,sBAAsB,GAAI,OAAO;IAAC,UAAU,EAAE,uBAAuB,CAAA;CAAC,YAC7C,CAAA;AAEtC,eAAO,MAAM,kBAAkB,GAAI,OAAO;IAAC,UAAU,EAAE,uBAAuB,CAAA;CAAC,YAAqC,CAAA;AAEpH,KAAK,mBAAmB,GAAG,mBAAmB,GAAG;IAC/C,kBAAkB,EAAE,aAAa,GAAG,IAAI,CAAA;IACxC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAA;IAC/B,aAAa,EAAE,OAAO,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,MAAM,WAAW,YAAa,SAAQ,mBAAmB;IACvD,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,OAAO,CAAA;IACxB,cAAc,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACjD,cAAc,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,KAAK,IAAI,CAAA;IAClH,WAAW,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACvD,cAAc,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAEtF,SAAS,EAAE,SAAS,GAAG,IAAI,CAAA;IAC3B,YAAY,EAAE,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAA;IAChD,qBAAqB,EAAE,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,KAAK,IAAI,CAAA;IAC3D,cAAc,EAAE,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAA;IACtD,gBAAgB,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAA;IAC/C,wBAAwB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACpD,aAAa,EAAE,MAAM,IAAI,CAAA;IACzB,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,qBAAqB,EAAE,OAAO,CAAA;CAC/B;AAED,KAAK,gBAAgB,GAAG;IACtB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,KAAK,mBAAmB,GAAG;IACzB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,KAAK,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC,GAAG,gBAAgB,GAAG,mBAAmB,CAAA;AA2B9F,eAAO,MAAM,oBAAoB,GAAI,OAAO,mBAAmB;;;;;;;;;;CAgB9D,CAAA;
|
|
1
|
+
{"version":3,"file":"glasses.d.ts","sourceRoot":"","sources":["../../src/stores/glasses.ts"],"names":[],"mappings":"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"}
|
package/build/stores/glasses.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glasses.js","sourceRoot":"","sources":["../../src/stores/glasses.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n GlassesConnectionStatus,\n HotspotStatus,\n OtaProgress,\n OtaStatus,\n OtaUpdateInfo,\n PublicGlassesStatus,\n WifiStatus,\n} from \"@glassly/bluetooth-sdk\"\nimport {isGlassesConnected, isGlassesLinkLayerBusy, isGlassesReady} from \"../services/GlassesReadiness\"\nimport {create} from \"zustand\"\nimport {subscribeWithSelector} from \"zustand/middleware\"\n\nimport {BgTimer} from \"../utils/timers\"\n\n// Re-export the connection predicates (single source of truth lives in\n// GlassesReadiness) so importers of this store keep getting them.\nexport {isGlassesConnected, isGlassesLinkLayerBusy, isGlassesReady}\n\nexport const selectGlassesConnected = (state: {connection: GlassesConnectionStatus}) =>\n isGlassesConnected(state.connection)\n\nexport const selectGlassesReady = (state: {connection: GlassesConnectionStatus}) => isGlassesReady(state.connection)\n\ntype EngineGlassesStatus = PublicGlassesStatus & {\n otaUpdateAvailable: OtaUpdateInfo | null\n otaProgress: OtaProgress | null\n otaInProgress: boolean\n otaVersionUrl: string\n}\n\nexport interface GlassesState extends EngineGlassesStatus {\n systemTimeMs: number\n wifiStatusKnown: boolean\n setGlassesInfo: (info: GlassesInfoUpdate) => void\n setBatteryInfo: (batteryLevel: number, charging: boolean, caseBatteryLevel: number, caseCharging: boolean) => void\n setWifiInfo: (connected: boolean, ssid: string) => void\n setHotspotInfo: (enabled: boolean, ssid: string, password: string, ip: string) => void\n // OTA methods\n otaStatus: OtaStatus | null\n setOtaStatus: (status: OtaStatus | null) => void\n setOtaUpdateAvailable: (info: OtaUpdateInfo | null) => void\n setOtaProgress: (progress: OtaProgress | null) => void\n setOtaInProgress: (inProgress: boolean) => void\n setMtkUpdatedThisSession: (updated: boolean) => void\n clearOtaState: () => void\n reset: () => void\n mtkUpdatedThisSession: boolean\n}\n\ntype LegacyWifiFields = {\n wifiConnected?: boolean\n wifiSsid?: string\n wifiLocalIp?: string\n}\n\ntype LegacyHotspotFields = {\n hotspotEnabled?: boolean\n hotspotSsid?: string\n hotspotPassword?: string\n hotspotGatewayIp?: string\n hotspotLocalIp?: string\n}\n\ntype GlassesInfoUpdate = Partial<EngineGlassesStatus> & LegacyWifiFields & LegacyHotspotFields\n\nfunction wifiFromLegacyFields(info: LegacyWifiFields): WifiStatus | null {\n if (info.wifiConnected === true) {\n const ssid = info.wifiSsid?.trim()\n const localIp = info.wifiLocalIp?.trim()\n return ssid ? {state: \"connected\", ssid, ...(localIp ? {localIp} : {})} : null\n }\n if (info.wifiConnected === false) {\n return {state: \"disconnected\"}\n }\n return null\n}\n\nfunction hotspotFromLegacyFields(info: LegacyHotspotFields): HotspotStatus | null {\n if (info.hotspotEnabled === true) {\n const ssid = info.hotspotSsid?.trim()\n const password = info.hotspotPassword?.trim()\n const localIp = (info.hotspotGatewayIp ?? info.hotspotLocalIp)?.trim()\n return ssid && password && localIp ? {state: \"enabled\", ssid, password, localIp} : null\n }\n if (info.hotspotEnabled === false) {\n return {state: \"disabled\"}\n }\n return null\n}\n\nexport const getGlasesInfoPartial = (state: EngineGlassesStatus) => {\n const wifi = state.wifi\n const connected = isGlassesConnected(state.connection)\n return {\n batteryLevel: state.batteryLevel,\n charging: state.charging,\n caseBatteryLevel: state.caseBatteryLevel,\n caseCharging: state.caseCharging,\n connected,\n wifiConnected: wifi.state === \"connected\",\n wifiSsid: wifi.state === \"connected\" ? wifi.ssid : \"\",\n deviceModel: state.deviceModel,\n // Cloud GlassesInfo uses modelName, map from deviceModel so the cloud\n // knows which device is connected when it receives connection state updates\n modelName: state.deviceModel || null,\n }\n}\n\ninterface GlassesStore extends EngineGlassesStatus {\n systemTimeMs: number\n mtkUpdatedThisSession: boolean\n wifiStatusKnown: boolean\n otaStatus: OtaStatus | null\n}\n\nconst initialState: GlassesStore = {\n // state:\n connection: {state: \"disconnected\"},\n micEnabled: false,\n bluetoothClassicConnected: false,\n signalStrength: -1,\n signalStrengthUpdatedAt: 0,\n voiceActivityDetectionEnabled: true,\n // device info\n deviceModel: \"\",\n androidVersion: \"\",\n firmwareVersion: \"\",\n bluetoothMacAddress: \"\",\n wifiMacAddress: \"\",\n leftMacAddress: \"\",\n rightMacAddress: \"\",\n buildNumber: \"\",\n systemTimeMs: 0,\n otaVersionUrl: \"\",\n appVersion: \"\",\n bluetoothName: \"\",\n serialNumber: \"\",\n style: \"\",\n color: \"\",\n mtkFirmwareVersion: \"\",\n besFirmwareVersion: \"\",\n hotspotOtaVersion: 0,\n sceneCanvasWidth: 0,\n sceneCanvasHeight: 0,\n sceneVector: false,\n sceneSettle: false,\n // wifi info\n wifi: {state: \"disconnected\"},\n wifiStatusKnown: false,\n // battery info\n batteryLevel: -1,\n charging: false,\n ringConnected: false,\n ringBatteryLevel: -1,\n ringCharging: false,\n caseBatteryLevel: -1,\n caseCharging: false,\n caseOpen: false,\n caseRemoved: true,\n // hotspot info\n hotspot: {state: \"disabled\"},\n // OTA update info\n otaStatus: null,\n otaUpdateAvailable: null,\n otaProgress: null,\n otaInProgress: false,\n mtkUpdatedThisSession: false,\n // ring:\n controllerConnected: false,\n controllerFullyBooted: false,\n controllerMacAddress: \"\",\n controllerBatteryLevel: -1,\n controllerSignalStrength: -1,\n // iOS ANCS consent (G2 consumes notifications on-firmware):\n ancsAuthorized: false,\n // G2 Glassly CFW keyboard bridge (revision 39+):\n keyboardBridge: false,\n keyboardConnected: false,\n keyboardName: \"\",\n keyboardSaved: false,\n keyboardState: \"idle\",\n keyboardError: \"\",\n}\n\nexport const useGlassesStore = create<GlassesState>()(\n subscribeWithSelector((set) => ({\n ...initialState,\n\n setGlassesInfo: (info) =>\n set((state) => {\n const {\n wifiConnected,\n wifiSsid,\n wifiLocalIp,\n hotspotEnabled,\n hotspotSsid,\n hotspotPassword,\n hotspotGatewayIp,\n hotspotLocalIp,\n wifi,\n hotspot,\n ...sdkInfo\n } = info\n const wifiUpdate = wifi ?? wifiFromLegacyFields({wifiConnected, wifiSsid, wifiLocalIp})\n const hotspotUpdate =\n hotspot ??\n hotspotFromLegacyFields({hotspotEnabled, hotspotSsid, hotspotPassword, hotspotGatewayIp, hotspotLocalIp})\n const hasWifiInfoUpdate =\n Object.prototype.hasOwnProperty.call(info, \"wifi\") ||\n Object.prototype.hasOwnProperty.call(info, \"wifiConnected\") ||\n Object.prototype.hasOwnProperty.call(info, \"wifiSsid\") ||\n Object.prototype.hasOwnProperty.call(info, \"wifiLocalIp\")\n const next = {\n ...state,\n ...sdkInfo,\n ...(wifiUpdate ? {wifi: wifiUpdate} : {}),\n ...(hotspotUpdate ? {hotspot: hotspotUpdate} : {}),\n ...(hasWifiInfoUpdate ? {wifiStatusKnown: true} : {}),\n }\n if (!isGlassesConnected(next.connection)) {\n next.wifiStatusKnown = false\n next.hotspotOtaVersion = 0\n }\n return next\n }),\n\n setBatteryInfo: (batteryLevel, charging, caseBatteryLevel, caseCharging) =>\n set({\n batteryLevel,\n charging,\n caseBatteryLevel,\n caseCharging,\n }),\n\n setWifiInfo: (connected, ssid) =>\n set(() => {\n const trimmedSsid = ssid.trim()\n if (connected && !trimmedSsid) {\n return {}\n }\n const wifi: WifiStatus = connected ? {state: \"connected\", ssid: trimmedSsid} : {state: \"disconnected\"}\n return {\n wifi,\n wifiStatusKnown: true,\n }\n }),\n\n setHotspotInfo: (enabled: boolean, ssid: string, password: string, ip: string) =>\n set(() => {\n const hotspot = hotspotFromLegacyFields({\n hotspotEnabled: enabled,\n hotspotSsid: ssid,\n hotspotPassword: password,\n hotspotGatewayIp: ip,\n })\n return hotspot ? {hotspot} : {}\n }),\n\n // OTA methods\n setOtaStatus: (status: OtaStatus | null) => set({otaStatus: status}),\n\n setOtaUpdateAvailable: (info: OtaUpdateInfo | null) => set({otaUpdateAvailable: info}),\n\n setOtaProgress: (progress: OtaProgress | null) =>\n set((state) => {\n const otaInProgress = progress !== null && progress.status !== \"FINISHED\" && progress.status !== \"FAILED\"\n console.log(\"🔍 GLASSES STORE: setOtaProgress called with:\", JSON.stringify(progress))\n console.log(\"🔍 GLASSES STORE: otaInProgress =\", otaInProgress)\n\n // Never allow progress to regress within the same stage+currentUpdate — except a new\n // work wave (STARTED) or the step after FINISHED (multi-hop APK: 27→31→36 re-downloads from 0).\n const prev = state.otaProgress\n const sameWave =\n progress &&\n prev &&\n progress.stage === prev.stage &&\n progress.currentUpdate === prev.currentUpdate &&\n progress.progress < prev.progress\n if (sameWave) {\n const nextIsNewWave = progress.status === \"STARTED\" || prev.status === \"FINISHED\"\n if (!nextIsNewWave) {\n return {otaProgress: {...progress, progress: prev.progress}, otaInProgress}\n }\n }\n\n return {otaProgress: progress, otaInProgress}\n }),\n\n setOtaInProgress: (inProgress: boolean) => set({otaInProgress: inProgress}),\n\n setMtkUpdatedThisSession: (updated: boolean) => set({mtkUpdatedThisSession: updated}),\n\n clearOtaState: () =>\n set({\n otaUpdateAvailable: null,\n otaProgress: null,\n otaInProgress: false,\n // Note: mtkUpdatedThisSession is NOT cleared here - it stays true until glasses disconnect/reboot\n }),\n\n reset: () => set(initialState),\n })),\n)\n\nexport function getGlassesSystemTimeMs(): number {\n return useGlassesStore.getState().systemTimeMs ?? 0\n}\n\nexport const waitForGlassesState = <K extends keyof GlassesState>(\n key: K,\n predicate: (value: GlassesState[K]) => boolean,\n timeoutMs = 1000,\n): Promise<boolean> => {\n return new Promise((resolve) => {\n const state = useGlassesStore.getState()\n if (predicate(state[key])) {\n resolve(true)\n return\n }\n\n const unsubscribe = useGlassesStore.subscribe(\n (s) => s[key],\n (value) => {\n if (predicate(value)) {\n unsubscribe()\n resolve(true)\n }\n },\n )\n\n BgTimer.setTimeout(() => {\n unsubscribe()\n resolve(predicate(useGlassesStore.getState()[key]))\n }, timeoutMs)\n })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"glasses.js","sourceRoot":"","sources":["../../src/stores/glasses.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n GlassesConnectionStatus,\n HotspotStatus,\n OtaProgress,\n OtaStatus,\n OtaUpdateInfo,\n PublicGlassesStatus,\n WifiStatus,\n} from \"@glassly/bluetooth-sdk\"\nimport {isGlassesConnected, isGlassesLinkLayerBusy, isGlassesReady} from \"../services/GlassesReadiness\"\nimport {create} from \"zustand\"\nimport {subscribeWithSelector} from \"zustand/middleware\"\n\nimport {BgTimer} from \"../utils/timers\"\n\n// Re-export the connection predicates (single source of truth lives in\n// GlassesReadiness) so importers of this store keep getting them.\nexport {isGlassesConnected, isGlassesLinkLayerBusy, isGlassesReady}\n\nexport const selectGlassesConnected = (state: {connection: GlassesConnectionStatus}) =>\n isGlassesConnected(state.connection)\n\nexport const selectGlassesReady = (state: {connection: GlassesConnectionStatus}) => isGlassesReady(state.connection)\n\ntype EngineGlassesStatus = PublicGlassesStatus & {\n otaUpdateAvailable: OtaUpdateInfo | null\n otaProgress: OtaProgress | null\n otaInProgress: boolean\n otaVersionUrl: string\n}\n\nexport interface GlassesState extends EngineGlassesStatus {\n systemTimeMs: number\n wifiStatusKnown: boolean\n setGlassesInfo: (info: GlassesInfoUpdate) => void\n setBatteryInfo: (batteryLevel: number, charging: boolean, caseBatteryLevel: number, caseCharging: boolean) => void\n setWifiInfo: (connected: boolean, ssid: string) => void\n setHotspotInfo: (enabled: boolean, ssid: string, password: string, ip: string) => void\n // OTA methods\n otaStatus: OtaStatus | null\n setOtaStatus: (status: OtaStatus | null) => void\n setOtaUpdateAvailable: (info: OtaUpdateInfo | null) => void\n setOtaProgress: (progress: OtaProgress | null) => void\n setOtaInProgress: (inProgress: boolean) => void\n setMtkUpdatedThisSession: (updated: boolean) => void\n clearOtaState: () => void\n reset: () => void\n mtkUpdatedThisSession: boolean\n}\n\ntype LegacyWifiFields = {\n wifiConnected?: boolean\n wifiSsid?: string\n wifiLocalIp?: string\n}\n\ntype LegacyHotspotFields = {\n hotspotEnabled?: boolean\n hotspotSsid?: string\n hotspotPassword?: string\n hotspotGatewayIp?: string\n hotspotLocalIp?: string\n}\n\ntype GlassesInfoUpdate = Partial<EngineGlassesStatus> & LegacyWifiFields & LegacyHotspotFields\n\nfunction wifiFromLegacyFields(info: LegacyWifiFields): WifiStatus | null {\n if (info.wifiConnected === true) {\n const ssid = info.wifiSsid?.trim()\n const localIp = info.wifiLocalIp?.trim()\n return ssid ? {state: \"connected\", ssid, ...(localIp ? {localIp} : {})} : null\n }\n if (info.wifiConnected === false) {\n return {state: \"disconnected\"}\n }\n return null\n}\n\nfunction hotspotFromLegacyFields(info: LegacyHotspotFields): HotspotStatus | null {\n if (info.hotspotEnabled === true) {\n const ssid = info.hotspotSsid?.trim()\n const password = info.hotspotPassword?.trim()\n const localIp = (info.hotspotGatewayIp ?? info.hotspotLocalIp)?.trim()\n return ssid && password && localIp ? {state: \"enabled\", ssid, password, localIp} : null\n }\n if (info.hotspotEnabled === false) {\n return {state: \"disabled\"}\n }\n return null\n}\n\nexport const getGlasesInfoPartial = (state: EngineGlassesStatus) => {\n const wifi = state.wifi\n const connected = isGlassesConnected(state.connection)\n return {\n batteryLevel: state.batteryLevel,\n charging: state.charging,\n caseBatteryLevel: state.caseBatteryLevel,\n caseCharging: state.caseCharging,\n connected,\n wifiConnected: wifi.state === \"connected\",\n wifiSsid: wifi.state === \"connected\" ? wifi.ssid : \"\",\n deviceModel: state.deviceModel,\n // Cloud GlassesInfo uses modelName, map from deviceModel so the cloud\n // knows which device is connected when it receives connection state updates\n modelName: state.deviceModel || null,\n }\n}\n\ninterface GlassesStore extends EngineGlassesStatus {\n systemTimeMs: number\n mtkUpdatedThisSession: boolean\n wifiStatusKnown: boolean\n otaStatus: OtaStatus | null\n}\n\nconst initialState: GlassesStore = {\n // state:\n connection: {state: \"disconnected\"},\n micEnabled: false,\n bluetoothClassicConnected: false,\n signalStrength: -1,\n signalStrengthUpdatedAt: 0,\n voiceActivityDetectionEnabled: true,\n // device info\n deviceModel: \"\",\n androidVersion: \"\",\n firmwareVersion: \"\",\n bluetoothMacAddress: \"\",\n wifiMacAddress: \"\",\n leftMacAddress: \"\",\n rightMacAddress: \"\",\n buildNumber: \"\",\n systemTimeMs: 0,\n otaVersionUrl: \"\",\n appVersion: \"\",\n bluetoothName: \"\",\n serialNumber: \"\",\n style: \"\",\n color: \"\",\n mtkFirmwareVersion: \"\",\n besFirmwareVersion: \"\",\n hotspotOtaVersion: 0,\n sceneCanvasWidth: 0,\n sceneCanvasHeight: 0,\n sceneVector: false,\n sceneSettle: false,\n volumeGate: false,\n // wifi info\n wifi: {state: \"disconnected\"},\n wifiStatusKnown: false,\n // battery info\n batteryLevel: -1,\n charging: false,\n ringConnected: false,\n ringBatteryLevel: -1,\n ringCharging: false,\n caseBatteryLevel: -1,\n caseCharging: false,\n caseOpen: false,\n caseRemoved: true,\n // hotspot info\n hotspot: {state: \"disabled\"},\n // OTA update info\n otaStatus: null,\n otaUpdateAvailable: null,\n otaProgress: null,\n otaInProgress: false,\n mtkUpdatedThisSession: false,\n // ring:\n controllerConnected: false,\n controllerFullyBooted: false,\n controllerMacAddress: \"\",\n controllerBatteryLevel: -1,\n controllerSignalStrength: -1,\n // iOS ANCS consent (G2 consumes notifications on-firmware):\n ancsAuthorized: false,\n // G2 Glassly CFW keyboard bridge (revision 39+):\n keyboardBridge: false,\n keyboardConnected: false,\n keyboardName: \"\",\n keyboardSaved: false,\n keyboardState: \"idle\",\n keyboardError: \"\",\n}\n\nexport const useGlassesStore = create<GlassesState>()(\n subscribeWithSelector((set) => ({\n ...initialState,\n\n setGlassesInfo: (info) =>\n set((state) => {\n const {\n wifiConnected,\n wifiSsid,\n wifiLocalIp,\n hotspotEnabled,\n hotspotSsid,\n hotspotPassword,\n hotspotGatewayIp,\n hotspotLocalIp,\n wifi,\n hotspot,\n ...sdkInfo\n } = info\n const wifiUpdate = wifi ?? wifiFromLegacyFields({wifiConnected, wifiSsid, wifiLocalIp})\n const hotspotUpdate =\n hotspot ??\n hotspotFromLegacyFields({hotspotEnabled, hotspotSsid, hotspotPassword, hotspotGatewayIp, hotspotLocalIp})\n const hasWifiInfoUpdate =\n Object.prototype.hasOwnProperty.call(info, \"wifi\") ||\n Object.prototype.hasOwnProperty.call(info, \"wifiConnected\") ||\n Object.prototype.hasOwnProperty.call(info, \"wifiSsid\") ||\n Object.prototype.hasOwnProperty.call(info, \"wifiLocalIp\")\n const next = {\n ...state,\n ...sdkInfo,\n ...(wifiUpdate ? {wifi: wifiUpdate} : {}),\n ...(hotspotUpdate ? {hotspot: hotspotUpdate} : {}),\n ...(hasWifiInfoUpdate ? {wifiStatusKnown: true} : {}),\n }\n if (!isGlassesConnected(next.connection)) {\n next.wifiStatusKnown = false\n next.hotspotOtaVersion = 0\n }\n return next\n }),\n\n setBatteryInfo: (batteryLevel, charging, caseBatteryLevel, caseCharging) =>\n set({\n batteryLevel,\n charging,\n caseBatteryLevel,\n caseCharging,\n }),\n\n setWifiInfo: (connected, ssid) =>\n set(() => {\n const trimmedSsid = ssid.trim()\n if (connected && !trimmedSsid) {\n return {}\n }\n const wifi: WifiStatus = connected ? {state: \"connected\", ssid: trimmedSsid} : {state: \"disconnected\"}\n return {\n wifi,\n wifiStatusKnown: true,\n }\n }),\n\n setHotspotInfo: (enabled: boolean, ssid: string, password: string, ip: string) =>\n set(() => {\n const hotspot = hotspotFromLegacyFields({\n hotspotEnabled: enabled,\n hotspotSsid: ssid,\n hotspotPassword: password,\n hotspotGatewayIp: ip,\n })\n return hotspot ? {hotspot} : {}\n }),\n\n // OTA methods\n setOtaStatus: (status: OtaStatus | null) => set({otaStatus: status}),\n\n setOtaUpdateAvailable: (info: OtaUpdateInfo | null) => set({otaUpdateAvailable: info}),\n\n setOtaProgress: (progress: OtaProgress | null) =>\n set((state) => {\n const otaInProgress = progress !== null && progress.status !== \"FINISHED\" && progress.status !== \"FAILED\"\n console.log(\"🔍 GLASSES STORE: setOtaProgress called with:\", JSON.stringify(progress))\n console.log(\"🔍 GLASSES STORE: otaInProgress =\", otaInProgress)\n\n // Never allow progress to regress within the same stage+currentUpdate — except a new\n // work wave (STARTED) or the step after FINISHED (multi-hop APK: 27→31→36 re-downloads from 0).\n const prev = state.otaProgress\n const sameWave =\n progress &&\n prev &&\n progress.stage === prev.stage &&\n progress.currentUpdate === prev.currentUpdate &&\n progress.progress < prev.progress\n if (sameWave) {\n const nextIsNewWave = progress.status === \"STARTED\" || prev.status === \"FINISHED\"\n if (!nextIsNewWave) {\n return {otaProgress: {...progress, progress: prev.progress}, otaInProgress}\n }\n }\n\n return {otaProgress: progress, otaInProgress}\n }),\n\n setOtaInProgress: (inProgress: boolean) => set({otaInProgress: inProgress}),\n\n setMtkUpdatedThisSession: (updated: boolean) => set({mtkUpdatedThisSession: updated}),\n\n clearOtaState: () =>\n set({\n otaUpdateAvailable: null,\n otaProgress: null,\n otaInProgress: false,\n // Note: mtkUpdatedThisSession is NOT cleared here - it stays true until glasses disconnect/reboot\n }),\n\n reset: () => set(initialState),\n })),\n)\n\nexport function getGlassesSystemTimeMs(): number {\n return useGlassesStore.getState().systemTimeMs ?? 0\n}\n\nexport const waitForGlassesState = <K extends keyof GlassesState>(\n key: K,\n predicate: (value: GlassesState[K]) => boolean,\n timeoutMs = 1000,\n): Promise<boolean> => {\n return new Promise((resolve) => {\n const state = useGlassesStore.getState()\n if (predicate(state[key])) {\n resolve(true)\n return\n }\n\n const unsubscribe = useGlassesStore.subscribe(\n (s) => s[key],\n (value) => {\n if (predicate(value)) {\n unsubscribe()\n resolve(true)\n }\n },\n )\n\n BgTimer.setTimeout(() => {\n unsubscribe()\n resolve(predicate(useGlassesStore.getState()[key]))\n }, timeoutMs)\n })\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/stores/settings.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAwB,MAAM,aAAa,CAAA;AAQ9D,OAAO,EAAC,wBAAwB,EAAE,gCAAgC,EAAC,MAAM,wBAAwB,CAAA;AAEjG,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,MAAM,GAAG,CAAA;IACvB,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,OAAO,CAAA;IAGrB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAEjC,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAA;IAEpB,OAAO,EAAE,OAAO,CAAA;IAQhB,mBAAmB,CAAC,EAAE,IAAI,CAAA;IAC1B;;;;;;;;;;;;;;;OAeG;IACH,qBAAqB,CAAC,EAAE,IAAI,CAAA;CAC7B;AAKD,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/stores/settings.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAwB,MAAM,aAAa,CAAA;AAQ9D,OAAO,EAAC,wBAAwB,EAAE,gCAAgC,EAAC,MAAM,wBAAwB,CAAA;AAEjG,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,MAAM,GAAG,CAAA;IACvB,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,OAAO,CAAA;IAGrB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAEjC,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAA;IAEpB,OAAO,EAAE,OAAO,CAAA;IAQhB,mBAAmB,CAAC,EAAE,IAAI,CAAA;IAC1B;;;;;;;;;;;;;;;OAeG;IACH,qBAAqB,CAAC,EAAE,IAAI,CAAA;CAC7B;AAKD,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAqjCnC,CAAA;AAEV,eAAO,MAAM,eAAe,EAAE,MAAM,EAAuD,CAAA;AAI3F,eAAO,MAAM,sBAAsB,EAAE,MAAM,EAyD1C,CAAA;AAeD,eAAO,MAAM,qBAAqB,EAAE,MAAM,EAEV,CAAA;AAIhC,MAAM,WAAW,aAAa;IAE5B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAE7B,aAAa,EAAE,OAAO,CAAA;IAEtB,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,EAAE,OAAO,KAAK,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACzF,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC3E,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,GAAG,CAAA;IAEhC,eAAe,EAAE,MAAM,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAE/C,oBAAoB,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC/C,uBAAuB,EAAE,MAAM,IAAI,CAAA;CACpC;AAoBD,eAAO,MAAM,gBAAgB;;;;;;;;EA+U5B,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,GAAG,GAAG,EAAE,KAAK,MAAM,KAAG,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAI3F,CAAA"}
|
package/build/stores/settings.js
CHANGED
|
@@ -567,6 +567,40 @@ export const SETTINGS = {
|
|
|
567
567
|
saveOnServer: true,
|
|
568
568
|
persist: true,
|
|
569
569
|
},
|
|
570
|
+
// G2 Glassly CFW microphone volume gate (revision 40+): the temples stop
|
|
571
|
+
// streaming audio while nothing reaches the threshold. Off until the user
|
|
572
|
+
// turns it on; shown only when the connected firmware reports the capability.
|
|
573
|
+
volume_gate_enabled: {
|
|
574
|
+
key: "volume_gate_enabled",
|
|
575
|
+
defaultValue: () => false,
|
|
576
|
+
writable: true,
|
|
577
|
+
saveOnServer: true,
|
|
578
|
+
persist: true,
|
|
579
|
+
},
|
|
580
|
+
// dBFS RMS a 50 ms chunk must reach to count as sound.
|
|
581
|
+
volume_gate_threshold_db: {
|
|
582
|
+
key: "volume_gate_threshold_db",
|
|
583
|
+
defaultValue: () => -45,
|
|
584
|
+
writable: true,
|
|
585
|
+
saveOnServer: true,
|
|
586
|
+
persist: true,
|
|
587
|
+
},
|
|
588
|
+
// Quiet time before the gate closes.
|
|
589
|
+
volume_gate_hold_ms: {
|
|
590
|
+
key: "volume_gate_hold_ms",
|
|
591
|
+
defaultValue: () => 3000,
|
|
592
|
+
writable: true,
|
|
593
|
+
saveOnServer: true,
|
|
594
|
+
persist: true,
|
|
595
|
+
},
|
|
596
|
+
// Minimum time the gate stays open once sound opens it.
|
|
597
|
+
volume_gate_min_open_ms: {
|
|
598
|
+
key: "volume_gate_min_open_ms",
|
|
599
|
+
defaultValue: () => 2000,
|
|
600
|
+
writable: true,
|
|
601
|
+
saveOnServer: true,
|
|
602
|
+
persist: true,
|
|
603
|
+
},
|
|
570
604
|
always_on_status_bar: {
|
|
571
605
|
key: "always_on_status_bar",
|
|
572
606
|
defaultValue: () => false,
|
|
@@ -1061,6 +1095,10 @@ export const BLUETOOTH_SETTING_KEYS = [
|
|
|
1061
1095
|
SETTINGS.power_saving_mode.key,
|
|
1062
1096
|
SETTINGS.voice_activity_detection_enabled.key,
|
|
1063
1097
|
SETTINGS.loudness_gate_enabled.key,
|
|
1098
|
+
SETTINGS.volume_gate_enabled.key,
|
|
1099
|
+
SETTINGS.volume_gate_threshold_db.key,
|
|
1100
|
+
SETTINGS.volume_gate_hold_ms.key,
|
|
1101
|
+
SETTINGS.volume_gate_min_open_ms.key,
|
|
1064
1102
|
SETTINGS.lc3_frame_size.key,
|
|
1065
1103
|
SETTINGS.preferred_mic.key,
|
|
1066
1104
|
SETTINGS.screen_disabled.key,
|