From 7562e768c1f98c6006a881ec0c4992a7e4198df2 Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Tue, 20 Sep 2022 14:17:44 +0200 Subject: [PATCH 1/5] add device model --- lib/device_model.dart | 27 ++++++++++++++++++++ test/device_model_test.dart | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 lib/device_model.dart create mode 100644 test/device_model_test.dart diff --git a/lib/device_model.dart b/lib/device_model.dart new file mode 100644 index 00000000..dadb626d --- /dev/null +++ b/lib/device_model.dart @@ -0,0 +1,27 @@ +import 'package:fwupd/fwupd.dart'; +import 'package:safe_change_notifier/safe_change_notifier.dart'; + +import 'firmware_model.dart'; + +class DeviceModel extends SafeChangeNotifier { + DeviceModel(this._firmwareModel, this._device) + : _releases = _firmwareModel.state.getReleases(_device); + final FirmwareModel _firmwareModel; + final FwupdDevice _device; + final List? _releases; + + FwupdRelease? _selectedRelease; + FwupdRelease? get selectedRelease => _selectedRelease; + set selectedRelease(FwupdRelease? release) { + _selectedRelease = release; + notifyListeners(); + } + + FwupdDevice get device => _device; + List? get releases => _releases; + + Future verify() => _firmwareModel.verify(_device); + Future install(FwupdRelease release) => + _firmwareModel.install(_device, release); + bool hasUpgrade() => _firmwareModel.state.hasUpgrade(_device); +} diff --git a/test/device_model_test.dart b/test/device_model_test.dart new file mode 100644 index 00000000..3f0af5fa --- /dev/null +++ b/test/device_model_test.dart @@ -0,0 +1,50 @@ +import 'package:firmware_updater/device_model.dart'; +import 'package:firmware_updater/firmware_model.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:fwupd/fwupd.dart'; +import 'package:mockito/mockito.dart'; + +import 'test_utils.dart'; + +void main() { + test('fetches releases', () async { + final device = testDevice(id: 'a'); + + final releases = [ + FwupdRelease(version: '1', name: ''), + FwupdRelease(version: '2', name: ''), + ]; + + final service = mockService(devices: [device], releases: {'a': releases}); + + final firmwareModel = FirmwareModel(service); + await firmwareModel.init(); + + final deviceModel = DeviceModel(firmwareModel, device); + + expect(deviceModel.releases, releases); + }); + + test('install release', () async { + final device = testDevice(id: ''); + final release = FwupdRelease(name: ''); + + final service = mockService(); + + final firmwareModel = FirmwareModel(service); + final deviceModel = DeviceModel(firmwareModel, device); + await deviceModel.install(release); + verify(service.install(device, release)).called(1); + }); + + test('verify', () async { + final device = testDevice(id: ''); + + final service = mockService(); + + final firmwareModel = FirmwareModel(service); + final deviceModel = DeviceModel(firmwareModel, device); + await deviceModel.verify(); + verify(service.verify(device)).called(1); + }); +} From 95adb76ba89fb34975b0371406e53b4b039a3e23 Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Tue, 20 Sep 2022 14:19:01 +0200 Subject: [PATCH 2/5] add device body page --- lib/firmware_body_page.dart | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 lib/firmware_body_page.dart diff --git a/lib/firmware_body_page.dart b/lib/firmware_body_page.dart new file mode 100644 index 00000000..a4ada521 --- /dev/null +++ b/lib/firmware_body_page.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:fwupd/fwupd.dart'; +import 'package:provider/provider.dart'; + +import 'device_model.dart'; +import 'firmware_model.dart'; +import 'fwupd_x.dart'; +import 'src/widgets/device_body.dart'; +import 'src/widgets/release_dialog.dart'; + +class FirmwareBodyPage extends StatefulWidget { + const FirmwareBodyPage({ + super.key, + }); + + static Widget create( + BuildContext context, { + required FwupdDevice device, + }) { + final firmwareModel = context.read(); + return ChangeNotifierProvider( + create: (_) => DeviceModel(firmwareModel, device), + child: const FirmwareBodyPage(), + ); + } + + @override + State createState() => _FirmwareBodyPageState(); +} + +class _FirmwareBodyPageState extends State { + @override + Widget build(BuildContext context) { + final deviceModel = context.watch(); + return Navigator( + pages: [ + MaterialPage( + child: DeviceBody( + device: deviceModel.device, + canVerify: deviceModel.device.canVerify, + onVerify: deviceModel.verify, + releases: deviceModel.releases ?? [], + onInstall: deviceModel.install, + hasUpgrade: deviceModel.hasUpgrade(), + ), + ), + if (deviceModel.selectedRelease != null) + MaterialPage( + child: ReleaseDialog( + device: deviceModel.device, + releases: deviceModel.releases ?? [], + onInstall: deviceModel.install, + ), + ) + ], + onPopPage: (route, result) => route.didPop(result), + ); + } +} From a4f58fb2b237774ecb0bbf0e135480eb6f37f49c Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Tue, 20 Sep 2022 14:20:31 +0200 Subject: [PATCH 3/5] show release dialog as a page --- lib/firmware_page.dart | 10 ++----- lib/src/widgets/device_body.dart | 21 +++++--------- lib/src/widgets/release_dialog.dart | 45 ++++++++++++++--------------- 3 files changed, 32 insertions(+), 44 deletions(-) diff --git a/lib/firmware_page.dart b/lib/firmware_page.dart index 3e757f22..fe17f759 100644 --- a/lib/firmware_page.dart +++ b/lib/firmware_page.dart @@ -4,10 +4,10 @@ import 'package:provider/provider.dart'; import 'package:ubuntu_service/ubuntu_service.dart'; import 'package:yaru_widgets/yaru_widgets.dart'; +import 'firmware_body_page.dart'; import 'firmware_model.dart'; import 'fwupd_notifier.dart'; import 'fwupd_service.dart'; -import 'fwupd_x.dart'; import 'widgets.dart'; class FirmwarePage extends StatefulWidget { @@ -46,13 +46,9 @@ class _FirmwarePageState extends State { device: device, hasUpgrade: state.hasUpgrade(device), ), - builder: (context) => DeviceBody( + builder: (context) => FirmwareBodyPage.create( + context, device: device, - canVerify: device.canVerify, - onVerify: () => model.verify(device), - releases: state.getReleases(device) ?? [], - onInstall: (release) => model.install(device, release), - hasUpgrade: state.hasUpgrade(device), ), iconData: DeviceIcon.fromName(device.icon.firstOrNull), )) diff --git a/lib/src/widgets/device_body.dart b/lib/src/widgets/device_body.dart index 9d320386..3cb66a88 100644 --- a/lib/src/widgets/device_body.dart +++ b/lib/src/widgets/device_body.dart @@ -2,10 +2,11 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:fwupd/fwupd.dart'; +import 'package:provider/provider.dart'; +import '../../device_model.dart'; import '../../fwupd_l10n.dart'; import 'device_icon.dart'; -import 'release_dialog.dart'; class DeviceBody extends StatelessWidget { const DeviceBody({ @@ -118,21 +119,15 @@ class DeviceBody extends StatelessWidget { if (releases.isNotEmpty) hasUpgrade ? ElevatedButton( - onPressed: () => showReleaseDialog( - context, - device: device, - releases: releases, - onInstall: onInstall, - ), + onPressed: () => context + .read() + .selectedRelease = releases.first, child: Text(l10n.showUpdates), ) : OutlinedButton( - onPressed: () => showReleaseDialog( - context, - device: device, - releases: releases, - onInstall: onInstall, - ), + onPressed: () => context + .read() + .selectedRelease = releases.first, child: Text(l10n.showReleases), ), ], diff --git a/lib/src/widgets/release_dialog.dart b/lib/src/widgets/release_dialog.dart index beeb8e5a..e61fa1cc 100644 --- a/lib/src/widgets/release_dialog.dart +++ b/lib/src/widgets/release_dialog.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:fwupd/fwupd.dart'; +import 'package:provider/provider.dart'; +import '../../device_model.dart'; import '../../fwupd_x.dart'; import 'confirmation_dialog.dart'; import 'release_card.dart'; @@ -22,7 +24,7 @@ Future showReleaseDialog( ); } -class ReleaseDialog extends StatefulWidget { +class ReleaseDialog extends StatelessWidget { const ReleaseDialog({ super.key, required this.device, @@ -34,43 +36,37 @@ class ReleaseDialog extends StatefulWidget { final List releases; final ValueChanged onInstall; - @override - State createState() => _ReleaseDialogState(); -} - -class _ReleaseDialogState extends State { - FwupdRelease? _selected; - @override Widget build(BuildContext context) { + final selected = context.watch().selectedRelease; final l10n = AppLocalizations.of(context); final String action; final String dialogText; - if (_selected?.isDowngrade == true) { + if (selected?.isDowngrade == true) { action = l10n.downgrade; dialogText = l10n.downgradeConfirm( - widget.device.name, - widget.device.version, - _selected?.version, + device.name, + device.version, + selected?.version, ); - } else if (_selected?.isUpgrade == false) { + } else if (selected?.isUpgrade == false) { action = l10n.reinstall; dialogText = l10n.reinstallConfirm( - widget.device.name, - widget.device.version, + device.name, + device.version, ); } else { action = l10n.upgrade; dialogText = l10n.upgradeConfirm( - widget.device.name, - widget.device.version, - _selected?.version, + device.name, + device.version, + selected?.version, ); } return AlertDialog( - title: Text('${widget.device.name} ${widget.device.version}'), + title: Text('${device.name} ${device.version}'), titlePadding: const EdgeInsets.fromLTRB(16, 16, 16, 0), contentPadding: const EdgeInsets.all(4), content: SingleChildScrollView( @@ -78,12 +74,13 @@ class _ReleaseDialogState extends State { child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, - children: widget.releases.map((release) { + children: releases.map((release) { return Flexible( child: ReleaseCard( release: release, - selected: release == _selected, - onSelected: () => setState(() => _selected = release), + selected: release == selected, + onSelected: () => + context.read().selectedRelease = release, ), ); }).toList(), @@ -93,13 +90,13 @@ class _ReleaseDialogState extends State { actionsPadding: const EdgeInsets.fromLTRB(0, 0, 12, 12), actions: [ ElevatedButton( - onPressed: _selected != null + onPressed: selected != null ? () { showConfirmationDialog( context, text: dialogText, onConfirm: () { - widget.onInstall(_selected!); + onInstall(selected); Navigator.of(context).pop(); }, onCancel: () {}, From 53fec93283230f45ef95e396bbe3b29646ac6632 Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Tue, 20 Sep 2022 15:23:44 +0200 Subject: [PATCH 4/5] make FirmwareBodyPage a StatelessWidget --- lib/firmware_body_page.dart | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/firmware_body_page.dart b/lib/firmware_body_page.dart index a4ada521..e22d5da5 100644 --- a/lib/firmware_body_page.dart +++ b/lib/firmware_body_page.dart @@ -8,7 +8,7 @@ import 'fwupd_x.dart'; import 'src/widgets/device_body.dart'; import 'src/widgets/release_dialog.dart'; -class FirmwareBodyPage extends StatefulWidget { +class FirmwareBodyPage extends StatelessWidget { const FirmwareBodyPage({ super.key, }); @@ -24,11 +24,6 @@ class FirmwareBodyPage extends StatefulWidget { ); } - @override - State createState() => _FirmwareBodyPageState(); -} - -class _FirmwareBodyPageState extends State { @override Widget build(BuildContext context) { final deviceModel = context.watch(); From 3c3003c9141e1af78f026fabba1f55c07daba727 Mon Sep 17 00:00:00 2001 From: Dennis Loose Date: Tue, 20 Sep 2022 15:26:27 +0200 Subject: [PATCH 5/5] give IDs to test device --- test/device_model_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/device_model_test.dart b/test/device_model_test.dart index 3f0af5fa..723bf00b 100644 --- a/test/device_model_test.dart +++ b/test/device_model_test.dart @@ -26,7 +26,7 @@ void main() { }); test('install release', () async { - final device = testDevice(id: ''); + final device = testDevice(id: 'a'); final release = FwupdRelease(name: ''); final service = mockService(); @@ -38,7 +38,7 @@ void main() { }); test('verify', () async { - final device = testDevice(id: ''); + final device = testDevice(id: 'a'); final service = mockService();