Skip to content

Commit 070bc58

Browse files
committed
feat: Enhance OTA upgrade process with cancellation functionality and online status check
1 parent 2509375 commit 070bc58

File tree

3 files changed

+41
-7
lines changed

3 files changed

+41
-7
lines changed

client/lib/devices/borneo/lyfi/views/settings_screen.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ class SettingsScreen extends StatelessWidget {
268268
final otaVm = DeviceOtaViewModel(
269269
otaProvider: context.read<OtaProvider>(),
270270
boundDevice: bound,
271+
isOnlineProvider: () => svm.isOnline,
271272
eventBus: context.read<EventBus>(),
272273
gt: context.read<GettextLocalizations>(),
273274
logger: context.read<Logger>(),

client/lib/devices/view_models/device_ota_view_model.dart

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import 'dart:async';
22

33
import 'package:borneo_app/core/events/app_events.dart';
4+
import 'package:cancellation_token/cancellation_token.dart';
45
import 'package:flutter/foundation.dart';
56
import 'package:borneo_app/core/services/devices/ota_providers.dart';
67
import 'package:borneo_kernel_abstractions/models/bound_device.dart';
7-
import 'package:cancellation_token/cancellation_token.dart';
88
import 'package:event_bus/event_bus.dart';
99

1010
import '../../shared/view_models/base_view_model.dart';
@@ -15,6 +15,10 @@ class DeviceOtaViewModel extends BaseViewModel with ViewModelEventBusMixin {
1515
final OtaProvider otaProvider;
1616
final BoundDevice boundDevice;
1717

18+
/// Returns whether the device is currently reachable.
19+
/// Provided as a callback so the getter stays live after construction.
20+
final bool Function() _isOnlineProvider;
21+
1822
OtaState _state = OtaState.idle;
1923
OtaState get state => _state;
2024

@@ -35,19 +39,25 @@ class DeviceOtaViewModel extends BaseViewModel with ViewModelEventBusMixin {
3539

3640
bool get canCheck => _state != OtaState.checking && _state != OtaState.upgrading;
3741

38-
bool get canUpgrade => _state == OtaState.updateAvailable && (_upgradeInfo?.canUpgrade ?? false);
42+
bool get isOnline => _isOnlineProvider();
43+
44+
bool get canUpgrade => isOnline && _state == OtaState.updateAvailable && (_upgradeInfo?.canUpgrade ?? false);
3945

4046
/// In debug builds the user can force-push firmware even when already up to date.
4147
bool get canForceUpgrade =>
42-
kDebugMode && _upgradeInfo != null && (_state == OtaState.upToDate || _state == OtaState.updateAvailable);
48+
kDebugMode &&
49+
isOnline &&
50+
_upgradeInfo != null &&
51+
(_state == OtaState.upToDate || _state == OtaState.updateAvailable);
4352

4453
DeviceOtaViewModel({
4554
required this.otaProvider,
4655
required this.boundDevice,
56+
required bool Function() isOnlineProvider,
4757
required EventBus eventBus,
4858
required super.gt,
4959
super.logger,
50-
}) {
60+
}) : _isOnlineProvider = isOnlineProvider {
5161
super.globalEventBus = eventBus;
5262
}
5363

@@ -84,13 +94,23 @@ class DeviceOtaViewModel extends BaseViewModel with ViewModelEventBusMixin {
8494
await service.upgrade(boundDevice, cancelToken: _cancelToken, force: force);
8595
if (isDisposed) return;
8696
_setState(OtaState.success);
97+
} on CancelledException {
98+
if (isDisposed) return;
99+
// Restore to updateAvailable so the user can retry
100+
_setState(_upgradeInfo?.canUpgrade == true ? OtaState.updateAvailable : OtaState.upToDate);
87101
} catch (e, st) {
88102
_errorMessage = e.toString();
89103
_setState(OtaState.error);
90104
logger?.e('OTA upgrade failed', error: e, stackTrace: st);
91105
}
92106
}
93107

108+
/// Cancels an in-progress upgrade.
109+
void cancelUpgrade() {
110+
if (!isUpgrading) return;
111+
_cancelToken?.cancel();
112+
}
113+
94114
void _setState(OtaState newState) {
95115
_state = newState;
96116
if (!isDisposed) notifyListeners();

client/lib/devices/views/device_ota_screen.dart

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ class _DeviceOtaScreenState extends State<DeviceOtaScreen> with TickerProviderSt
116116
'Firmware update is in progress. Interrupting the update may permanently damage the device. Are you sure you want to abort?',
117117
),
118118
);
119-
if (confirmed && context.mounted) {
120-
Navigator.of(context).pop();
119+
if (confirmed) {
120+
vm.cancelUpgrade();
121121
}
122122
},
123123
child: Scaffold(
@@ -418,7 +418,20 @@ class _DeviceOtaScreenState extends State<DeviceOtaScreen> with TickerProviderSt
418418
if (vm.isUpgrading) {
419419
return SizedBox(
420420
width: double.infinity,
421-
child: FilledButton.tonal(onPressed: null, child: Text(context.translate('Updating...'))),
421+
child: FilledButton.tonal(
422+
onPressed: () async {
423+
final confirmed = await AsyncConfirmationSheet.show(
424+
context,
425+
message: context.translate(
426+
'Firmware update is in progress. Interrupting the update may permanently damage the device. Are you sure you want to cancel?',
427+
),
428+
);
429+
if (confirmed) {
430+
vm.cancelUpgrade();
431+
}
432+
},
433+
child: Text(context.translate('Cancel')),
434+
),
422435
);
423436
}
424437

0 commit comments

Comments
 (0)