import React, { useEffect, useState } from "react"; import { View, Text, StyleSheet, Alert, BackHandler } from "react-native"; import { NativeStackScreenProps } from "@react-navigation/native-stack"; import { RootStackParamList } from "../App"; import RNFS from "react-native-fs"; import { startDfu, DfuProgressEvent, DfuStateEvent } from "@systemic-games/react-native-nordic-nrf5-dfu"; type Props = NativeStackScreenProps; interface DeviceInfo { hardware: number; latestFirmware: string; download: string; } export default function DfuScreen({ route, navigation }: Props) { const { deviceId, name, firmware: deviceFirmware } = route.params; const [progress, setProgress] = useState(0); const [state, setState] = useState("准备中..."); const [error, setError] = useState(); const [latestVersion, setLatestVersion] = useState("读取中..."); const [isDfuRunning, setIsDfuRunning] = useState(false); const mapDfuStateToChinese = (state: string): string => { switch (state) { case "connecting": return "连接中…"; case "starting": return "初始化中…"; case "enablingDfuMode": return "启用 DFU 模式…"; case "uploading": return "上传固件中…"; case "validating": return "校验固件…"; case "disconnecting": return "断开连接…"; case "completed": return "升级完成"; case "aborted": return "已取消"; case "failed": case "dfu_failed": return "升级失败"; case "initializing": return "启动中…"; case "errored": return "升级出错!"; default: return state; } }; // ✅ 拦截所有导航返回(iOS + Android) useEffect(() => { const unsubscribe = navigation.addListener("beforeRemove", (e) => { if (!isDfuRunning) return; // 阻止返回 e.preventDefault(); Alert.alert("请稍候", "正在升级,请勿返回或关闭应用!"); }); return unsubscribe; }, [navigation, isDfuRunning]); useEffect(() => { const runDfu = async () => { try { setIsDfuRunning(true); const manifestUrl = "https://powerfun.oss-cn-shanghai.aliyuncs.com/yecongdfu/latest.json"; const manifestPath = RNFS.CachesDirectoryPath + "/latest.json"; await RNFS.downloadFile({ fromUrl: manifestUrl, toFile: manifestPath }).promise; const manifestContent = await RNFS.readFile(manifestPath); const manifest = JSON.parse(manifestContent) as { devices: DeviceInfo[] }; const [deviceHWStr, deviceFWStr] = deviceFirmware.split("."); const deviceHW = parseInt(deviceHWStr); const deviceFW = parseInt(deviceFWStr); const deviceInfo = manifest.devices.find(d => d.hardware === deviceHW); if (!deviceInfo) { setIsDfuRunning(false); Alert.alert("无法升级", `未找到硬件版本 ${deviceHW} 的固件`, [ { text: "确认", onPress: () => navigation.goBack() }, ]); return; } setLatestVersion(deviceInfo.latestFirmware); const [, latestFWStr] = deviceInfo.latestFirmware.split("."); const latestFW = parseInt(latestFWStr); if (latestFW <= deviceFW) { setIsDfuRunning(false); Alert.alert("无需升级", "已是最新固件,无需升级", [ { text: "确认", onPress: () => navigation.goBack() }, ]); return; } const localPath = RNFS.CachesDirectoryPath + "/firmware.zip"; await RNFS.downloadFile({ fromUrl: deviceInfo.download, toFile: localPath }).promise; await startDfu(deviceId, "file://" + localPath, { dfuStateListener: (ev: DfuStateEvent) => setState(ev.state), dfuProgressListener: (ev: DfuProgressEvent) => setProgress(ev.percent), }); setIsDfuRunning(false); Alert.alert("升级成功", "升级成功,请重连设备", [ { text: "确认", onPress: () => navigation.navigate("Home") }, ]); } catch (err: any) { setIsDfuRunning(false); setError(err.message || "DFU失败"); Alert.alert("升级失败", err.message || "DFU失败", [ { text: "确认", onPress: () => navigation.goBack() }, ]); } }; runDfu(); }, [deviceId, deviceFirmware, navigation]); return ( 蓝牙名称: {name} 最新版本: {latestVersion} 当前版本: {deviceFirmware} 升级状态: {mapDfuStateToChinese(state)} {/* 横向进度条 */} {progress}% {error && {error}} ); } const styles = StyleSheet.create({ row: { borderBottomWidth: 1, borderBottomColor: "red", paddingBottom: 4, marginBottom: 8, }, progressContainer: { height: 30, backgroundColor: "#eee", borderRadius: 15, overflow: "hidden", marginTop: 40, justifyContent: "center", }, progressBar: { height: "100%", backgroundColor: "#E7141E", }, progressText: { position: "absolute", alignSelf: "center", fontWeight: "bold", color: "#000", }, });