123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- import 'dart:async';
- import 'dart:io';
- import 'package:dio/dio.dart';
- import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
- import 'package:get_it/get_it.dart';
- import 'package:nordic_dfu/nordic_dfu.dart';
- import 'package:path_provider/path_provider.dart';
- import 'package:sport/bean/hardware.dart';
- import 'package:sport/provider/bluetooth.dart';
- import 'package:sport/services/app_subscription_state.dart';
- import 'package:sport/utils/toast.dart';
- import 'package:sport/widgets/button_primary.dart';
- import 'package:umeng_common_sdk/umeng_common_sdk.dart';
- import 'package:wakelock/wakelock.dart';
- class DfuUpdatePage extends StatefulWidget {
- final Hardware hardware;
- final String version;
- const DfuUpdatePage({Key? key, required this.hardware,required this.version}) : super(key: key);
- @override
- State<StatefulWidget> createState() => _PateState();
- }
- class _PateState extends State<DfuUpdatePage> with SubscriptionState{
- ValueNotifier<String> _msg = ValueNotifier<String>("请稍候...");
- ValueNotifier<bool> _close = ValueNotifier<bool>(false);
- ValueNotifier<String> _title = ValueNotifier<String>("升级设备");
- StreamSubscription? scanSubscription;
- double _progress = .0;
- bool _error = false;
- late Bluetooth bluetooth;
- @override
- void initState() {
- super.initState();
- bluetooth = GetIt.I<Bluetooth>();
- final context = this.context;
- final deviceId = bluetooth.deviceId;
- if (deviceId == null) {
- ToastUtil.show("请先连接鞋子!!");
- Navigator.of(context).pop();
- return;
- }
- if (bluetooth.electricityNotifier.value < 10) {
- ToastUtil.showBottom("鞋子电量不足,请确保电量10%以上!");
- Navigator.of(context).pop();
- return;
- }
- Wakelock.enable();
- _update(deviceId).then((value) async {
- bluetooth.addErr("0", "DFU升级成功", "${widget.version}");
- _msg.value = "正在重启鞋子...";
- }).catchError((error){
- bluetooth.addErr("100", "DFU升级失败", "$error");
- print("bluetooth -- dfu -- error $error");
- _msg.value = "升级失败!!";
- }).whenComplete(() async {
- if (!_error) {
- ToastUtil.show("升级成功");
- }else{
- ToastUtil.show("升级失败!!");
- }
- setState(() {
- _error = false;
- });
- bluetooth.dfu = false;
- if(!bluetooth.isConnected) {
- bluetooth.connectDevice(id: deviceId);
- }
- Navigator.of(context).pop(true);
- await Future.delayed(Duration(seconds: 15));
- _close.value = true;
- });
- UmengCommonSdk.onEvent("shoe_dfu", {});
- }
- @override
- void dispose() {
- bluetooth.dfu = false;
- Wakelock.disable();
- super.dispose();
- scanSubscription?.cancel();
- }
- Future<String?> _update(String deviceId) async {
- List<String> list = widget.hardware.devices ?? [];
- _msg.value = "下载固件...";
- var file = await _download(widget.hardware.file!);
- if (file == null) {
- ToastUtil.show("下载固件失败!!");
- Navigator.of(context).pop();
- return null;
- }
- print("bluetooth -- dfu -- $list");
- print("bluetooth -- dfu -- start device $deviceId");
- bluetooth.dfu = true;
- for (var i = 0; i < list.length; i++) {
- String mac = list[i];
- int type = list.length - i - 1;
- _title.value = "升级设备(${i + 1}/${list.length})";
- print("bluetooth -- dfu -- file $file");
- try {
- _msg.value = "正在连接设备($mac)...";
- if (i == 0) {
- bool setup = false;
- _msg.value = "正在断开右鞋连接...";
- for (var i = 0; i < 2; i++) {
- print("bluetooth -- dfu setupDeviceVer start $i");
- setup = await bluetooth.setupDeviceVer(type);
- if (setup == true) {
- _msg.value = "已断开右鞋连接...";
- break;
- }
- }
- if (setup != true) {
- throw TimeoutException("发送指令超时");
- }
- String name = "${mac.replaceAll(":", "").toUpperCase()}";
- _msg.value = "开始搜索$name";
- if (Platform.isIOS) {
- DiscoveredDevice? device = await bluetooth.discoverDevice(deviceName: name);
- if (device == null) {
- _msg.value = "搜索超时";
- throw TimeoutException("搜索超时$name");
- }
- await bluetooth.disconnectDevice("dfu升级", clearGatt: true);
- await doDfu(device.id, file, i);
- } else {
- await bluetooth.disconnectDevice("dfu升级", clearGatt: true);
- await doDfu(mac, file, i);
- }
- } else {
- await doDfu(deviceId, file, i);
- }
- } catch (e) {
- print(e);
- _msg.value = "升级失败: ${e.toString()}";
- Future.delayed(Duration(seconds: 3)).then((value) => _close.value = true);
- throw e;
- }
- }
- return deviceId;
- }
- Future<String?> _download(String url) async {
- Directory dir = await getTemporaryDirectory();
- File file = File("${dir.path}/${url.hashCode}.zip");
- if (!await file.exists()) {
- file.createSync();
- }
- for (var i = 0; i < 3; i++) {
- try {
- var resp = await GetIt.I<Dio>().download(url, file.path, deleteOnError: true, onReceiveProgress: (index, total) => _msg.value = "下载中 (${index ~/ total * 100}%)...", options: CacheOptions(store: null, policy: CachePolicy.noCache).toOptions());
- if (resp.data != null) return file.path;
- } catch (e) {
- print(e);
- }
- }
- return null;
- }
- Future<String?> doDfu(String deviceId, String filePath, int index) async {
- _msg.value = "正在准备升级服务...";
- await Future.delayed(Duration(seconds: 3));
- // String label = index == 0 ? "右鞋" : "左鞋";
- return await NordicDfu().startDfu(
- deviceId,
- filePath,
- fileInAsset: false,
- onEnablingDfuMode: (deviceAddress) {
- _msg.value = "准备升级($deviceAddress)...";
- },
- onDfuProcessStarted: (deviceAddress) {
- _msg.value = "准备升级($deviceAddress)...";
- },
- onDfuProcessStarting: (deviceAddress) {
- _msg.value = "准备升级($deviceAddress)...";
- },
- onDeviceConnecting: (deviceAddress) {
- _msg.value = "准备升级($deviceAddress)...";
- },
- onDeviceConnected: (deviceAddress) {
- _msg.value = "准备升级($deviceAddress)";
- },
- onDfuCompleted: (deviceAddress) {
- _msg.value = "升级完成($deviceAddress)";
- },
- onFirmwareValidating: (deviceAddress) {
- _msg.value = "正在校验($deviceAddress)...";
- },
- onProgressChanged: (
- deviceAddress,
- percent,
- speed,
- avgSpeed,
- currentPart,
- partsTotal,
- ) {
- print('deviceAddress: $deviceAddress, percent: $percent');
- double progress = (percent / 200) + index * .5;
- setState(() {
- _progress = progress;
- });
- _msg.value = "升级中 (${(progress * 100).toInt()}%)...";
- },
- onError: (
- String? deviceAddress,
- int? error,
- int? errorType,
- String? message,
- ) {
- throw Exception("$deviceAddress, $error $errorType, $message");
- },
- );
- }
- @override
- Widget build(BuildContext context) {
- return WillPopScope(
- onWillPop: () async {
- return _error;
- },
- child: Padding(
- padding: const EdgeInsets.all(12.0),
- child: Column(
- children: <Widget>[
- ValueListenableBuilder(
- valueListenable: _title,
- builder: (BuildContext context, String value, Widget? child) => Text(
- value,
- style: Theme.of(context).textTheme.headline3,
- )),
- Padding(
- padding: const EdgeInsets.fromLTRB(15.0, 40.0, 15.0, 15.0),
- child: SizedBox(
- height: 5,
- child: LinearProgressIndicator(
- value: _progress <= 0 ? null : _progress,
- backgroundColor: const Color(0xfff1f1f1),
- )),
- ),
- ValueListenableBuilder(
- valueListenable: _msg,
- builder: (BuildContext context, String value, Widget? child) => Text(
- value,
- style: Theme.of(context).textTheme.bodyText1,
- )),
- ValueListenableBuilder(
- valueListenable: _close,
- builder: (BuildContext context, bool value, Widget? child) => value
- ? Container(
- width: 140,
- margin: EdgeInsets.only(top: 12.0),
- child: PrimaryButton(
- callback: () {
- Navigator.pop(context, false);
- },
- content: "关闭"),
- )
- : Container())
- ],
- ),
- ),
- );
- }
- }
|