From 476995123e6beab55e086b21c7231c947f710d48 Mon Sep 17 00:00:00 2001 From: Gold87 <91761103+Gold872@users.noreply.github.com> Date: Sun, 27 Oct 2024 17:55:41 -0400 Subject: [PATCH] Added shortcuts for robot connection and opening settings (#123) Resolves #116 and #118 `Ctrl + ,` will open the settings dialog `Ctrl + K` will connect to the robot `Ctrl + Shift + K` will connect to sim --- lib/pages/dashboard_page.dart | 104 ++++++++++----- test/pages/dashboard_page_test.dart | 120 ++++++++++++++++++ .../shuffleboard_nt_listener_test.dart | 3 + test/test_util.dart | 4 +- 4 files changed, 201 insertions(+), 30 deletions(-) diff --git a/lib/pages/dashboard_page.dart b/lib/pages/dashboard_page.dart index 255987f3..cdf524e5 100644 --- a/lib/pages/dashboard_page.dart +++ b/lib/pages/dashboard_page.dart @@ -859,6 +859,51 @@ class _DashboardPageState extends State with WindowListener { }); }, ); + // Open settings dialog (Ctrl + ,) + hotKeyManager.register( + HotKey( + LogicalKeyboardKey.comma, + modifiers: [KeyModifier.control], + ), + callback: () { + if ((ModalRoute.of(context)?.isCurrent ?? false) && mounted) { + _displaySettingsDialog(context); + } + }, + ); + // Connect to robot (Ctrl + K) + hotKeyManager.register( + HotKey( + LogicalKeyboardKey.keyK, + modifiers: [KeyModifier.control], + ), + callback: () { + if (preferences.getInt(PrefKeys.ipAddressMode) == + IPAddressMode.driverStation.index) { + return; + } + _updateIPAddress(IPAddressUtil.teamNumberToIP( + preferences.getInt(PrefKeys.teamNumber) ?? Defaults.teamNumber)); + _changeIPAddressMode(IPAddressMode.driverStation); + }, + ); + // Connect to sim (Ctrl + Shift + K) + hotKeyManager.register( + HotKey( + LogicalKeyboardKey.keyK, + modifiers: [ + KeyModifier.control, + KeyModifier.shift, + ], + ), + callback: () { + if (preferences.getInt(PrefKeys.ipAddressMode) == + IPAddressMode.localhost.index) { + return; + } + _changeIPAddressMode(IPAddressMode.localhost); + }, + ); } void _lockLayout() async { @@ -975,36 +1020,8 @@ class _DashboardPageState extends State with WindowListener { if (mode.index == preferences.getInt(PrefKeys.ipAddressMode)) { return; } - await preferences.setInt(PrefKeys.ipAddressMode, mode.index); - - switch (mode) { - case IPAddressMode.driverStation: - String? lastAnnouncedIP = - widget.ntConnection.dsClient.lastAnnouncedIP; - - if (lastAnnouncedIP == null) { - break; - } - _updateIPAddress(lastAnnouncedIP); - break; - case IPAddressMode.roboRIOmDNS: - _updateIPAddress(IPAddressUtil.teamNumberToRIOmDNS( - preferences.getInt(PrefKeys.teamNumber) ?? - Defaults.teamNumber)); - break; - case IPAddressMode.teamNumber: - _updateIPAddress(IPAddressUtil.teamNumberToIP( - preferences.getInt(PrefKeys.teamNumber) ?? - Defaults.teamNumber)); - break; - case IPAddressMode.localhost: - _updateIPAddress('localhost'); - break; - default: - setState(() {}); - break; - } + _changeIPAddressMode(mode); }, onIPAddressChanged: (String? data) async { if (data == null || @@ -1163,6 +1180,35 @@ class _DashboardPageState extends State with WindowListener { ); } + void _changeIPAddressMode(IPAddressMode mode) async { + await preferences.setInt(PrefKeys.ipAddressMode, mode.index); + switch (mode) { + case IPAddressMode.driverStation: + String? lastAnnouncedIP = widget.ntConnection.dsClient.lastAnnouncedIP; + + if (lastAnnouncedIP == null) { + break; + } + + _updateIPAddress(lastAnnouncedIP); + break; + case IPAddressMode.roboRIOmDNS: + _updateIPAddress(IPAddressUtil.teamNumberToRIOmDNS( + preferences.getInt(PrefKeys.teamNumber) ?? Defaults.teamNumber)); + break; + case IPAddressMode.teamNumber: + _updateIPAddress(IPAddressUtil.teamNumberToIP( + preferences.getInt(PrefKeys.teamNumber) ?? Defaults.teamNumber)); + break; + case IPAddressMode.localhost: + _updateIPAddress('localhost'); + break; + default: + setState(() {}); + break; + } + } + void _updateIPAddress(String newIPAddress) async { if (newIPAddress == preferences.getString(PrefKeys.ipAddress)) { return; diff --git a/test/pages/dashboard_page_test.dart b/test/pages/dashboard_page_test.dart index 822b593e..5b2f94af 100644 --- a/test/pages/dashboard_page_test.dart +++ b/test/pages/dashboard_page_test.dart @@ -14,6 +14,7 @@ import 'package:titlebar_buttons/titlebar_buttons.dart'; import 'package:elastic_dashboard/pages/dashboard_page.dart'; import 'package:elastic_dashboard/services/field_images.dart'; import 'package:elastic_dashboard/services/hotkey_manager.dart'; +import 'package:elastic_dashboard/services/ip_address_util.dart'; import 'package:elastic_dashboard/services/nt4_client.dart'; import 'package:elastic_dashboard/services/nt_connection.dart'; import 'package:elastic_dashboard/services/settings.dart'; @@ -46,7 +47,9 @@ void main() { await FieldImages.loadFields('assets/fields/'); jsonString = jsonEncode(jsonDecode(File(jsonFilePath).readAsStringSync())); + }); + setUp(() async { SharedPreferences.setMockInitialValues({ PrefKeys.layout: jsonString, PrefKeys.teamNumber: 353, @@ -479,6 +482,7 @@ void main() { final mockSubscription = MockNT4Subscription(); when(mockNT4Connection.isNT4Connected).thenReturn(true); + when(mockNT4Connection.ntConnected).thenReturn(ValueNotifier(true)); when(mockNT4Connection.connectionStatus()) .thenAnswer((_) => Stream.value(true)); when(mockNT4Connection.latencyStream()).thenAnswer((_) => Stream.value(0)); @@ -1325,6 +1329,122 @@ void main() { expect(find.byType(SettingsDialog), findsOneWidget); }); + testWidgets('Opening settings (shortcut)', (widgetTester) async { + FlutterError.onError = ignoreOverflowErrors; + + await widgetTester.pumpWidget( + MaterialApp( + home: DashboardPage( + ntConnection: createMockOfflineNT4(), + preferences: preferences, + version: '0.0.0.0', + ), + ), + ); + + await widgetTester.pumpAndSettle(); + + await widgetTester.sendKeyDownEvent(LogicalKeyboardKey.control); + + await widgetTester.sendKeyDownEvent(LogicalKeyboardKey.comma); + await widgetTester.sendKeyUpEvent(LogicalKeyboardKey.comma); + + await widgetTester.sendKeyUpEvent(LogicalKeyboardKey.control); + + await widgetTester.pumpAndSettle(); + + expect(find.byType(SettingsDialog), findsOneWidget); + }); + + testWidgets('IP Address shortcuts', (widgetTester) async { + FlutterError.onError = ignoreOverflowErrors; + + SharedPreferences.setMockInitialValues({ + PrefKeys.ipAddressMode: IPAddressMode.custom.index, + PrefKeys.ipAddress: '127.0.0.1', + PrefKeys.teamNumber: 353, + }); + + MockNTConnection ntConnection = createMockOfflineNT4(); + MockDSInteropClient dsClient = MockDSInteropClient(); + when(dsClient.lastAnnouncedIP).thenReturn(null); + when(ntConnection.dsClient).thenReturn(dsClient); + + await widgetTester.pumpWidget( + MaterialApp( + home: DashboardPage( + ntConnection: ntConnection, + preferences: preferences, + version: '0.0.0.0', + ), + ), + ); + + await widgetTester.pumpAndSettle(); + + await widgetTester.sendKeyDownEvent(LogicalKeyboardKey.control); + + await widgetTester.sendKeyDownEvent(LogicalKeyboardKey.keyK); + await widgetTester.sendKeyUpEvent(LogicalKeyboardKey.keyK); + + await widgetTester.sendKeyUpEvent(LogicalKeyboardKey.control); + + await widgetTester.pumpAndSettle(); + + expect(preferences.getInt(PrefKeys.ipAddressMode), + IPAddressMode.driverStation.index); + expect(preferences.getString(PrefKeys.ipAddress), '10.3.53.2'); + + await preferences.setString(PrefKeys.ipAddress, '0.0.0.0'); + + await widgetTester.sendKeyDownEvent(LogicalKeyboardKey.control); + + await widgetTester.sendKeyDownEvent(LogicalKeyboardKey.keyK); + await widgetTester.sendKeyUpEvent(LogicalKeyboardKey.keyK); + + await widgetTester.sendKeyUpEvent(LogicalKeyboardKey.control); + + await widgetTester.pumpAndSettle(); + + // IP Address shouldn't change since it's already driver station + expect(preferences.getInt(PrefKeys.ipAddressMode), + IPAddressMode.driverStation.index); + expect(preferences.getString(PrefKeys.ipAddress), '0.0.0.0'); + + await widgetTester.sendKeyDownEvent(LogicalKeyboardKey.control); + await widgetTester.sendKeyDownEvent(LogicalKeyboardKey.shift); + + await widgetTester.sendKeyDownEvent(LogicalKeyboardKey.keyK); + await widgetTester.sendKeyUpEvent(LogicalKeyboardKey.keyK); + + await widgetTester.sendKeyUpEvent(LogicalKeyboardKey.control); + await widgetTester.sendKeyUpEvent(LogicalKeyboardKey.shift); + + await widgetTester.pumpAndSettle(); + + expect(preferences.getInt(PrefKeys.ipAddressMode), + IPAddressMode.localhost.index); + expect(preferences.getString(PrefKeys.ipAddress), 'localhost'); + + await preferences.setString(PrefKeys.ipAddress, '0.0.0.0'); + + await widgetTester.sendKeyDownEvent(LogicalKeyboardKey.control); + await widgetTester.sendKeyDownEvent(LogicalKeyboardKey.shift); + + await widgetTester.sendKeyDownEvent(LogicalKeyboardKey.keyK); + await widgetTester.sendKeyUpEvent(LogicalKeyboardKey.keyK); + + await widgetTester.sendKeyUpEvent(LogicalKeyboardKey.control); + await widgetTester.sendKeyUpEvent(LogicalKeyboardKey.shift); + + await widgetTester.pumpAndSettle(); + + // IP address shouldn't change since mode is set to localhost + expect(preferences.getInt(PrefKeys.ipAddressMode), + IPAddressMode.localhost.index); + expect(preferences.getString(PrefKeys.ipAddress), '0.0.0.0'); + }); + testWidgets( 'Robot Notifications', (widgetTester) async { diff --git a/test/services/shuffleboard_nt_listener_test.dart b/test/services/shuffleboard_nt_listener_test.dart index 77069d53..da34b293 100644 --- a/test/services/shuffleboard_nt_listener_test.dart +++ b/test/services/shuffleboard_nt_listener_test.dart @@ -1,3 +1,5 @@ +import 'package:flutter/foundation.dart'; + import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -34,6 +36,7 @@ void main() { .thenAnswer((_) => Stream.value(null)); when(mockNT4Connection.isNT4Connected).thenReturn(true); + when(mockNT4Connection.ntConnected).thenReturn(ValueNotifier(true)); when(mockNT4Connection.latencyStream()).thenAnswer((_) => Stream.value(0)); diff --git a/test/test_util.dart b/test/test_util.dart index 5e69dfdd..08dfcbd4 100644 --- a/test/test_util.dart +++ b/test/test_util.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; +import 'package:elastic_dashboard/services/ds_interop.dart'; import 'package:elastic_dashboard/services/nt4_client.dart'; import 'package:elastic_dashboard/services/nt_connection.dart'; import 'test_util.mocks.dart'; @@ -12,7 +13,8 @@ import 'test_util.mocks.dart'; @GenerateNiceMocks([ MockSpec(), MockSpec(), - MockSpec() + MockSpec(), + MockSpec(), ]) MockNTConnection createMockOfflineNT4() { HttpOverrides.global = null;