@mentra/engine 3.1.0-dev.4 → 3.1.0-dev.40
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 +19 -5
- package/build/bluetooth-sdk/ota-transport.d.ts +2 -0
- package/build/bluetooth-sdk/ota-transport.d.ts.map +1 -0
- package/build/bluetooth-sdk/ota-transport.js +2 -0
- package/build/bluetooth-sdk/ota-transport.js.map +1 -0
- package/build/engine.d.ts +5 -2
- package/build/engine.d.ts.map +1 -1
- package/build/facades/ota.d.ts +9 -6
- package/build/facades/ota.d.ts.map +1 -1
- package/build/facades/ota.js +6 -4
- package/build/facades/ota.js.map +1 -1
- package/build/facades/pairing.d.ts +4 -0
- package/build/facades/pairing.d.ts.map +1 -1
- package/build/facades/pairing.js +81 -7
- package/build/facades/pairing.js.map +1 -1
- package/build/generated/releaseMetadata.js +5 -5
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js.map +1 -1
- package/build/react/MentraLiveOtaFlow.d.ts +3 -2
- package/build/react/MentraLiveOtaFlow.d.ts.map +1 -1
- package/build/react/MentraLiveOtaFlow.js +113 -352
- package/build/react/MentraLiveOtaFlow.js.map +1 -1
- package/build/react/index.d.ts +1 -0
- package/build/react/index.d.ts.map +1 -1
- package/build/react/index.js +1 -0
- package/build/react/index.js.map +1 -1
- package/build/react/useMentraLiveOta.d.ts +80 -0
- package/build/react/useMentraLiveOta.d.ts.map +1 -0
- package/build/react/useMentraLiveOta.js +550 -0
- package/build/react/useMentraLiveOta.js.map +1 -0
- package/build/services/BlobStore.d.ts +2 -0
- package/build/services/BlobStore.d.ts.map +1 -1
- package/build/services/BlobStore.js +63 -10
- package/build/services/BlobStore.js.map +1 -1
- package/build/services/GlassesStatusProjection.d.ts +15 -2
- package/build/services/GlassesStatusProjection.d.ts.map +1 -1
- package/build/services/GlassesStatusProjection.js +3 -3
- package/build/services/GlassesStatusProjection.js.map +1 -1
- package/build/services/HotspotOtaTransport.d.ts.map +1 -1
- package/build/services/HotspotOtaTransport.js +8 -7
- package/build/services/HotspotOtaTransport.js.map +1 -1
- package/build/services/OtaAutoChain.d.ts +9 -2
- package/build/services/OtaAutoChain.d.ts.map +1 -1
- package/build/services/OtaAutoChain.js +10 -2
- package/build/services/OtaAutoChain.js.map +1 -1
- package/build/services/OtaInstallCoordinator.d.ts +24 -3
- package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
- package/build/services/OtaInstallCoordinator.js +14 -12
- package/build/services/OtaInstallCoordinator.js.map +1 -1
- package/build/services/OtaService.d.ts.map +1 -1
- package/build/services/OtaService.js +1 -1
- package/build/services/OtaService.js.map +1 -1
- package/build/services/OtaUpdateCheckService.d.ts +4 -1
- package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
- package/build/services/OtaUpdateCheckService.js +21 -5
- package/build/services/OtaUpdateCheckService.js.map +1 -1
- package/build/services/asg/localNetworkTransport.d.ts.map +1 -1
- package/build/services/asg/localNetworkTransport.js +13 -12
- package/build/services/asg/localNetworkTransport.js.map +1 -1
- package/build/services/glassesClockSync.js +1 -1
- package/build/services/glassesClockSync.js.map +1 -1
- package/build/services/otaReleaseVersion.d.ts +10 -0
- package/build/services/otaReleaseVersion.d.ts.map +1 -0
- package/build/services/otaReleaseVersion.js +14 -0
- package/build/services/otaReleaseVersion.js.map +1 -0
- package/build/stores/appVisibility.d.ts +3 -0
- package/build/stores/appVisibility.d.ts.map +1 -0
- package/build/stores/appVisibility.js +3 -0
- package/build/stores/appVisibility.js.map +1 -0
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +7 -1
- package/build/stores/apps.js.map +1 -1
- package/build/stores/core.d.ts +3 -3
- package/build/stores/core.d.ts.map +1 -1
- package/build/stores/core.js.map +1 -1
- package/build/stores/glasses.d.ts +10 -4
- package/build/stores/glasses.d.ts.map +1 -1
- package/build/stores/glasses.js.map +1 -1
- package/package.json +12 -6
- package/src/bluetooth-sdk/ota-transport.ts +1 -0
- package/src/facades/ota.ts +16 -5
- package/src/facades/pairing.ts +88 -12
- package/src/generated/releaseMetadata.ts +5 -5
- package/src/index.ts +1 -0
- package/src/react/MentraLiveOtaFlow.tsx +178 -465
- package/src/react/index.ts +14 -0
- package/src/react/useMentraLiveOta.ts +707 -0
- package/src/services/BlobStore.ts +57 -10
- package/src/services/GlassesStatusProjection.ts +7 -6
- package/src/services/HotspotOtaTransport.ts +8 -7
- package/src/services/OtaAutoChain.ts +26 -2
- package/src/services/OtaInstallCoordinator.ts +16 -13
- package/src/services/OtaService.ts +1 -2
- package/src/services/OtaUpdateCheckService.ts +26 -8
- package/src/services/asg/localNetworkTransport.ts +20 -19
- package/src/services/glassesClockSync.ts +1 -1
- package/src/services/otaReleaseVersion.ts +22 -0
- package/src/stores/appVisibility.ts +3 -0
- package/src/stores/apps.ts +7 -1
- package/src/stores/core.ts +4 -4
- package/src/stores/glasses.ts +13 -6
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
1
|
+
/* eslint-disable react-native/no-raw-text -- BodyText and PercentText are local Text wrappers. */
|
|
2
|
+
import React, { useMemo } from "react";
|
|
3
|
+
import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Text, View, } from "react-native";
|
|
3
4
|
import { SafeAreaView } from "react-native-safe-area-context";
|
|
4
5
|
import Svg, { Path, Rect } from "react-native-svg";
|
|
5
|
-
import {
|
|
6
|
-
import { beginOtaAutoChain, clearOtaAutoChainReconnectWait, isOtaAutoChainActive, otaAutoChainFingerprint, otaAutoChainReconnectWaitRemaining, stopOtaAutoChain, tryAdvanceOtaAutoChain, } from "../services/OtaAutoChain";
|
|
7
|
-
import { BES_INSTALL_RESTART_MESSAGE, getOtaErrorMessage, shouldRequireGlassesRebootForBesFailure, shouldShowChangeWifiForOtaDownloadFailure, } from "../services/OtaErrorMapping";
|
|
8
|
-
import { useEngineSnapshot } from "./useEngineSnapshot";
|
|
9
|
-
const AUTO_CHAIN_NETWORK_RETRY_DELAY_MS = 5000;
|
|
10
|
-
const AUTO_CHAIN_COMPLETE_DELAY_MS = 750;
|
|
6
|
+
import { useMentraLiveOta } from "./useMentraLiveOta";
|
|
11
7
|
const DEFAULT_THEME = {
|
|
12
8
|
background: "#FFFFFF",
|
|
13
9
|
border: "#D7DFDA",
|
|
@@ -24,6 +20,9 @@ const ENGLISH_COPY = {
|
|
|
24
20
|
"ota:updateAvailable": "{{deviceName}} Update Available",
|
|
25
21
|
"ota:updateConnectWifi": "Connect your {{deviceName}} to WiFi to install the update.",
|
|
26
22
|
"ota:updateDescription": "A new update is available for your glasses. We recommend updating now for the best experience.",
|
|
23
|
+
"ota:releaseTransition": "{{fromVersion}} → {{toVersion}}",
|
|
24
|
+
"ota:releaseTransitionUnknown": "Current version unknown → {{toVersion}}",
|
|
25
|
+
"ota:updatedToVersion": "Updated to {{version}}",
|
|
27
26
|
"ota:downgradeAvailable": "{{deviceName}} Version Change Required",
|
|
28
27
|
"ota:downgradeDescription": "This app requires an earlier glasses software version. Your photos and videos will be preserved, but glasses settings will be reset and restored automatically after the change.",
|
|
29
28
|
"ota:updateNow": "Update Now",
|
|
@@ -55,403 +54,146 @@ function defaultTranslate(key, options) {
|
|
|
55
54
|
}
|
|
56
55
|
export function MentraLiveOtaFlow({ allowDevSkip = typeof __DEV__ !== "undefined" && __DEV__, deviceName = "Mentra Live", initialPage = "check", initializeRuntime = true, onFinished, onFirmwareRestartingChange, onOpenWifiSetup, style, superMode = false, theme, translate = defaultTranslate, }) {
|
|
57
56
|
const colors = useMemo(() => ({ ...DEFAULT_THEME, ...theme }), [theme]);
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
let cancelled = false;
|
|
67
|
-
setRuntimeReady(false);
|
|
68
|
-
void ota.initialize().finally(() => {
|
|
69
|
-
if (!cancelled)
|
|
70
|
-
setRuntimeReady(true);
|
|
71
|
-
});
|
|
72
|
-
return () => {
|
|
73
|
-
cancelled = true;
|
|
74
|
-
};
|
|
75
|
-
}, [initializeRuntime]);
|
|
76
|
-
const showCheck = useCallback(() => {
|
|
77
|
-
setPage("check");
|
|
78
|
-
setCheckGeneration((generation) => generation + 1);
|
|
79
|
-
}, []);
|
|
57
|
+
const controller = useMentraLiveOta({
|
|
58
|
+
initialPage,
|
|
59
|
+
initializeRuntime,
|
|
60
|
+
onFinished,
|
|
61
|
+
onFirmwareRestartingChange,
|
|
62
|
+
onOpenWifiSetup,
|
|
63
|
+
});
|
|
80
64
|
return (<SafeAreaView style={[styles.safeArea, { backgroundColor: colors.background }, style]}>
|
|
81
65
|
<View style={styles.header}>
|
|
82
66
|
<View />
|
|
83
67
|
<MentraMark color={colors.primary}/>
|
|
84
68
|
</View>
|
|
85
|
-
{
|
|
86
|
-
<ActivityIndicator size="large" color={colors.foreground}/>
|
|
87
|
-
</FlowPage>) : page === "check" ? (<OtaCheckPage allowDevSkip={allowDevSkip} colors={colors} deviceName={deviceName} generation={checkGeneration} onFinished={onFinished} onOpenWifiSetup={onOpenWifiSetup} onStartProgress={() => setPage("progress")} translate={translate}/>) : (<OtaProgressPage colors={colors} onFirmwareRestartingChange={onFirmwareRestartingChange} onOpenWifiSetup={onOpenWifiSetup} onReturnToCheck={showCheck} superMode={superMode} translate={translate}/>)}
|
|
69
|
+
<OtaFlowContent allowDevSkip={allowDevSkip} colors={colors} controller={controller} deviceName={deviceName} superMode={superMode} translate={translate}/>
|
|
88
70
|
</SafeAreaView>);
|
|
89
71
|
}
|
|
90
|
-
function
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const performCheckGenerationRef = useRef(0);
|
|
99
|
-
const activeCheckKeyRef = useRef(null);
|
|
100
|
-
const checkStartedRef = useRef(false);
|
|
101
|
-
const checkCompletedRef = useRef(false);
|
|
102
|
-
const selectedCheckResultRef = useRef(null);
|
|
103
|
-
const onFinishedRef = useRef(onFinished);
|
|
104
|
-
const onStartProgressRef = useRef(onStartProgress);
|
|
105
|
-
onFinishedRef.current = onFinished;
|
|
106
|
-
onStartProgressRef.current = onStartProgress;
|
|
107
|
-
const glassesWifiConnected = otaSnapshot.wifiConnected;
|
|
108
|
-
const glassesWifiStatusKnown = otaSnapshot.wifiStatusKnown;
|
|
109
|
-
const hotspotOtaSupported = otaSnapshot.hotspotOtaVersion === 1;
|
|
110
|
-
const canInstallUpdate = glassesWifiStatusKnown && (glassesWifiConnected || hotspotOtaSupported);
|
|
111
|
-
const checkKey = `${generation}:${retryGeneration}`;
|
|
112
|
-
const navigateToProgress = useCallback(() => {
|
|
113
|
-
ota.clearProgress();
|
|
114
|
-
onStartProgressRef.current();
|
|
115
|
-
}, []);
|
|
116
|
-
useEffect(() => {
|
|
117
|
-
const MIN_DISPLAY_TIME_MS = 1100;
|
|
118
|
-
const MAX_WAIT_FOR_VERSION_INFO_MS = 10_000;
|
|
119
|
-
if (activeCheckKeyRef.current !== checkKey) {
|
|
120
|
-
activeCheckKeyRef.current = checkKey;
|
|
121
|
-
checkStartedRef.current = false;
|
|
122
|
-
checkCompletedRef.current = false;
|
|
123
|
-
setCheckState("checking");
|
|
124
|
-
}
|
|
125
|
-
const myGeneration = ++performCheckGenerationRef.current;
|
|
126
|
-
let cancelled = false;
|
|
127
|
-
let reconnectTimeout = null;
|
|
128
|
-
const performCheck = async () => {
|
|
129
|
-
if (checkCompletedRef.current)
|
|
130
|
-
return;
|
|
131
|
-
if (!otaSnapshot.connected) {
|
|
132
|
-
if (isOtaAutoChainActive()) {
|
|
133
|
-
const remainingMs = otaAutoChainReconnectWaitRemaining();
|
|
134
|
-
if (remainingMs === null)
|
|
135
|
-
return;
|
|
136
|
-
reconnectTimeout = setTimeout(() => {
|
|
137
|
-
if (cancelled || myGeneration !== performCheckGenerationRef.current || ota.snapshot().connected)
|
|
138
|
-
return;
|
|
139
|
-
stopOtaAutoChain();
|
|
140
|
-
checkCompletedRef.current = true;
|
|
141
|
-
setCheckState("error");
|
|
142
|
-
}, remainingMs);
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
if (checkStartedRef.current) {
|
|
146
|
-
stopOtaAutoChain();
|
|
147
|
-
checkCompletedRef.current = true;
|
|
148
|
-
setCheckState("error");
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
checkCompletedRef.current = true;
|
|
152
|
-
onFinishedRef.current();
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
clearOtaAutoChainReconnectWait();
|
|
156
|
-
checkStartedRef.current = true;
|
|
157
|
-
const startTime = Date.now();
|
|
158
|
-
try {
|
|
159
|
-
let result = await ota.checkForUpdates({
|
|
160
|
-
waitForBuildNumberMs: MAX_WAIT_FOR_VERSION_INFO_MS,
|
|
161
|
-
waitForBesVersionMs: 5000,
|
|
162
|
-
waitForMtkVersionMs: 2000,
|
|
163
|
-
refreshVersionInfo: true,
|
|
164
|
-
fixClockBeforeCheck: false,
|
|
165
|
-
});
|
|
166
|
-
if (cancelled || myGeneration !== performCheckGenerationRef.current)
|
|
167
|
-
return;
|
|
168
|
-
if (!result.hasCheckCompleted && result.checkFailureReason === "network" && isOtaAutoChainActive()) {
|
|
169
|
-
await new Promise((resolve) => setTimeout(resolve, AUTO_CHAIN_NETWORK_RETRY_DELAY_MS));
|
|
170
|
-
if (cancelled || myGeneration !== performCheckGenerationRef.current)
|
|
171
|
-
return;
|
|
172
|
-
result = await ota.checkForUpdates({
|
|
173
|
-
waitForBuildNumberMs: MAX_WAIT_FOR_VERSION_INFO_MS,
|
|
174
|
-
waitForBesVersionMs: 5000,
|
|
175
|
-
waitForMtkVersionMs: 2000,
|
|
176
|
-
refreshVersionInfo: true,
|
|
177
|
-
fixClockBeforeCheck: false,
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
if (cancelled || myGeneration !== performCheckGenerationRef.current)
|
|
181
|
-
return;
|
|
182
|
-
selectedCheckResultRef.current = result;
|
|
183
|
-
await new Promise((resolve) => setTimeout(resolve, Math.max(0, MIN_DISPLAY_TIME_MS - (Date.now() - startTime))));
|
|
184
|
-
if (cancelled || myGeneration !== performCheckGenerationRef.current)
|
|
185
|
-
return;
|
|
186
|
-
if (result.skippedReason === "disconnected") {
|
|
187
|
-
stopOtaAutoChain();
|
|
188
|
-
checkCompletedRef.current = true;
|
|
189
|
-
setCheckState("error");
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
if (result.skippedReason === "missing_build") {
|
|
193
|
-
checkCompletedRef.current = true;
|
|
194
|
-
if (isOtaAutoChainActive()) {
|
|
195
|
-
stopOtaAutoChain();
|
|
196
|
-
setCheckState("error");
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
onFinishedRef.current();
|
|
200
|
-
}
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
if (result.skippedReason === "dev_build") {
|
|
204
|
-
stopOtaAutoChain();
|
|
205
|
-
checkCompletedRef.current = true;
|
|
206
|
-
ota.clearUpdateAvailable();
|
|
207
|
-
setCheckState("dev_build");
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if (!result.hasCheckCompleted) {
|
|
211
|
-
stopOtaAutoChain();
|
|
212
|
-
checkCompletedRef.current = true;
|
|
213
|
-
setErrorKind(result.checkFailureReason === "pin_unavailable" ? "pin_unavailable" : "network");
|
|
214
|
-
setCheckState("error");
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
if (result.updateAvailable && result.updateInfo) {
|
|
218
|
-
setIsUpdateRequired(result.isRequired);
|
|
219
|
-
setIsDowngradeUpdate(result.updateInfo.isDowngrade === true);
|
|
220
|
-
const fingerprint = otaAutoChainFingerprint(result);
|
|
221
|
-
setUpdateFingerprint(fingerprint);
|
|
222
|
-
if (isOtaAutoChainActive()) {
|
|
223
|
-
const snapshot = ota.snapshot();
|
|
224
|
-
if (!snapshot.wifiStatusKnown)
|
|
225
|
-
return;
|
|
226
|
-
if (!snapshot.wifiConnected && snapshot.hotspotOtaVersion !== 1) {
|
|
227
|
-
stopOtaAutoChain();
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
const admission = tryAdvanceOtaAutoChain(fingerprint, result.updateInfo.isDowngrade === true);
|
|
231
|
-
if (admission.advance) {
|
|
232
|
-
checkCompletedRef.current = true;
|
|
233
|
-
ota.installSession.prepare(result);
|
|
234
|
-
navigateToProgress();
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
checkCompletedRef.current = true;
|
|
240
|
-
setCheckState("update_available");
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
stopOtaAutoChain();
|
|
244
|
-
checkCompletedRef.current = true;
|
|
245
|
-
ota.clearUpdateAvailable();
|
|
246
|
-
setCheckState("no_update");
|
|
247
|
-
}
|
|
248
|
-
catch (error) {
|
|
249
|
-
console.error("OTA check failed:", error);
|
|
250
|
-
await new Promise((resolve) => setTimeout(resolve, Math.max(0, MIN_DISPLAY_TIME_MS - (Date.now() - startTime))));
|
|
251
|
-
if (cancelled || myGeneration !== performCheckGenerationRef.current)
|
|
252
|
-
return;
|
|
253
|
-
stopOtaAutoChain();
|
|
254
|
-
checkCompletedRef.current = true;
|
|
255
|
-
setCheckState("error");
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
void performCheck();
|
|
259
|
-
return () => {
|
|
260
|
-
cancelled = true;
|
|
261
|
-
if (reconnectTimeout)
|
|
262
|
-
clearTimeout(reconnectTimeout);
|
|
263
|
-
};
|
|
264
|
-
}, [
|
|
265
|
-
checkKey,
|
|
266
|
-
otaSnapshot.connected,
|
|
267
|
-
otaSnapshot.wifiStatusKnown,
|
|
268
|
-
navigateToProgress,
|
|
269
|
-
]);
|
|
270
|
-
const retry = () => {
|
|
271
|
-
setCheckState("checking");
|
|
272
|
-
setRetryGeneration((value) => value + 1);
|
|
273
|
-
};
|
|
274
|
-
const updateNow = () => {
|
|
275
|
-
const result = selectedCheckResultRef.current;
|
|
276
|
-
if (!result) {
|
|
277
|
-
setCheckState("error");
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
const snapshot = ota.snapshot();
|
|
281
|
-
if (!snapshot.wifiStatusKnown) {
|
|
282
|
-
retry();
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
if (!snapshot.wifiConnected && snapshot.hotspotOtaVersion !== 1) {
|
|
286
|
-
onOpenWifiSetup();
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
ota.installSession.prepare(result);
|
|
290
|
-
if (updateFingerprint)
|
|
291
|
-
beginOtaAutoChain(updateFingerprint, isDowngradeUpdate);
|
|
292
|
-
navigateToProgress();
|
|
293
|
-
};
|
|
294
|
-
if (checkState === "checking") {
|
|
72
|
+
function OtaFlowContent({ allowDevSkip, colors, controller, deviceName, superMode, translate, }) {
|
|
73
|
+
const { state } = controller;
|
|
74
|
+
if (state.screen === "initializing") {
|
|
75
|
+
return (<FlowPage colors={colors} icon="download" title={translate("ota:checkingForUpdates")}>
|
|
76
|
+
<ActivityIndicator size="large" color={colors.foreground}/>
|
|
77
|
+
</FlowPage>);
|
|
78
|
+
}
|
|
79
|
+
if (state.screen === "checking") {
|
|
295
80
|
return (<FlowPage colors={colors} icon="download" title={translate("ota:checkingForUpdates")}>
|
|
296
81
|
<BodyText colors={colors}>{translate("ota:checkingForUpdatesMessage")}</BodyText>
|
|
297
82
|
<ActivityIndicator size="large" color={colors.foreground}/>
|
|
298
83
|
</FlowPage>);
|
|
299
84
|
}
|
|
300
|
-
if (
|
|
301
|
-
return (<FlowPage colors={colors} icon="download" title={translate(
|
|
302
|
-
<FlowButton colors={colors} disabled={!
|
|
303
|
-
{
|
|
304
|
-
{allowDevSkip &&
|
|
85
|
+
if (state.screen === "update_available" || state.screen === "wifi_required") {
|
|
86
|
+
return (<FlowPage colors={colors} icon="download" title={translate(state.versionChange ? "ota:downgradeAvailable" : "ota:updateAvailable", { deviceName })} actions={<>
|
|
87
|
+
<FlowButton colors={colors} disabled={!state.wifiStatusKnown} label={translate(state.screen === "wifi_required" ? "ota:setupWifi" : "ota:updateNow")} onPress={state.screen === "wifi_required" ? controller.openWifiSetup : controller.install}/>
|
|
88
|
+
{state.canDismiss ? (<FlowButton colors={colors} label={translate("ota:updateLater")} onPress={controller.finish} secondary/>) : null}
|
|
89
|
+
{allowDevSkip && state.updateRequired ? (<FlowButton colors={colors} label="Skip (dev only)" onPress={controller.finish} secondary/>) : null}
|
|
305
90
|
</>}>
|
|
91
|
+
{state.releaseTransition ? (<BodyText colors={colors}>
|
|
92
|
+
{state.releaseTransition.fromVersion
|
|
93
|
+
? translate("ota:releaseTransition", {
|
|
94
|
+
fromVersion: state.releaseTransition.fromVersion,
|
|
95
|
+
toVersion: state.releaseTransition.toVersion,
|
|
96
|
+
})
|
|
97
|
+
: translate("ota:releaseTransitionUnknown", { toVersion: state.releaseTransition.toVersion })}
|
|
98
|
+
</BodyText>) : null}
|
|
306
99
|
<BodyText colors={colors}>
|
|
307
|
-
{
|
|
308
|
-
? translate(
|
|
309
|
-
: translate("ota:
|
|
100
|
+
{state.screen === "wifi_required"
|
|
101
|
+
? translate("ota:updateConnectWifi", { deviceName })
|
|
102
|
+
: translate(state.versionChange ? "ota:downgradeDescription" : "ota:updateDescription")}
|
|
310
103
|
</BodyText>
|
|
311
104
|
</FlowPage>);
|
|
312
105
|
}
|
|
313
|
-
if (
|
|
314
|
-
return (<FlowPage actions={<FlowButton colors={colors} label={translate("common:continue")} onPress={
|
|
106
|
+
if (state.screen === "dev_build") {
|
|
107
|
+
return (<FlowPage actions={<FlowButton colors={colors} label={translate("common:continue")} onPress={controller.finish}/>} colors={colors} icon="settings" title={translate("ota:devBuild")}>
|
|
315
108
|
<BodyText colors={colors}>{translate("ota:devBuildNoOta")}</BodyText>
|
|
316
109
|
</FlowPage>);
|
|
317
110
|
}
|
|
318
|
-
if (
|
|
319
|
-
return (<FlowPage actions={<FlowButton colors={colors} label={translate("common:continue")} onPress={
|
|
111
|
+
if (state.screen === "up_to_date") {
|
|
112
|
+
return (<FlowPage actions={<FlowButton colors={colors} label={translate("common:continue")} onPress={controller.finish}/>} colors={colors} icon="check" title={translate("ota:upToDate")}>
|
|
320
113
|
<BodyText colors={colors}>{translate("ota:noUpdatesAvailable")}</BodyText>
|
|
114
|
+
{state.releaseTransition ? (<BodyText colors={colors}>
|
|
115
|
+
{translate("ota:updatedToVersion", { version: state.releaseTransition.toVersion })}
|
|
116
|
+
</BodyText>) : null}
|
|
117
|
+
<ChangelogList changelogs={state.changelogs} colors={colors}/>
|
|
321
118
|
</FlowPage>);
|
|
322
119
|
}
|
|
323
|
-
if (
|
|
324
|
-
return (<FlowPage actions={<FlowButton colors={colors} label={translate("common:continue")} onPress={
|
|
120
|
+
if (state.screen === "update_info_unavailable") {
|
|
121
|
+
return (<FlowPage actions={<FlowButton colors={colors} label={translate("common:continue")} onPress={controller.finish}/>} colors={colors} icon="alert" title={translate("ota:updateInfoUnavailable")}>
|
|
325
122
|
<BodyText colors={colors}>{translate("ota:updateInfoUnavailableMessage")}</BodyText>
|
|
326
123
|
</FlowPage>);
|
|
327
124
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
const { connected, continueButtonDisabled, displayState, errorMsg, hotspotArtifactPercent, hotspotPhase, isVersionChange, mtkInstallStallSimulatedPercent, otaProgress, otaStatus, versionChangeConverged, versionChangePhase, } = install;
|
|
339
|
-
const firmwareRestarting = (!connected && displayState === "restarting") || versionChangePhase === "restarting";
|
|
340
|
-
useEffect(() => {
|
|
341
|
-
onFirmwareRestartingChange?.(firmwareRestarting, true);
|
|
342
|
-
}, [firmwareRestarting, onFirmwareRestartingChange]);
|
|
343
|
-
useEffect(() => () => {
|
|
344
|
-
onFirmwareRestartingChange?.(false, false);
|
|
345
|
-
}, [onFirmwareRestartingChange]);
|
|
346
|
-
useEffect(() => {
|
|
347
|
-
ota.installSession.attach();
|
|
348
|
-
return () => ota.installSession.detach();
|
|
349
|
-
}, []);
|
|
350
|
-
useEffect(() => {
|
|
351
|
-
if (displayState !== "complete" || !connected || !isOtaAutoChainActive() || autoChainAdvancedRef.current)
|
|
352
|
-
return;
|
|
353
|
-
const timeout = setTimeout(() => {
|
|
354
|
-
if (!isOtaAutoChainActive())
|
|
355
|
-
return;
|
|
356
|
-
autoChainAdvancedRef.current = true;
|
|
357
|
-
ota.installSession.finish();
|
|
358
|
-
onReturnToCheck();
|
|
359
|
-
}, AUTO_CHAIN_COMPLETE_DELAY_MS);
|
|
360
|
-
return () => clearTimeout(timeout);
|
|
361
|
-
}, [connected, displayState, onReturnToCheck]);
|
|
362
|
-
const finishAndCheck = () => {
|
|
363
|
-
ota.installSession.finish();
|
|
364
|
-
onReturnToCheck();
|
|
365
|
-
};
|
|
366
|
-
const stopAndCheck = () => {
|
|
367
|
-
stopOtaAutoChain();
|
|
368
|
-
finishAndCheck();
|
|
369
|
-
};
|
|
370
|
-
const retryInstall = () => {
|
|
371
|
-
onFirmwareRestartingChange?.(false, true);
|
|
372
|
-
ota.installSession.retry();
|
|
373
|
-
};
|
|
374
|
-
if (versionChangePhase === "restarting" || versionChangePhase === "verifying") {
|
|
375
|
-
return (<FlowPage colors={colors} icon="download" title={translate(versionChangePhase === "verifying" ? "ota:versionChangeVerifying" : "ota:versionChangeRestarting")}>
|
|
125
|
+
if (state.screen === "check_failed") {
|
|
126
|
+
return (<FlowPage actions={<>
|
|
127
|
+
<FlowButton colors={colors} label="Retry" onPress={controller.retryCheck}/>
|
|
128
|
+
{allowDevSkip ? (<FlowButton colors={colors} label="Skip (dev only)" onPress={controller.finish} secondary/>) : null}
|
|
129
|
+
</>} colors={colors} icon="alert" title={translate("ota:checkFailed")}>
|
|
130
|
+
<BodyText colors={colors}>{translate("ota:checkFailedMessage")}</BodyText>
|
|
131
|
+
</FlowPage>);
|
|
132
|
+
}
|
|
133
|
+
if (state.versionChangePhase === "restarting" || state.versionChangePhase === "verifying") {
|
|
134
|
+
return (<FlowPage colors={colors} icon="download" title={translate(state.versionChangePhase === "verifying" ? "ota:versionChangeVerifying" : "ota:versionChangeRestarting")}>
|
|
376
135
|
<ActivityIndicator size="large" color={colors.foreground}/>
|
|
377
136
|
<BodyText colors={colors}>{translate("ota:versionChangeKeepNearby")}</BodyText>
|
|
378
137
|
<BodyText colors={colors}>{translate("ota:downgradeDuration")}</BodyText>
|
|
379
138
|
</FlowPage>);
|
|
380
139
|
}
|
|
381
|
-
if (
|
|
382
|
-
const
|
|
140
|
+
if (state.screen === "starting" || state.screen === "preparing_hotspot") {
|
|
141
|
+
const title = state.hotspotPhase === "downloading"
|
|
383
142
|
? "Downloading update to phone..."
|
|
384
|
-
: hotspotPhase === "starting_hotspot"
|
|
143
|
+
: state.hotspotPhase === "starting_hotspot"
|
|
385
144
|
? "Starting glasses hotspot..."
|
|
386
|
-
: hotspotPhase === "joining_hotspot"
|
|
145
|
+
: state.hotspotPhase === "joining_hotspot"
|
|
387
146
|
? "Connecting phone to glasses..."
|
|
388
147
|
: "Starting update...";
|
|
389
|
-
return (<FlowPage colors={colors} icon="download" title={
|
|
390
|
-
{hotspotPhase === "downloading" && hotspotArtifactPercent !== null ? (<PercentText colors={colors} percent={hotspotArtifactPercent}/>) : null}
|
|
148
|
+
return (<FlowPage colors={colors} icon="download" title={title}>
|
|
149
|
+
{state.hotspotPhase === "downloading" && state.hotspotArtifactPercent !== null ? (<PercentText colors={colors} percent={state.hotspotArtifactPercent}/>) : null}
|
|
391
150
|
<ActivityIndicator size="large" color={colors.foreground}/>
|
|
392
151
|
<BodyText colors={colors}>Do not disconnect your glasses</BodyText>
|
|
393
152
|
</FlowPage>);
|
|
394
153
|
}
|
|
395
|
-
if (
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const rawPercent = isDownload
|
|
400
|
-
? otaStatus?.stepPercent ?? 0
|
|
401
|
-
: totalSteps >= 2
|
|
402
|
-
? otaStatus?.overallPercent ?? 0
|
|
403
|
-
: otaStatus?.stepPercent ?? 0;
|
|
404
|
-
const percent = Math.min(Math.max(rawPercent, mtkInstallStallSimulatedPercent ?? 0, 0), 100);
|
|
405
|
-
return (<FlowPage colors={colors} icon="download" title={isDownload ? "Downloading..." : "Installing..."}>
|
|
406
|
-
{isApkOnlyInstalling ? (<ActivityIndicator size="large" color={colors.foreground}/>) : (<>
|
|
407
|
-
<PercentText colors={colors} percent={percent}/>
|
|
154
|
+
if (state.screen === "updating") {
|
|
155
|
+
return (<FlowPage colors={colors} icon="download" title={state.phase === "download" ? "Downloading..." : "Installing..."}>
|
|
156
|
+
{state.installingApkOnly ? (<ActivityIndicator size="large" color={colors.foreground}/>) : (<>
|
|
157
|
+
<PercentText colors={colors} percent={state.progress ?? 0}/>
|
|
408
158
|
<View style={[styles.progressTrack, { backgroundColor: colors.border }]}>
|
|
409
|
-
<View style={[styles.progressFill, { backgroundColor: colors.primary, width: `${
|
|
159
|
+
<View style={[styles.progressFill, { backgroundColor: colors.primary, width: `${state.progress ?? 0}%` }]}/>
|
|
410
160
|
</View>
|
|
411
161
|
</>)}
|
|
412
162
|
<BodyText colors={colors}>Do not disconnect your glasses</BodyText>
|
|
413
|
-
{
|
|
163
|
+
{state.versionChange && state.phase === "install" ? (<BodyText colors={colors}>{translate("ota:downgradeDuration")}</BodyText>) : null}
|
|
414
164
|
</FlowPage>);
|
|
415
165
|
}
|
|
416
|
-
if (
|
|
417
|
-
return (<FlowPage actions={<FlowButton colors={colors} disabled={
|
|
166
|
+
if (state.screen === "restarting") {
|
|
167
|
+
return (<FlowPage actions={<FlowButton colors={colors} disabled={state.continueDisabled} label="Continue" onPress={controller.finish}/>} colors={colors} icon="check" title="Update Installed"/>);
|
|
418
168
|
}
|
|
419
|
-
if (
|
|
420
|
-
const title = versionChangeConverged
|
|
169
|
+
if (state.screen === "complete") {
|
|
170
|
+
const title = state.versionChangeConverged
|
|
421
171
|
? translate("ota:versionChangeComplete")
|
|
422
|
-
:
|
|
172
|
+
: state.versionChange
|
|
423
173
|
? translate("ota:versionChangeFirmwarePassComplete")
|
|
424
174
|
: "Update complete!";
|
|
425
|
-
const message = versionChangeConverged
|
|
175
|
+
const message = state.versionChangeConverged
|
|
426
176
|
? translate("ota:versionChangeCompleteMessage")
|
|
427
|
-
:
|
|
177
|
+
: state.versionChange
|
|
428
178
|
? translate("ota:versionChangeFirmwarePassCompleteMessage")
|
|
429
179
|
: "Your glasses are up to date.";
|
|
430
|
-
return (<FlowPage actions={<FlowButton colors={colors} label={
|
|
180
|
+
return (<FlowPage actions={<FlowButton colors={colors} label={state.versionChange && !state.versionChangeConverged ? "Continue" : "Done"} onPress={controller.finish}/>} colors={colors} icon="check" title={title}>
|
|
431
181
|
<BodyText colors={colors}>{message}</BodyText>
|
|
182
|
+
{state.releaseTransition ? (<BodyText colors={colors}>
|
|
183
|
+
{translate("ota:updatedToVersion", { version: state.releaseTransition.toVersion })}
|
|
184
|
+
</BodyText>) : null}
|
|
185
|
+
<ChangelogList changelogs={state.changelogs} colors={colors}/>
|
|
432
186
|
</FlowPage>);
|
|
433
187
|
}
|
|
434
|
-
if (
|
|
435
|
-
const requiresGlassesReboot = shouldRequireGlassesRebootForBesFailure(otaStatus, otaProgress, errorMsg);
|
|
436
|
-
const displayedError = requiresGlassesReboot
|
|
437
|
-
? BES_INSTALL_RESTART_MESSAGE
|
|
438
|
-
: errorMsg || getOtaErrorMessage(otaStatus?.error);
|
|
439
|
-
const showChangeWifi = shouldShowChangeWifiForOtaDownloadFailure(otaStatus, otaProgress, errorMsg);
|
|
188
|
+
if (state.screen === "failed") {
|
|
440
189
|
return (<FlowPage actions={<>
|
|
441
|
-
<FlowButton colors={colors} label={
|
|
442
|
-
{
|
|
443
|
-
stopOtaAutoChain();
|
|
444
|
-
onOpenWifiSetup();
|
|
445
|
-
}} secondary/>) : null}
|
|
190
|
+
<FlowButton colors={colors} label={state.canRetry ? "Retry" : "Done"} onPress={state.canRetry ? controller.retryInstall : controller.finish}/>
|
|
191
|
+
{state.canOpenWifiSetup ? (<FlowButton colors={colors} label="Change WiFi" onPress={controller.openWifiSetup} secondary/>) : null}
|
|
446
192
|
</>} colors={colors} icon="alert" title="Update Failed">
|
|
447
|
-
<BodyText colors={colors}>{
|
|
193
|
+
<BodyText colors={colors}>{state.error?.message}</BodyText>
|
|
448
194
|
</FlowPage>);
|
|
449
195
|
}
|
|
450
|
-
return (<FlowPage actions={superMode ? (<FlowButton colors={colors} label="Skip (super)" onPress={
|
|
451
|
-
stopOtaAutoChain();
|
|
452
|
-
ota.installSession.discard();
|
|
453
|
-
onReturnToCheck();
|
|
454
|
-
}} secondary/>) : undefined} colors={colors} icon="bluetooth" title="Glasses disconnected">
|
|
196
|
+
return (<FlowPage actions={superMode ? (<FlowButton colors={colors} label="Skip (super)" onPress={controller.discard} secondary/>) : undefined} colors={colors} icon="bluetooth" title="Glasses disconnected">
|
|
455
197
|
<BodyText colors={colors}>Reconnecting...</BodyText>
|
|
456
198
|
<ActivityIndicator size="large" color={colors.foreground}/>
|
|
457
199
|
</FlowPage>);
|
|
@@ -472,16 +214,30 @@ function BodyText({ children, colors }) {
|
|
|
472
214
|
function PercentText({ colors, percent }) {
|
|
473
215
|
return <Text style={[styles.percent, { color: colors.primary }]}>{Math.round(percent)}%</Text>;
|
|
474
216
|
}
|
|
217
|
+
function ChangelogList({ changelogs, colors, }) {
|
|
218
|
+
if (changelogs.length === 0)
|
|
219
|
+
return null;
|
|
220
|
+
return (<ScrollView contentContainerStyle={styles.changelogContent} style={styles.changelogList}>
|
|
221
|
+
{changelogs.map((entry) => (<View key={entry.version} style={styles.changelogEntry}>
|
|
222
|
+
<Text selectable style={[styles.changelogVersion, { color: colors.foreground }]}>
|
|
223
|
+
{entry.version}
|
|
224
|
+
</Text>
|
|
225
|
+
<Text selectable style={[styles.changelogMarkdown, { color: colors.textDim }]}>
|
|
226
|
+
{entry.markdown}
|
|
227
|
+
</Text>
|
|
228
|
+
</View>))}
|
|
229
|
+
</ScrollView>);
|
|
230
|
+
}
|
|
475
231
|
function FlowButton({ colors, disabled = false, label, onPress, secondary = false, }) {
|
|
476
232
|
return (<Pressable accessibilityRole="button" disabled={disabled} onPress={onPress} style={({ pressed }) => [
|
|
477
233
|
styles.button,
|
|
478
234
|
{
|
|
479
|
-
backgroundColor: secondary ? colors.background : colors.
|
|
480
|
-
borderColor: secondary ? colors.border : colors.
|
|
235
|
+
backgroundColor: secondary ? colors.background : colors.foreground,
|
|
236
|
+
borderColor: secondary ? colors.border : colors.foreground,
|
|
481
237
|
opacity: disabled ? 0.45 : pressed ? 0.75 : 1,
|
|
482
238
|
},
|
|
483
239
|
]} testID={`button-${label}`}>
|
|
484
|
-
<Text style={[styles.buttonText, { color: secondary ? colors.foreground : colors.
|
|
240
|
+
<Text style={[styles.buttonText, { color: secondary ? colors.foreground : colors.background }]}>{label}</Text>
|
|
485
241
|
</Pressable>);
|
|
486
242
|
}
|
|
487
243
|
function MentraMark({ color }) {
|
|
@@ -515,14 +271,19 @@ const styles = StyleSheet.create({
|
|
|
515
271
|
percent: { fontSize: 30, fontVariant: ["tabular-nums"], fontWeight: "700" },
|
|
516
272
|
progressTrack: { borderRadius: 4, height: 8, maxWidth: 420, overflow: "hidden", width: "100%" },
|
|
517
273
|
progressFill: { borderRadius: 4, height: 8 },
|
|
274
|
+
changelogList: { flexShrink: 1, maxHeight: 260, maxWidth: 420, width: "100%" },
|
|
275
|
+
changelogContent: { gap: 18, paddingVertical: 4 },
|
|
276
|
+
changelogEntry: { gap: 8 },
|
|
277
|
+
changelogVersion: { fontSize: 16, fontWeight: "600" },
|
|
278
|
+
changelogMarkdown: { fontSize: 14, lineHeight: 20 },
|
|
518
279
|
button: {
|
|
519
280
|
alignItems: "center",
|
|
520
|
-
borderRadius:
|
|
281
|
+
borderRadius: 50,
|
|
521
282
|
borderWidth: 1,
|
|
522
283
|
justifyContent: "center",
|
|
523
|
-
minHeight:
|
|
284
|
+
minHeight: 44,
|
|
524
285
|
paddingHorizontal: 20,
|
|
525
286
|
},
|
|
526
|
-
buttonText: { fontSize:
|
|
287
|
+
buttonText: { fontSize: 14, fontWeight: "500" },
|
|
527
288
|
});
|
|
528
289
|
//# sourceMappingURL=MentraLiveOtaFlow.js.map
|