From 912d044791d01e9e66cada16884e7c71c836e6b4 Mon Sep 17 00:00:00 2001 From: Jonghyeon Park Date: Mon, 21 Oct 2024 20:43:25 +0900 Subject: [PATCH] feat: Add parameter to each widget constructor to handle when managed value is changed --- .../lib/components/device_selector.dart | 27 +++- .../lib/components/key_config_list.dart | 40 +++++- configurator/lib/globals.dart | 23 ++++ configurator/lib/l10n/app_en.arb | 3 +- configurator/lib/models/each_key_config.dart | 2 + .../lib/models/error_serial_device.dart | 10 ++ configurator/lib/models/key_config.dart | 115 +++++++++--------- configurator/lib/models/notifying_events.dart | 5 +- configurator/lib/models/serial_device.dart | 33 +++-- .../lib/utilities/event_notifier.dart | 3 + 10 files changed, 189 insertions(+), 72 deletions(-) diff --git a/configurator/lib/components/device_selector.dart b/configurator/lib/components/device_selector.dart index 99bbd49..14fa8de 100644 --- a/configurator/lib/components/device_selector.dart +++ b/configurator/lib/components/device_selector.dart @@ -1,3 +1,6 @@ +import 'dart:async'; + +import 'package:configurator/models/error_serial_device.dart'; import 'package:configurator/models/notifying_events.dart'; import 'package:flutter/material.dart'; import 'package:flutter_libserialport/flutter_libserialport.dart'; @@ -66,10 +69,32 @@ class _DeviceSelectorState extends State { setState(() { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(EventNotifier.eventNotifyingMessage( - context, NotifyingEvents.serialDeviceDoesNotResponse)))); + context, + NotifyingEvents + .serialDeviceDoesNotResponseMayInvalidDevice)))); }); return; } + + bool errorThrownDuringLoad = false; + try { + Globals.instance.loadCurrentSerialDeviceConfig(); + } on SerialPortNotInstantiatedWell { + errorThrownDuringLoad = true; + } on SerialPortCannotOpen { + errorThrownDuringLoad = true; + } on StreamControllerNotInstantiatedWell { + errorThrownDuringLoad = true; + } on TimeoutException { + errorThrownDuringLoad = true; + } + if (errorThrownDuringLoad) { + setState(() { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(EventNotifier.eventNotifyingMessage( + context, NotifyingEvents.serialDeviceDoesNotResponse)))); + }); + } }, ); } diff --git a/configurator/lib/components/key_config_list.dart b/configurator/lib/components/key_config_list.dart index b0d21a5..eba3242 100644 --- a/configurator/lib/components/key_config_list.dart +++ b/configurator/lib/components/key_config_list.dart @@ -18,13 +18,23 @@ class KeyConfigListItemContainer { } class KeyConfigList extends StatefulWidget { - const KeyConfigList({super.key}); + const KeyConfigList({super.key, this.onKeyConfigUpdated}); + final Null Function()? onKeyConfigUpdated; @override State createState() => _KeyConfigListState(); } class _KeyConfigListState extends State { + void _onChangeHandler(Keycode keycode) { + /** + * Todo: + * onKeyConfigUpdated is intended to alert to parent value need to be saved. + * Parent should display the fact that change detected and save button. + */ + widget.onKeyConfigUpdated?.call(); + } + List getKeyConfigEssentialContainers( BuildContext context) { return [ @@ -34,6 +44,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.tuneLeftSide.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.tuneLeftSide.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameTuneS, @@ -41,6 +52,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.tuneS.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.tuneS.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameTuneD, @@ -48,6 +60,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.tuneD.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.tuneD.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameTuneF, @@ -55,6 +68,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.tuneF.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.tuneF.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameTuneF, @@ -62,6 +76,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.tuneC.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.tuneC.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameTuneF, @@ -69,6 +84,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.tuneM.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.tuneM.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameTuneJ, @@ -76,6 +92,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.tuneJ.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.tuneJ.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameTuneK, @@ -83,6 +100,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.tuneK.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.tuneK.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameTuneL, @@ -90,6 +108,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.tuneL.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.tuneL.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameTuneRightSide, @@ -97,6 +116,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.tuneRightSide.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.tuneRightSide.keycode = keycode; + _onChangeHandler(keycode); }), ]; } @@ -110,6 +130,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.esc.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.esc.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameEnter, @@ -117,6 +138,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.enter.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.enter.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameTab, @@ -124,6 +146,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.tab.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.tab.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameSpace, @@ -131,6 +154,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.space.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.space.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameSpeedUp, @@ -138,6 +162,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.speedUp.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.speedUp.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameSpeedDown, @@ -145,6 +170,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.speedDown.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.speedDown.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameRewind, @@ -152,6 +178,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.rewind.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.rewind.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameLeftShift, @@ -159,6 +186,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.leftShift.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.leftShift.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameRightShift, @@ -166,6 +194,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.rightShift.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.rightShift.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameArrowUp, @@ -173,6 +202,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.arrowUp.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.arrowUp.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameArrowDown, @@ -180,6 +210,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.arrowDown.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.arrowDown.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameArrowLeft, @@ -187,6 +218,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.arrowLeft.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.arrowLeft.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameArrowRight, @@ -194,6 +226,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.arrowRight.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.arrowRight.keycode = keycode; + _onChangeHandler(keycode); }), ]; } @@ -207,6 +240,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.emoticon1.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.emoticon1.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameEmoticon2, @@ -214,6 +248,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.emoticon2.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.emoticon2.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameEmoticon3, @@ -221,6 +256,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.emoticon3.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.emoticon3.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameEmoticon4, @@ -228,6 +264,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.emoticon4.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.emoticon4.keycode = keycode; + _onChangeHandler(keycode); }), KeyConfigListItemContainer( name: AppLocalizations.of(context)!.keyNameEmoticon5, @@ -235,6 +272,7 @@ class _KeyConfigListState extends State { enabled: Globals.instance.updatedKeyConfig.emoticon5.enabled, handler: (Keycode keycode) { Globals.instance.updatedKeyConfig.emoticon5.keycode = keycode; + _onChangeHandler(keycode); }), ]; } diff --git a/configurator/lib/globals.dart b/configurator/lib/globals.dart index 51e5e95..8d7962d 100644 --- a/configurator/lib/globals.dart +++ b/configurator/lib/globals.dart @@ -1,5 +1,6 @@ import 'package:configurator/build_config.dart'; import 'package:configurator/models/each_key_config.dart'; +import 'package:configurator/models/error_serial_device.dart'; import 'package:configurator/models/keycode.dart'; import 'package:configurator/models/key_config.dart'; import 'package:configurator/models/serial_device.dart'; @@ -12,6 +13,10 @@ class Globals { late String currentSerialDevicePort; late Map _serialDevices; + bool get keyConfigUpdated { + return !(keyConfig == updatedKeyConfig); + } + static Globals instance = Globals._privateConstructor(); factory Globals() { @@ -153,4 +158,22 @@ class Globals { SerialDevice current = currentSerialDevice; return await current.checkDeviceIsValid(); } + + Future _getCurrentSerialDeviceConfig() async { + SerialDevice current = currentSerialDevice; + return await current.requestLoadKeyConfiguration(); + } + + void loadCurrentSerialDeviceConfig() async { + /// Load device's keyconfig data + /// + /// Below errors must be handled: + /// - SerialPortNotInstantiatedWell + /// - SerialPortCannotOpen + /// - StreamControllerNotInstantiatedWell + /// - TimeoutException + /// - SerialPortCommunicationDoneIncompleted + keyConfig = await _getCurrentSerialDeviceConfig(); + updatedKeyConfig = KeyConfig.clone(keyConfig); + } } diff --git a/configurator/lib/l10n/app_en.arb b/configurator/lib/l10n/app_en.arb index 6ad5311..5e4cb65 100644 --- a/configurator/lib/l10n/app_en.arb +++ b/configurator/lib/l10n/app_en.arb @@ -35,5 +35,6 @@ "keyNameEmoticon3": "Emoticon #3", "keyNameEmoticon4": "Emoticon #4", "keyNameEmoticon5": "Emoticon #5", - "notifyingEventsSerialDeviceDoesNotResponse": "Selected device doesn't response. It seems to be not correct device or software installed." + "notifyingEventsSerialDeviceDoesNotResponse": "Selected device doesn't response.", + "notifyingEventsSerialDeviceDoesNotResponse_mayInvalidDevice": "Selected device doesn't response. It seems to be not correct device or software installed." } diff --git a/configurator/lib/models/each_key_config.dart b/configurator/lib/models/each_key_config.dart index 4c812b8..ddfe490 100644 --- a/configurator/lib/models/each_key_config.dart +++ b/configurator/lib/models/each_key_config.dart @@ -4,4 +4,6 @@ class EachKeyConfig { Keycode keycode; bool enabled; EachKeyConfig({required this.keycode, required this.enabled}); + EachKeyConfig.clone(EachKeyConfig other) + : this(keycode: other.keycode, enabled: other.enabled); } diff --git a/configurator/lib/models/error_serial_device.dart b/configurator/lib/models/error_serial_device.dart index b0c86bd..1661b8e 100644 --- a/configurator/lib/models/error_serial_device.dart +++ b/configurator/lib/models/error_serial_device.dart @@ -1,3 +1,8 @@ +class SerialPortNotInstantiatedWell implements Exception { + String cause; + SerialPortNotInstantiatedWell(this.cause); +} + class SerialPortIsNotOpened implements Exception { String cause; SerialPortIsNotOpened(this.cause); @@ -27,3 +32,8 @@ class StreamControllerNotInstantiatedWell implements Exception { String cause; StreamControllerNotInstantiatedWell(this.cause); } + +class SerialPortCommunicationDoneIncompleted implements Exception { + String cause; + SerialPortCommunicationDoneIncompleted(this.cause); +} diff --git a/configurator/lib/models/key_config.dart b/configurator/lib/models/key_config.dart index 0dd61d3..0418ff1 100644 --- a/configurator/lib/models/key_config.dart +++ b/configurator/lib/models/key_config.dart @@ -1,5 +1,8 @@ import 'package:configurator/models/each_key_config.dart'; +// Todo: Find more better way. +const keyConfigArrayLength = 28; + class KeyConfig { EachKeyConfig esc; EachKeyConfig enter; @@ -38,66 +41,66 @@ class KeyConfig { EachKeyConfig emoticon5; // digit7 KeyConfig( - esc, - enter, - tab, - space, - speedUp, - speedDown, - rewind, - leftShift, - rightShift, - arrowUp, - arrowDown, - arrowLeft, - arrowRight, - tuneLeftSide, - tuneS, - tuneD, - tuneF, - tuneC, - tuneM, - tuneJ, - tuneK, - tuneL, - tuneRightSide, - emoticon1, - emoticon2, - emoticon3, - emoticon4, - emoticon5, + this.esc, + this.enter, + this.tab, + this.space, + this.speedUp, + this.speedDown, + this.rewind, + this.leftShift, + this.rightShift, + this.arrowUp, + this.arrowDown, + this.arrowLeft, + this.arrowRight, + this.tuneLeftSide, + this.tuneS, + this.tuneD, + this.tuneF, + this.tuneC, + this.tuneM, + this.tuneJ, + this.tuneK, + this.tuneL, + this.tuneRightSide, + this.emoticon1, + this.emoticon2, + this.emoticon3, + this.emoticon4, + this.emoticon5, ); KeyConfig.clone(KeyConfig other) : this( - other.esc, - other.enter, - other.tab, - other.space, - other.speedUp, - other.speedDown, - other.rewind, - other.leftShift, - other.rightShift, - other.arrowUp, - other.arrowDown, - other.arrowLeft, - other.arrowRight, - other.tuneLeftSide, - other.tuneS, - other.tuneD, - other.tuneF, - other.tuneC, - other.tuneM, - other.tuneJ, - other.tuneK, - other.tuneL, - other.tuneRightSide, - other.emoticon1, - other.emoticon2, - other.emoticon3, - other.emoticon4, - other.emoticon5); + EachKeyConfig.clone(other.esc), + EachKeyConfig.clone(other.enter), + EachKeyConfig.clone(other.tab), + EachKeyConfig.clone(other.space), + EachKeyConfig.clone(other.speedUp), + EachKeyConfig.clone(other.speedDown), + EachKeyConfig.clone(other.rewind), + EachKeyConfig.clone(other.leftShift), + EachKeyConfig.clone(other.rightShift), + EachKeyConfig.clone(other.arrowUp), + EachKeyConfig.clone(other.arrowDown), + EachKeyConfig.clone(other.arrowLeft), + EachKeyConfig.clone(other.arrowRight), + EachKeyConfig.clone(other.tuneLeftSide), + EachKeyConfig.clone(other.tuneS), + EachKeyConfig.clone(other.tuneD), + EachKeyConfig.clone(other.tuneF), + EachKeyConfig.clone(other.tuneC), + EachKeyConfig.clone(other.tuneM), + EachKeyConfig.clone(other.tuneJ), + EachKeyConfig.clone(other.tuneK), + EachKeyConfig.clone(other.tuneL), + EachKeyConfig.clone(other.tuneRightSide), + EachKeyConfig.clone(other.emoticon1), + EachKeyConfig.clone(other.emoticon2), + EachKeyConfig.clone(other.emoticon3), + EachKeyConfig.clone(other.emoticon4), + EachKeyConfig.clone(other.emoticon5)); // @override // int get hashCode diff --git a/configurator/lib/models/notifying_events.dart b/configurator/lib/models/notifying_events.dart index a00f642..84fa7e3 100644 --- a/configurator/lib/models/notifying_events.dart +++ b/configurator/lib/models/notifying_events.dart @@ -1 +1,4 @@ -enum NotifyingEvents { serialDeviceDoesNotResponse } +enum NotifyingEvents { + serialDeviceDoesNotResponse, + serialDeviceDoesNotResponseMayInvalidDevice +} diff --git a/configurator/lib/models/serial_device.dart b/configurator/lib/models/serial_device.dart index 4079228..b86db28 100644 --- a/configurator/lib/models/serial_device.dart +++ b/configurator/lib/models/serial_device.dart @@ -1,7 +1,10 @@ import 'dart:async'; import 'dart:typed_data'; +import 'package:configurator/models/each_key_config.dart'; import 'package:configurator/models/error_serial_device.dart'; +import 'package:configurator/models/key_config.dart'; +import 'package:configurator/models/keycode.dart'; import 'package:flutter_libserialport/flutter_libserialport.dart'; import 'package:configurator/build_config.dart'; import 'package:configurator/models/serial_descriptor.dart'; @@ -124,7 +127,6 @@ class SerialDevice { } Future checkDeviceIsValid() async => await requestHandshake(); - Future requestHandshake() async { if (serialPort == null) return false; try { @@ -148,15 +150,11 @@ class SerialDevice { } } - Future requestLoadKeyConfiguration() async { - if (serialPort == null) return false; - try { - beginCommunication(); - } on SerialPortCannotOpen { - return false; - } on StreamControllerNotInstantiatedWell { - return false; + Future requestLoadKeyConfiguration() async { + if (serialPort == null) { + throw SerialPortNotInstantiatedWell('SerialPort is `null`'); } + beginCommunication(); serialPort!.write(magic.loadKeyConfigurationRequest); try { @@ -164,10 +162,21 @@ class SerialDevice { .timeout(const Duration(seconds: 5)); print('Received response: $response'); closeCommunication(); - return response.equals(magic.loadKeyConfigurationResponse); - } on TimeoutException catch (e) { + if (response.length != keyConfigArrayLength) { + throw SerialPortCommunicationDoneIncompleted( + "Serial commuication has been done incompletely."); + } + List keyConfigs = response.map((each) { + return EachKeyConfig( + keycode: ArduinoKeycode.toKey(each % (1 << 8)), + enabled: (each / (1 << 8) == 0), + ); + }).toList(); + + return Function.apply(KeyConfig.new, keyConfigs) as KeyConfig; + } on TimeoutException { closeCommunication(); - return false; + rethrow; } } } diff --git a/configurator/lib/utilities/event_notifier.dart b/configurator/lib/utilities/event_notifier.dart index f7af91b..440b0bf 100644 --- a/configurator/lib/utilities/event_notifier.dart +++ b/configurator/lib/utilities/event_notifier.dart @@ -9,6 +9,9 @@ class EventNotifier { case NotifyingEvents.serialDeviceDoesNotResponse: return AppLocalizations.of(context)! .notifyingEventsSerialDeviceDoesNotResponse; + case NotifyingEvents.serialDeviceDoesNotResponseMayInvalidDevice: + return AppLocalizations.of(context)! + .notifyingEventsSerialDeviceDoesNotResponse_mayInvalidDevice; } } }