From d270b1ca4aa5e3ec437ee12102bf33f35ab7cea2 Mon Sep 17 00:00:00 2001 From: Codel1417 Date: Sun, 22 Dec 2024 15:34:03 -0500 Subject: [PATCH] add About your gear section and update check button --- .../Definitions/Device/device_definition.dart | 1 + lib/Frontend/Widgets/manage_gear.dart | 270 ++++++++++++++---- .../translation_string_definitions.dart | 14 + 3 files changed, 230 insertions(+), 55 deletions(-) diff --git a/lib/Backend/Definitions/Device/device_definition.dart b/lib/Backend/Definitions/Device/device_definition.dart index 319b90cf..8c4acb01 100644 --- a/lib/Backend/Definitions/Device/device_definition.dart +++ b/lib/Backend/Definitions/Device/device_definition.dart @@ -407,6 +407,7 @@ String getNameFromBTName(String bluetoothDeviceName) { return bluetoothDeviceName; } +//todo: convert into a proper provider class CommandQueue { final PriorityQueue state = PriorityQueue(); final BaseStatefulDevice device; diff --git a/lib/Frontend/Widgets/manage_gear.dart b/lib/Frontend/Widgets/manage_gear.dart index 25f92f65..b584256a 100644 --- a/lib/Frontend/Widgets/manage_gear.dart +++ b/lib/Frontend/Widgets/manage_gear.dart @@ -3,6 +3,7 @@ import 'package:flex_color_picker/flex_color_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:tail_app/Backend/firmware_update.dart'; import 'package:tail_app/Backend/move_lists.dart'; import 'package:tail_app/Frontend/Widgets/tutorial_card.dart'; @@ -75,64 +76,73 @@ class _ManageGearState extends ConsumerState { ), ), ], - if (device!.mandatoryOtaRequired.value) ...[ - BaseCard( - elevation: 3, - color: Colors.red, - child: InkWell( - onTap: () async { - OtaUpdateRoute(device: device!.baseStoredDevice.btMACAddress).push(context); - }, - child: ListTile( - leading: const Icon( - Icons.warning, - color: Colors.white, - ), - trailing: const Icon( - Icons.warning, - color: Colors.white, - ), - title: Text( - mandatoryOtaRequired(), - style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Colors.white), - textAlign: TextAlign.center, - ), - ), - ), - ), - ], - if (device!.hasUpdate.value) ...[ - Padding( - padding: const EdgeInsets.all(16.0), - child: FilledButton( - onPressed: () async { - OtaUpdateRoute(device: device!.baseStoredDevice.btMACAddress).push(context); - }, - style: ElevatedButton.styleFrom( - foregroundColor: getTextColor(color!), - elevation: 1, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.system_update, - color: getTextColor(color!), - ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 4), - ), - Text( - manageDevicesOtaButton(), - style: Theme.of(context).textTheme.labelLarge!.copyWith( - color: getTextColor(color!), + ValueListenableBuilder( + valueListenable: device!.hasUpdate, + builder: (context, value, child) { + return Column( + children: [ + if (device!.mandatoryOtaRequired.value) ...[ + BaseCard( + elevation: 3, + color: Colors.red, + child: InkWell( + onTap: () async { + OtaUpdateRoute(device: device!.baseStoredDevice.btMACAddress).push(context); + }, + child: ListTile( + leading: const Icon( + Icons.warning, + color: Colors.white, + ), + trailing: const Icon( + Icons.warning, + color: Colors.white, ), + title: Text( + mandatoryOtaRequired(), + style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Colors.white), + textAlign: TextAlign.center, + ), + ), + ), ), ], - ), - ), - ), - ], + if (device!.hasUpdate.value) ...[ + Padding( + padding: const EdgeInsets.all(16.0), + child: FilledButton( + onPressed: () async { + OtaUpdateRoute(device: device!.baseStoredDevice.btMACAddress).push(context); + }, + style: ElevatedButton.styleFrom( + foregroundColor: getTextColor(color!), + elevation: 1, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.system_update, + color: getTextColor(color!), + ), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 4), + ), + Text( + manageDevicesOtaButton(), + style: Theme.of(context).textTheme.labelLarge!.copyWith( + color: getTextColor(color!), + ), + ), + ], + ), + ), + ), + ], + ], + ); + }, + ), Padding( padding: const EdgeInsets.all(16.0), child: TextField( @@ -190,6 +200,13 @@ class _ManageGearState extends ConsumerState { ManageGearConventionMode(device: device!), ManageGearDebug(device: device!), ], + // We only know this info if the gear is connected + if (device!.deviceConnectionState.value == ConnectivityState.connected) ...[ + ManageGearAbout( + device: device!, + color: color!, + ), + ], OverflowBar( alignment: MainAxisAlignment.end, children: [ @@ -250,6 +267,95 @@ class _ManageGearState extends ConsumerState { } } +class ManageGearUpdateCheckButton extends ConsumerStatefulWidget { + final BaseStatefulDevice device; + final Color color; + + const ManageGearUpdateCheckButton({super.key, required this.device, required this.color}); + + @override + ConsumerState createState() => _ManageGearUpdateCheckButtonState(); +} + +class _ManageGearUpdateCheckButtonState extends ConsumerState { + Future? _otaAvailable; + + @override + Widget build(BuildContext context) { + return OverflowBar( + alignment: MainAxisAlignment.center, + children: [ + FutureBuilder( + future: _otaAvailable, + builder: (context, snapshot) { + String buttonText = ""; + IconData iconData = Icons.device_unknown; + if (snapshot.connectionState == ConnectionState.none) { + buttonText = manageDevicesOtaCheckButtonLabel(); + iconData = Icons.question_mark; + } else if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) { + if (snapshot.data == true) { + buttonText = manageDevicesOtaButton(); + iconData = Icons.system_update; + } else { + buttonText = manageDevicesOtaUpToDateButtonLabel(); + iconData = Icons.check; + } + } else if (snapshot.hasError) { + buttonText = manageDevicesOtaCheckErrorButtonLabel(); + iconData = Icons.error; + } else if (snapshot.connectionState == ConnectionState.active || snapshot.connectionState == ConnectionState.waiting) { + buttonText = manageDevicesOtaCheckInProgressButtonLabel(); + } + return FilledButton( + onPressed: (snapshot.connectionState == ConnectionState.active || snapshot.connectionState == ConnectionState.waiting) + ? null + : () { + if (snapshot.data == true) { + OtaUpdateRoute(device: widget.device.baseStoredDevice.btMACAddress).push(context); + } else { + setState(() { + _otaAvailable = ref.watch(hasOtaUpdateProvider(widget.device).future); + }); + } + }, + style: ElevatedButton.styleFrom( + foregroundColor: getTextColor(widget.color), + elevation: 1, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + if (snapshot.connectionState == ConnectionState.active || snapshot.connectionState == ConnectionState.waiting) ...[ + CircularProgressIndicator( + color: getTextColor(widget.color), + ) + ] else ...[ + Icon( + iconData, + color: getTextColor(widget.color), + ) + ], + const Padding( + padding: EdgeInsets.symmetric(horizontal: 4), + ), + Text( + buttonText, + style: Theme.of(context).textTheme.labelLarge!.copyWith( + color: getTextColor(widget.color), + ), + ), + ], + ), + ); + }, + ) + ], + ); + } +} + class ManageGearConventionMode extends ConsumerWidget { final BaseStatefulDevice device; @@ -301,6 +407,60 @@ class ManageGearConventionMode extends ConsumerWidget { } } +class ManageGearAbout extends StatelessWidget { + final BaseStatefulDevice device; + final Color color; + + const ManageGearAbout({super.key, required this.device, required this.color}); + + @override + Widget build(BuildContext context) { + return ExpansionTile( + title: Text(manageDevicesAboutLabel()), + children: [ + ListTile( + dense: true, + title: Text( + manageDevicesAboutSoftwareVersionLabel(), + style: Theme.of(context).textTheme.bodyMedium, + ), + trailing: ValueListenableBuilder( + valueListenable: device.fwVersion, + builder: (context, value, child) { + return Text( + "${value.major}.${value.minor}.${value.patch}", + ); + }, + ), + ), + ListTile( + dense: true, + title: Text( + manageDevicesAboutHardwareVersionLabel(), + style: Theme.of(context).textTheme.bodyMedium, + ), + trailing: ValueListenableBuilder( + valueListenable: device.hwVersion, + builder: (context, value, child) { + return Text( + value, + ); + }, + ), + ), + OverflowBar( + children: [ + ManageGearUpdateCheckButton( + device: device, + color: color, + ), + ], + ) + ], + ); + } +} + class ManageGearBatteryGraph extends StatelessWidget { final BaseStatefulDevice device; diff --git a/lib/Frontend/translation_string_definitions.dart b/lib/Frontend/translation_string_definitions.dart index 8583c2a5..9706c541 100644 --- a/lib/Frontend/translation_string_definitions.dart +++ b/lib/Frontend/translation_string_definitions.dart @@ -175,6 +175,20 @@ String manageDevicesForget() => Intl.message('Forget', name: 'manageDevicesForge String manageDevicesOtaButton() => Intl.message('Tap to update firmware', name: 'manageDevicesOtaButton', desc: 'manage devices ota available button'); +String manageDevicesOtaCheckButtonLabel() => Intl.message('Check for update', name: 'manageDevicesOtaCheckButtonLabel', desc: 'manage devices button to check if an update is available'); + +String manageDevicesOtaUpToDateButtonLabel() => Intl.message('Gear is up to date!', name: 'manageDevicesOtaUpToDateButtonLabel', desc: 'manage devices button label if gear is up to date'); + +String manageDevicesOtaCheckInProgressButtonLabel() => Intl.message('Checking', name: 'manageDevicesOtaCheckInProgressButtonLabel', desc: 'manage devices button label if gear is checking for an update'); + +String manageDevicesOtaCheckErrorButtonLabel() => Intl.message('Please try again later', name: 'manageDevicesOtaCheckErrorButtonLabel', desc: 'manage devices button label if an error occured while checking for an update'); + +String manageDevicesAboutLabel() => Intl.message('About your gear', name: 'manageDevicesAboutLabel', desc: 'label for the about your gear section in the manage devices area'); + +String manageDevicesAboutHardwareVersionLabel() => Intl.message('Hardware Version', name: 'manageDevicesAboutHardwareVersionLabel', desc: 'label for the hardware version on the manage devices page'); + +String manageDevicesAboutSoftwareVersionLabel() => Intl.message('Software Version', name: 'manageDevicesAboutSoftwareVersionLabel', desc: 'label for the software version on the manage devices page'); + String scanDevicesTitle() => Intl.message('Scan For New Gear', name: 'scanDevicesTitle', desc: 'button which opens the scan window'); String scanDevicesFoundTitle() => Intl.message('Found Gear. Tap the gear name to connect', name: 'scanDevicesFoundTitle', desc: 'Title when gear is found on the scan for new gear page');