device_info_page.dart 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. import 'dart:math';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
  5. import 'package:get_it/get_it.dart';
  6. import 'package:provider/provider.dart';
  7. import 'package:shared_preferences/shared_preferences.dart';
  8. import 'package:sport/application.dart';
  9. import 'package:sport/bean/hardware.dart';
  10. import 'package:sport/pages/home/guide_page.dart';
  11. import 'package:sport/pages/my/dfu_update_page.dart';
  12. import 'package:sport/pages/web/data_test_page.dart';
  13. import 'package:sport/pages/web/game_test_page.dart';
  14. import 'package:sport/provider/bluetooth.dart';
  15. import 'package:sport/provider/user_model.dart';
  16. import 'package:sport/router/navigator_util.dart';
  17. import 'package:sport/services/Converter.dart';
  18. import 'package:sport/utils/toast.dart';
  19. import 'package:sport/widgets/appbar.dart';
  20. import 'package:sport/widgets/button_primary.dart';
  21. import 'package:sport/widgets/decoration.dart';
  22. import 'package:sport/widgets/dialog/alert_dialog.dart';
  23. import 'package:sport/widgets/dialog/request_dialog.dart';
  24. import 'package:sport/widgets/dialog/search_device.dart';
  25. import 'package:sport/widgets/image.dart';
  26. import 'package:sport/widgets/loading.dart';
  27. import 'package:umeng_common_sdk/umeng_common_sdk.dart';
  28. class DeviceInfoPage extends StatefulWidget {
  29. @override
  30. State<StatefulWidget> createState() => _PageState();
  31. }
  32. class _PageState extends State<DeviceInfoPage> {
  33. var labelPadding = const EdgeInsets.symmetric(horizontal: 12.0, vertical: 10.0);
  34. var contentPadding = const EdgeInsets.symmetric(horizontal: 0.0);
  35. late Bluetooth _bluetooth;
  36. @override
  37. void initState() {
  38. super.initState();
  39. _bluetooth = GetIt.I<Bluetooth>();
  40. _bluetooth.queryDeviceInfo();
  41. _bluetooth.stepTotalTestNotifier.value = 1;
  42. UmengCommonSdk.onEvent("shoe_detail", {});
  43. //
  44. // ByteDataReader r = ByteDataReader();
  45. // r.add([0,0,1,128,33,184,201,128]);
  46. // int i = r.readUint64();
  47. // print("1111111111 ${i } ${DateTime.fromMillisecondsSinceEpoch(i)}");
  48. // r = ByteDataReader();
  49. // r.add([0,0,1,128,33,19,254,0]);
  50. // i = r.readUint64();
  51. // print("1111111111 ${i } ${DateTime.fromMillisecondsSinceEpoch(i)}");
  52. // r = ByteDataReader();
  53. // r.add([0,0,1,128,27,182,179,128 ]);
  54. // print("1111111111 ${r.readUint64()}");
  55. // r = ByteDataReader();
  56. // r.add([0,0,84,135]);
  57. // print("1111111111 ${r.readUint32()}");
  58. // r = ByteDataReader();
  59. // r.add([0,0,84,136]);
  60. // print("1111111111 ${r.readUint32()}");
  61. }
  62. @override
  63. void dispose() {
  64. _bluetooth.stepTotalTestNotifier.value = 0;
  65. super.dispose();
  66. }
  67. @override
  68. Widget build(BuildContext context) {
  69. var _padding = EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0);
  70. return Scaffold(
  71. appBar: buildAppBar(context, backgroundColor: Colors.transparent, brightness: Brightness.light, actions: [
  72. TextButton(
  73. onPressed: () {
  74. NavigatorUtil.goPage(context, (context) => GuidePage());
  75. },
  76. child: Row(
  77. children: [
  78. Image.asset("lib/assets/img/mine_icon_novice.png"),
  79. const SizedBox(
  80. width: 6,
  81. ),
  82. Text(
  83. "新手指引",
  84. style: Theme.of(context).textTheme.subtitle1,
  85. ),
  86. const SizedBox(
  87. width: 6,
  88. ),
  89. ],
  90. )),
  91. ]),
  92. body: ValueListenableBuilder(
  93. valueListenable: GetIt.I<Bluetooth>().deviceNotifier,
  94. builder: (BuildContext context, DiscoveredDevice? d, Widget? child) {
  95. if (d == null) return RequestLoadingWidget();
  96. final device = d;
  97. return CustomScrollView(
  98. slivers: <Widget>[
  99. SliverToBoxAdapter(
  100. child: Padding(
  101. padding: const EdgeInsets.all(16.0),
  102. child: Column(
  103. children: [
  104. Padding(
  105. padding: const EdgeInsets.fromLTRB(0, 20, 0, 30.0),
  106. child: ValueListenableBuilder<String>(
  107. valueListenable: GetIt.I<Bluetooth>().deviceNameNotifier,
  108. builder: (context, data, _) {
  109. return Row(
  110. mainAxisSize: MainAxisSize.min,
  111. children: [
  112. SizedBox(
  113. width: 32,
  114. ),
  115. Text(
  116. "当前鞋子:$data",
  117. style: Theme.of(context).textTheme.headline1,
  118. ),
  119. IconButton(
  120. icon: Image.asset("lib/assets/img/modify_pen.png"),
  121. onPressed: () {
  122. TextEditingController controller = TextEditingController(text: data);
  123. showDialog(
  124. context: context,
  125. builder: (context) => CustomAlertDialog(
  126. title: '修改昵称',
  127. child: Column(
  128. children: [
  129. Padding(
  130. padding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 12.0),
  131. child: TextField(
  132. textAlign: TextAlign.center,
  133. controller: controller,
  134. autofocus: true,
  135. maxLength: 100,
  136. // inputFormatters: [
  137. // FilteringTextInputFormatter(RegExp(
  138. // "[a-zA-Z]|[\u4e00-\u9fa5]|[0-9]"), allow: true),//最大长度
  139. // ],
  140. onChanged: (str) {},
  141. decoration: InputDecoration(
  142. counterText: '',
  143. // helperText: '用户名长度为2-10个字母,数字,中文',
  144. // hintText: name,
  145. helperMaxLines: 1,
  146. focusedBorder: UnderlineInputBorder(
  147. borderSide: BorderSide(color: Color(0xffF1F1F1)),
  148. ))),
  149. ),
  150. Text(
  151. "给你的鞋子起一个容易记住的名称",
  152. style: Theme.of(context).textTheme.bodyText1,
  153. ),
  154. ],
  155. ),
  156. ok: () {
  157. String str = controller.text;
  158. int length = 0;
  159. for (int i = 0; i < str.length; i++) {
  160. length++;
  161. if (str.codeUnitAt(i) > 122) {
  162. length++;
  163. }
  164. }
  165. if (length <= 10) {
  166. Navigator.of(context).pop(true);
  167. } else {
  168. ToastUtil.show("昵称长度不能大于5个中文字或10个英文字母");
  169. }
  170. }),
  171. ).then((value) {
  172. if (value == true) {
  173. String name = controller.value.text;
  174. _bluetooth.updateName(name);
  175. }
  176. });
  177. },
  178. ),
  179. ],
  180. );
  181. }),
  182. ),
  183. Row(
  184. children: [
  185. Expanded(
  186. child: PrimaryButton(
  187. height: 44.0,
  188. callback: () {
  189. openSearchDeviceDialog(context);
  190. },
  191. content: '',
  192. child: Center(
  193. child: Row(
  194. mainAxisSize: MainAxisSize.min,
  195. children: [
  196. Image.asset("lib/assets/img/icon_change.png"),
  197. SizedBox(
  198. width: 5,
  199. ),
  200. Text("更换鞋子", style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white, fontSize: 16.0))
  201. ],
  202. ),
  203. ),
  204. ),
  205. ),
  206. const SizedBox(
  207. width: 20.0,
  208. ),
  209. Expanded(
  210. child: PrimaryButton(
  211. height: 44.0,
  212. callback: () async {
  213. if (await showDialog(
  214. context: context,
  215. builder: (context) => CustomAlertDialog(title: '断开当前已连接鞋子', ok: () => Navigator.of(context).pop(true)),
  216. ) ==
  217. true) {
  218. await request(context, () async {
  219. await GetIt.I<Bluetooth>().clearDevice(deleteStep: true);
  220. await Future.delayed(Duration(seconds: 2));
  221. return 1;
  222. });
  223. Navigator.of(context).pop(true);
  224. }
  225. },
  226. content: '',
  227. child: Center(
  228. child: Row(
  229. mainAxisSize: MainAxisSize.min,
  230. children: [
  231. Image.asset("lib/assets/img/icon_disconnect.png"),
  232. SizedBox(
  233. width: 5,
  234. ),
  235. Text("断开连接", style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white, fontSize: 16.0))
  236. ],
  237. ),
  238. ))),
  239. ],
  240. )
  241. ],
  242. ),
  243. ),
  244. ),
  245. SliverToBoxAdapter(
  246. child: Container(
  247. decoration: circular(),
  248. margin: _padding,
  249. child: ListView(
  250. padding: _padding,
  251. physics: NeverScrollableScrollPhysics(),
  252. shrinkWrap: true,
  253. children: ListTile.divideTiles(context: context, tiles: [
  254. ListTile(
  255. title: Row(
  256. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  257. children: <Widget>[
  258. Text(
  259. "鞋子电量",
  260. style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16.0),
  261. ),
  262. ValueListenableBuilder<Map>(
  263. valueListenable: _bluetooth.dataNotifier,
  264. builder: (context, data, _) => Column(
  265. children: [
  266. Text(
  267. isDebugShoe == true ? "${Converter.toInt(data['0_electricity'])}%\t\t/\t\t${Converter.toInt(data['1_electricity'])}%" : "${min(Converter.toInt(data['0_electricity']), Converter.toInt(data['1_electricity']))}%",
  268. style: Theme.of(context).textTheme.bodyText2!,
  269. ),
  270. if (isDebugShoe)
  271. Text(
  272. "${Converter.toInt(data['0_adc'])}\t\t/\t\t${Converter.toInt(data['1_adc'])}",
  273. style: Theme.of(context).textTheme.bodyText2!,
  274. ),
  275. ],
  276. ),
  277. ),
  278. ],
  279. ),
  280. contentPadding: contentPadding,
  281. onTap: () {
  282. _bluetooth.queryDeviceData();
  283. },
  284. ),
  285. ListTile(
  286. title: Row(
  287. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  288. children: <Widget>[
  289. Text(
  290. "鞋子编号",
  291. style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16.0),
  292. ),
  293. Text(
  294. "${device.name}",
  295. style: Theme.of(context).textTheme.bodyText2!,
  296. ),
  297. ],
  298. ),
  299. contentPadding: contentPadding,
  300. ),
  301. if (isDebugShoe == true)
  302. ListTile(
  303. title: Row(
  304. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  305. children: <Widget>[
  306. Text(
  307. "鞋子测试",
  308. style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16.0),
  309. ),
  310. ],
  311. ),
  312. contentPadding: contentPadding,
  313. trailing: arrowRight5(),
  314. onTap: () {
  315. NavigatorUtil.goPage(
  316. context,
  317. (context) => DeviceScreen(
  318. device: device,
  319. ));
  320. },
  321. ),
  322. if (isDebugShoe == true)
  323. ListTile(
  324. title: Row(
  325. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  326. children: <Widget>[
  327. Text(
  328. "鞋子数据测试",
  329. style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16.0),
  330. ),
  331. ],
  332. ),
  333. contentPadding: contentPadding,
  334. trailing: arrowRight5(),
  335. onTap: () {
  336. NavigatorUtil.goPage(context, (context) => DataTestPage());
  337. },
  338. ),
  339. if (isDebugShoe == true)
  340. ListTile(
  341. title: Row(
  342. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  343. children: <Widget>[
  344. Text(
  345. "游戏数据测试",
  346. style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16.0),
  347. ),
  348. ],
  349. ),
  350. contentPadding: contentPadding,
  351. trailing: arrowRight5(),
  352. onTap: () {
  353. NavigatorUtil.goPage(context, (context) => GameTestPage());
  354. },
  355. ),
  356. ]).toList()),
  357. ),
  358. ),
  359. SliverToBoxAdapter(
  360. child: Container(
  361. decoration: circular(),
  362. margin: _padding,
  363. child: ListView(
  364. padding: _padding,
  365. physics: NeverScrollableScrollPhysics(),
  366. shrinkWrap: true,
  367. children: ListTile.divideTiles(context: context, tiles: [
  368. ListTile(
  369. title: StreamBuilder<bool>(
  370. stream: SharedPreferences.getInstance().asStream().asyncMap((event) => event.getBool("vibrate") ?? true),
  371. builder: (context, snapshot) {
  372. return Row(
  373. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  374. children: <Widget>[
  375. Text(
  376. "游戏震动反馈开关",
  377. style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16.0),
  378. ),
  379. Switch(
  380. value: snapshot.data ?? true,
  381. onChanged: (bool value) {
  382. SharedPreferences.getInstance().then((prefs) {
  383. prefs.setBool("vibrate", value);
  384. setState(() {});
  385. });
  386. },
  387. )
  388. ],
  389. );
  390. }),
  391. subtitle: Padding(
  392. padding: const EdgeInsets.symmetric(vertical: 8.0),
  393. child: Text("开启后,游戏过程中使用鞋子做某些动作时,鞋子会震动提示", style: Theme.of(context).textTheme.bodyText1),
  394. ),
  395. contentPadding: contentPadding,
  396. ),
  397. ]).toList()),
  398. )),
  399. if (isDebugShoe == true)
  400. SliverToBoxAdapter(
  401. child: Container(
  402. decoration: circular(),
  403. margin: _padding,
  404. child: ListView(
  405. padding: _padding,
  406. physics: NeverScrollableScrollPhysics(),
  407. shrinkWrap: true,
  408. children: ListTile.divideTiles(context: context, tiles: [
  409. ListTile(
  410. title: StreamBuilder<bool>(
  411. stream: SharedPreferences.getInstance().asStream().asyncMap((event) => event.getBool("debug_game") ?? true),
  412. builder: (context, snapshot) {
  413. return Row(
  414. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  415. children: <Widget>[
  416. Text(
  417. "原生游戏界面调试开关",
  418. style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16.0),
  419. ),
  420. Switch(
  421. value: snapshot.data ?? true,
  422. onChanged: (bool value) {
  423. SharedPreferences.getInstance().then((prefs) {
  424. prefs.setBool("debug_game", value);
  425. setState(() {});
  426. });
  427. },
  428. )
  429. ],
  430. );
  431. }),
  432. subtitle: Padding(
  433. padding: const EdgeInsets.symmetric(vertical: 8.0),
  434. child: Text("开启后,原生游戏界面会打印调试数据和保存数据日志", style: Theme.of(context).textTheme.bodyText1),
  435. ),
  436. contentPadding: contentPadding,
  437. ),
  438. ]).toList()),
  439. )),
  440. SliverToBoxAdapter(
  441. child: ValueListenableBuilder<Map>(
  442. valueListenable: _bluetooth.infoNotifier,
  443. builder: (_, data, ___) {
  444. return ValueListenableBuilder<Hardware?>(
  445. valueListenable: _bluetooth.verNotifier,
  446. builder: (_, version, __) {
  447. return Column(
  448. children: [
  449. Padding(
  450. padding: _padding.copyWith(bottom: 0),
  451. child: Row(
  452. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  453. children: [
  454. Text("设备信息", style: Theme.of(context).textTheme.bodyText1),
  455. ElevatedButton(
  456. style: ButtonStyle(shape: MaterialStateProperty.all(StadiumBorder(side: BorderSide(color: const Color(0xffdcdcdc)))), elevation: MaterialStateProperty.all(0), backgroundColor: MaterialStateProperty.all(Colors.transparent)),
  457. child: Text('复制信息', style: Theme.of(context).textTheme.bodyText1),
  458. onPressed: () {
  459. var user = Provider.of<UserModel>(context, listen: false);
  460. Clipboard.setData(ClipboardData(text: '用户ID:${user.user.id}\n型号:${data['name']?.toString().trim() ?? 'SHOES'}\n固件:${data['softwareVer'] ?? 0}\n硬件:${data['hardwareVer'] ?? 0}'));
  461. ToastUtil.show("已复制至粘贴板");
  462. },
  463. ),
  464. ],
  465. ),
  466. ),
  467. Container(
  468. decoration: circular(),
  469. margin: _padding,
  470. child: ListView(
  471. padding: _padding,
  472. physics: NeverScrollableScrollPhysics(),
  473. shrinkWrap: true,
  474. children: ListTile.divideTiles(context: context, tiles: [
  475. ListTile(
  476. title: Row(
  477. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  478. children: <Widget>[
  479. Text(
  480. "鞋子型号",
  481. style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16.0),
  482. ),
  483. Text(
  484. "${data['name']?.toString().trim() ?? 'SHOES'}",
  485. style: Theme.of(context).textTheme.bodyText2!,
  486. maxLines: 1,
  487. )
  488. ],
  489. ),
  490. contentPadding: contentPadding,
  491. onTap: () {
  492. if (isDebugShoe) {
  493. _bluetooth.testDfu++;
  494. if (_bluetooth.testDfu > 5) {
  495. ToastUtil.showBottom("进入DFU模式");
  496. _bluetooth.infoNotifier.value = {};
  497. final oldVersion = _bluetooth.versionNotifier.value;
  498. _bluetooth.versionNotifier.value = "";
  499. _bluetooth.verNotifier.value = null;
  500. _bluetooth.queryDeviceInfo();
  501. }
  502. }
  503. },
  504. ),
  505. // ListTile(
  506. // title: Row(
  507. // mainAxisAlignment: MainAxisAlignment.spaceBetween,
  508. // children: <Widget>[
  509. // Text("鞋码"),
  510. // Text(
  511. // "42码",
  512. // style: Theme.of(context).textTheme.bodyText2!,
  513. // )
  514. // ],
  515. // ),
  516. // contentPadding: contentPadding,
  517. // ),
  518. ListTile(
  519. title: Row(
  520. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  521. children: <Widget>[
  522. Text(
  523. "鞋子固件版本",
  524. style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16.0),
  525. ),
  526. Text(
  527. "V ${data['softwareVer'] ?? 0}",
  528. style: Theme.of(context).textTheme.bodyText2!,
  529. ),
  530. ],
  531. ),
  532. contentPadding: contentPadding,
  533. trailing: version?.devices?.isNotEmpty == true
  534. ? Container(
  535. padding: EdgeInsets.symmetric(horizontal: 10, vertical: 6),
  536. decoration: BoxDecoration(borderRadius: BorderRadius.circular(50.0), color: Color(0xffFF5B1D)),
  537. child: Text(
  538. "点击更新",
  539. style: Theme.of(context).textTheme.subtitle2!.copyWith(fontSize: 12.0, color: Colors.white),
  540. ),
  541. )
  542. : null,
  543. onTap: () async {
  544. if (version == null) return;
  545. if (version.devices?.isNotEmpty != true) return;
  546. var result = await showDialog(
  547. context: context,
  548. builder: (context) => CustomAlertDialog(
  549. title: '发现新的鞋子固件',
  550. child: Container(
  551. width: double.infinity,
  552. padding: const EdgeInsets.symmetric(horizontal: 24.0),
  553. child: Text(
  554. "${version.name}\n${version.localVer} -> ${version.ver}\n${version.msg}",
  555. style: TextStyle(fontSize: 14, color: Color(0xff333333), height: 1.4),
  556. )),
  557. textOk: '立即升级',
  558. ok: () => Navigator.of(context).pop(true)),
  559. );
  560. if (result != true) return;
  561. result = await showDialog(
  562. context: context,
  563. barrierDismissible: false,
  564. builder: (context) => SimpleDialog(
  565. children: <Widget>[DfuUpdatePage(hardware: version, version: "${version.ver}")],
  566. ));
  567. },
  568. ),
  569. ListTile(
  570. title: Row(
  571. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  572. children: <Widget>[
  573. Text(
  574. "鞋子硬件版本",
  575. style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16.0),
  576. ),
  577. Text(
  578. "V ${data['hardwareVer'] ?? 0}",
  579. style: Theme.of(context).textTheme.bodyText2!,
  580. )
  581. ],
  582. ),
  583. onTap: () {
  584. _bluetooth.checkUpdate();
  585. },
  586. contentPadding: contentPadding,
  587. ),
  588. if (isDebugShoe)
  589. ListTile(
  590. title: Row(
  591. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  592. children: <Widget>[
  593. Text(
  594. "蓝牙地址",
  595. style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16.0),
  596. ),
  597. SizedBox(
  598. width: 10,
  599. ),
  600. Expanded(
  601. child: Align(
  602. alignment: Alignment.centerRight,
  603. child: Text(
  604. "${_bluetooth.deviceId}",
  605. style: Theme.of(context).textTheme.bodyText2!,
  606. maxLines: 1,
  607. ),
  608. )),
  609. ],
  610. ),
  611. contentPadding: contentPadding,
  612. ),
  613. ]).toList())),
  614. ],
  615. );
  616. });
  617. })),
  618. ],
  619. );
  620. }));
  621. }
  622. }