Skip to content

Commit

Permalink
Added shortcuts for robot connection and opening settings (#123)
Browse files Browse the repository at this point in the history
Resolves #116 and #118

`Ctrl + ,` will open the settings dialog
`Ctrl + K` will connect to the robot
`Ctrl + Shift + K` will connect to sim
  • Loading branch information
Gold872 authored Oct 27, 2024
1 parent a96201d commit 4769951
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 30 deletions.
104 changes: 75 additions & 29 deletions lib/pages/dashboard_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,51 @@ class _DashboardPageState extends State<DashboardPage> 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 {
Expand Down Expand Up @@ -975,36 +1020,8 @@ class _DashboardPageState extends State<DashboardPage> 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 ||
Expand Down Expand Up @@ -1163,6 +1180,35 @@ class _DashboardPageState extends State<DashboardPage> 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;
Expand Down
120 changes: 120 additions & 0 deletions test/pages/dashboard_page_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions test/services/shuffleboard_nt_listener_test.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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));

Expand Down
4 changes: 3 additions & 1 deletion test/test_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ 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';

@GenerateNiceMocks([
MockSpec<NTConnection>(),
MockSpec<NT4Client>(),
MockSpec<NT4Subscription>()
MockSpec<NT4Subscription>(),
MockSpec<DSInteropClient>(),
])
MockNTConnection createMockOfflineNT4() {
HttpOverrides.global = null;
Expand Down

0 comments on commit 4769951

Please sign in to comment.