fix(tv): make select activation one-shot
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user