fix(tv): make select activation one-shot

This commit is contained in:
edde746
2026-07-15 06:40:47 +02:00
parent 139cf83507
commit 3168f6327f
4 changed files with 73 additions and 10 deletions
+3 -5
View File
@@ -380,6 +380,9 @@ class _TvPinInputState extends State<_TvPinInput> with ControllerDisposerMixin {
final backResult = handleBackKeyAction(event, widget.onCancel);
if (backResult != KeyEventResult.ignored) return backResult;
if (event.isTvSelectEvent || (PlatformDetector.isTV() && event.isPhysicalKeyboardEnter)) {
return handleOneShotSelect(event, () => _activate(_rows[_row][_column]));
}
if (event is KeyDownEvent || event is KeyRepeatEvent) {
if (key == LogicalKeyboardKey.backspace || key == LogicalKeyboardKey.delete) {
@@ -387,11 +390,6 @@ class _TvPinInputState extends State<_TvPinInput> with ControllerDisposerMixin {
return KeyEventResult.handled;
}
if (event.isTvSelectEvent || (PlatformDetector.isTV() && event.isPhysicalKeyboardEnter)) {
_activate(_rows[_row][_column]);
return KeyEventResult.handled;
}
if (event.isPhysicalKeyboardEnter) {
_trySubmit();
return KeyEventResult.handled;
+4 -5
View File
@@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../focus/dpad_navigator.dart';
import '../focus/key_event_utils.dart';
import '../i18n/strings.g.dart';
import '../utils/platform_detector.dart';
import 'app_icon.dart';
@@ -348,6 +349,9 @@ class _TvVirtualKeyboardDialogState extends State<_TvVirtualKeyboardDialog> {
if (event is KeyUpEvent) Navigator.of(context).pop();
return KeyEventResult.handled;
}
if (event.isTvSelectEvent) {
return handleOneShotSelect(event, () => _activate(_rows[_row][_column]));
}
if (event is KeyDownEvent || event is KeyRepeatEvent) {
if (_handlePhysicalKeyboardTextInput(event)) return KeyEventResult.handled;
@@ -370,11 +374,6 @@ class _TvVirtualKeyboardDialogState extends State<_TvVirtualKeyboardDialog> {
return KeyEventResult.handled;
}
if (event.isTvSelectEvent) {
_activate(_rows[_row][_column]);
return KeyEventResult.handled;
}
if (key.isUpKey) {
_moveVertical(-1);
return KeyEventResult.handled;
+51
View File
@@ -219,6 +219,29 @@ void main() {
expect(find.byType(PinEntryDialog), findsNothing);
});
testWidgets('held D-pad select activates each PIN key once', (tester) async {
TvDetectionService.debugSetAppleTVOverride(true);
String? result;
await _pumpPinDialogLauncher(tester, onResult: (pin) => result = pin);
await tester.tap(find.text('Open'));
await tester.pumpAndSettle();
await _holdDpadSelect(tester); // 1
await _pressDpadKey(tester, LogicalKeyboardKey.arrowRight, PhysicalKeyboardKey.arrowRight);
await _pressDpadKey(tester, LogicalKeyboardKey.select, PhysicalKeyboardKey.select); // 2
await _pressDpadKey(tester, LogicalKeyboardKey.arrowRight, PhysicalKeyboardKey.arrowRight);
await _pressDpadKey(tester, LogicalKeyboardKey.select, PhysicalKeyboardKey.select); // 3
await _pressDpadKey(tester, LogicalKeyboardKey.arrowLeft, PhysicalKeyboardKey.arrowLeft);
await _pressDpadKey(tester, LogicalKeyboardKey.arrowLeft, PhysicalKeyboardKey.arrowLeft);
await _pressDpadKey(tester, LogicalKeyboardKey.arrowDown, PhysicalKeyboardKey.arrowDown);
await _pressDpadKey(tester, LogicalKeyboardKey.select, PhysicalKeyboardKey.select); // 4
await tester.pumpAndSettle();
expect(result, '1234');
expect(find.byType(PinEntryDialog), findsNothing);
});
testWidgets('Android TV keyboard-mapped enter/select activates highlighted PIN key', (tester) async {
TvDetectionService.debugSetAppleTVOverride(true);
String? result;
@@ -359,6 +382,34 @@ Future<void> _pressKey(WidgetTester tester, LogicalKeyboardKey key) async {
await tester.pump();
}
Future<void> _holdDpadSelect(WidgetTester tester) async {
_dispatchKey(
const KeyDownEvent(
physicalKey: PhysicalKeyboardKey.select,
logicalKey: LogicalKeyboardKey.select,
timeStamp: Duration.zero,
deviceType: ui.KeyEventDeviceType.directionalPad,
),
);
_dispatchKey(
const KeyRepeatEvent(
physicalKey: PhysicalKeyboardKey.select,
logicalKey: LogicalKeyboardKey.select,
timeStamp: Duration(milliseconds: 100),
deviceType: ui.KeyEventDeviceType.directionalPad,
),
);
_dispatchKey(
const KeyUpEvent(
physicalKey: PhysicalKeyboardKey.select,
logicalKey: LogicalKeyboardKey.select,
timeStamp: Duration(milliseconds: 200),
deviceType: ui.KeyEventDeviceType.directionalPad,
),
);
await tester.pump();
}
Future<void> _pressDpadKey(WidgetTester tester, LogicalKeyboardKey logicalKey, PhysicalKeyboardKey physicalKey) async {
_dispatchKey(
KeyDownEvent(
@@ -43,6 +43,21 @@ void main() {
expect(find.byType(Dialog), findsOneWidget);
});
testWidgets('held select activates the highlighted key once', (tester) async {
final controller = TextEditingController();
addTearDown(controller.dispose);
await _pumpKeyboard(tester, controller: controller);
await tester.sendKeyDownEvent(LogicalKeyboardKey.select);
await tester.sendKeyRepeatEvent(LogicalKeyboardKey.select);
await tester.sendKeyUpEvent(LogicalKeyboardKey.select);
await tester.pump();
expect(controller.text, '1');
expect(find.byType(Dialog), findsOneWidget);
});
testWidgets('directional pad enter activates highlighted key', (tester) async {
final controller = TextEditingController();
addTearDown(controller.dispose);