diff --git a/lib/android/management/state.dart b/lib/android/management/state.dart index 1806039d..bd5b55fa 100755 --- a/lib/android/management/state.dart +++ b/lib/android/management/state.dart @@ -25,8 +25,10 @@ class _AndroidManagementStateNotifier extends ManagementStateNotifier { void refresh() async {} @override - Future setMode(int mode, - {int challengeResponseTimeout = 0, int autoEjectTimeout = 0}) async {} + Future setMode( + {required int interfaces, + int challengeResponseTimeout = 0, + int? autoEjectTimeout}) async {} @override Future writeConfig(DeviceConfig config, diff --git a/lib/desktop/management/state.dart b/lib/desktop/management/state.dart index 465bbfd2..821600fa 100755 --- a/lib/desktop/management/state.dart +++ b/lib/desktop/management/state.dart @@ -68,10 +68,12 @@ class _DesktopManagementStateNotifier extends ManagementStateNotifier { }); @override - Future setMode(int mode, - {int challengeResponseTimeout = 0, int autoEjectTimeout = 0}) async { + Future setMode( + {required int interfaces, + int challengeResponseTimeout = 0, + int? autoEjectTimeout}) async { await _session.command('set_mode', target: _subpath, params: { - 'mode': mode, + 'interfaces': interfaces, 'challenge_response_timeout': challengeResponseTimeout, 'auto_eject_timeout': autoEjectTimeout, }); diff --git a/lib/management/state.dart b/lib/management/state.dart index 37e66349..dab5499a 100755 --- a/lib/management/state.dart +++ b/lib/management/state.dart @@ -16,6 +16,9 @@ abstract class ManagementStateNotifier String newLockCode = '', bool reboot = false}); - Future setMode(int mode, - {int challengeResponseTimeout = 0, int autoEjectTimeout = 0}); + Future setMode({ + required int interfaces, + int challengeResponseTimeout = 0, + int? autoEjectTimeout, + }); } diff --git a/lib/management/views/management_screen.dart b/lib/management/views/management_screen.dart index fc2d9f3e..94752688 100755 --- a/lib/management/views/management_screen.dart +++ b/lib/management/views/management_screen.dart @@ -45,53 +45,25 @@ class _CapabilityForm extends StatelessWidget { } } -class _ModeForm extends StatefulWidget { - final int initialInterfaces; - final Function(int) onSubmit; - const _ModeForm(this.initialInterfaces, {required this.onSubmit, Key? key}) +class _ModeForm extends StatelessWidget { + final int interfaces; + final Function(int) onChanged; + const _ModeForm(this.interfaces, {required this.onChanged, Key? key}) : super(key: key); - @override - State createState() => _ModeFormState(); -} - -class _ModeFormState extends State<_ModeForm> { - int _enabledInterfaces = 0; - - @override - void initState() { - super.initState(); - _enabledInterfaces = widget.initialInterfaces; - } - @override Widget build(BuildContext context) { - final valid = _enabledInterfaces != 0 && - _enabledInterfaces != widget.initialInterfaces; return Column(children: [ ...UsbInterface.values.map( (iface) => CheckboxListTile( title: Text(iface.name.toUpperCase()), - value: iface.value & _enabledInterfaces != 0, + value: iface.value & interfaces != 0, onChanged: (_) { - setState(() { - _enabledInterfaces ^= iface.value; - }); + onChanged(interfaces ^ iface.value); }, ), ), - Container( - padding: const EdgeInsets.all(16.0), - alignment: Alignment.centerRight, - child: ElevatedButton( - onPressed: valid - ? () { - widget.onSubmit(_enabledInterfaces); - } - : null, - child: const Text('Apply changes'), - ), - ) + Text(interfaces == 0 ? 'At least one interface must be enabled' : ''), ]); } } @@ -156,11 +128,14 @@ class ManagementScreen extends ConsumerStatefulWidget { class _ManagementScreenState extends ConsumerState { late Map _enabled; + late int _interfaces; @override void initState() { super.initState(); _enabled = widget.deviceData.info.config.enabledCapabilities; + _interfaces = UsbInterfaces.forCapabilites( + widget.deviceData.info.config.enabledCapabilities[Transport.usb] ?? 0); } Widget _buildCapabilitiesForm( @@ -216,11 +191,29 @@ class _ManagementScreenState extends ConsumerState { Widget _buildModeForm(BuildContext context, WidgetRef ref, DeviceInfo info) => _ModeForm( - UsbInterfaces.forCapabilites( - info.config.enabledCapabilities[Transport.usb] ?? 0), - onSubmit: (enabledInterfaces) { - showMessage(context, 'Not yet implemented!'); - }); + _interfaces, + onChanged: (interfaces) { + setState(() { + _interfaces = interfaces; + }); + }, + ); + + void _submitModeForm() async { + await ref + .read(managementStateProvider(widget.deviceData.node.path).notifier) + .setMode(interfaces: _interfaces); + showMessage( + context, 'Configuration updated, remove and reinsert your YubiKey'); + } + + void _submitForm() { + if (widget.deviceData.info.version.major > 4) { + _submitCapabilitiesForm(); + } else { + _submitModeForm(); + } + } @override Widget build(BuildContext context) { @@ -229,7 +222,7 @@ class _ManagementScreenState extends ConsumerState { Navigator.of(context).popUntil((route) => route.isFirst); }); - bool changed = false; + bool canSave = false; return ResponsiveDialog( title: const Text('Toggle applications'), @@ -238,14 +231,26 @@ class _ManagementScreenState extends ConsumerState { loading: () => const AppLoadingScreen(), error: (error, _) => AppFailureScreen('$error'), data: (info) { + bool hasConfig = info.version.major > 4; // TODO: Check mode for < YK5 intead - changed = !_mapEquals( - _enabled, - info.config.enabledCapabilities, - ); + if (hasConfig) { + canSave = !_mapEquals( + _enabled, + info.config.enabledCapabilities, + ); + } else { + canSave = _interfaces != 0 && + _interfaces != + UsbInterfaces.forCapabilites(widget + .deviceData + .info + .config + .enabledCapabilities[Transport.usb] ?? + 0); + } return Column( children: [ - info.version.major > 4 + hasConfig ? _buildCapabilitiesForm(context, ref, info) : _buildModeForm(context, ref, info), ], @@ -254,7 +259,7 @@ class _ManagementScreenState extends ConsumerState { ), actions: [ TextButton( - onPressed: changed ? _submitCapabilitiesForm : null, + onPressed: canSave ? _submitForm : null, child: const Text('Save'), ), ],