From aa623f0facdc4165fa2163df4fd4e4657423e8f1 Mon Sep 17 00:00:00 2001 From: Material Design Team Date: Mon, 20 Oct 2025 11:46:01 +0000 Subject: [PATCH] [Slider][A11y] Fix slider D-Pad keys to modify value instead of focus Previously, Up/Down arrow keys navigated focus instead of changing the slider value. This change updates keyboard handling so all D-Pad keys modify the slider value: Up/Down increment/decrement the value, and Left/Right increment/decrement based on layout direction. PiperOrigin-RevId: 821593460 --- .../android/material/slider/BaseSlider.java | 14 +++--- .../material/slider/SliderKeyTestCommon.java | 48 +++++++++---------- .../material/slider/SliderKeyTestLtr.java | 24 ++++++++++ .../material/slider/SliderKeyTestRtl.java | 24 ++++++++++ 4 files changed, 78 insertions(+), 32 deletions(-) diff --git a/lib/java/com/google/android/material/slider/BaseSlider.java b/lib/java/com/google/android/material/slider/BaseSlider.java index 3eb083d14..9df0d77b2 100644 --- a/lib/java/com/google/android/material/slider/BaseSlider.java +++ b/lib/java/com/google/android/material/slider/BaseSlider.java @@ -3877,20 +3877,18 @@ abstract class BaseSlider< // Otherwise choose the smallest valid increment. float increment = isLongPress ? calculateStepIncrement(20) : calculateStepIncrement(); switch (keyCode) { + // Numpad Plus == Shift + Equals, at least in AVD, so fall through. + case KeyEvent.KEYCODE_PLUS: case KeyEvent.KEYCODE_DPAD_UP: - return isVertical() ? increment : null; + case KeyEvent.KEYCODE_EQUALS: + return increment; case KeyEvent.KEYCODE_DPAD_DOWN: - return isVertical() ? -increment : null; + case KeyEvent.KEYCODE_MINUS: + return -increment; case KeyEvent.KEYCODE_DPAD_LEFT: return isRtl() ? increment : -increment; case KeyEvent.KEYCODE_DPAD_RIGHT: return isRtl() ? -increment : increment; - case KeyEvent.KEYCODE_MINUS: - return -increment; - case KeyEvent.KEYCODE_EQUALS: - // Numpad Plus == Shift + Equals, at least in AVD, so fall through. - case KeyEvent.KEYCODE_PLUS: - return increment; default: return null; } diff --git a/lib/javatests/com/google/android/material/slider/SliderKeyTestCommon.java b/lib/javatests/com/google/android/material/slider/SliderKeyTestCommon.java index d923cfa60..e7d77afa5 100644 --- a/lib/javatests/com/google/android/material/slider/SliderKeyTestCommon.java +++ b/lib/javatests/com/google/android/material/slider/SliderKeyTestCommon.java @@ -126,30 +126,6 @@ public abstract class SliderKeyTestCommon { sendKeyEventThereAndBack(tab, shiftTab); } - @Test - public void testFocusDefaultThumb_clickUpDPad_unhandled() { - slider.requestFocus(); - - KeyEventBuilder up = new KeyEventBuilder(KeyEvent.KEYCODE_DPAD_UP); - boolean handledDown = slider.dispatchKeyEvent(up.buildDown()); - boolean handledUp = slider.dispatchKeyEvent(up.buildUp()); - - assertThat(handledDown).isFalse(); - assertThat(handledUp).isFalse(); - } - - @Test - public void testFocusDefaultThumb_clickDownDPad_unhandled() { - slider.requestFocus(); - - KeyEventBuilder down = new KeyEventBuilder(KeyEvent.KEYCODE_DPAD_DOWN); - boolean handledDown = slider.dispatchKeyEvent(down.buildDown()); - boolean handledUp = slider.dispatchKeyEvent(down.buildUp()); - - assertThat(handledDown).isFalse(); - assertThat(handledUp).isFalse(); - } - @Test public void testFocusFirstThumb_shiftTab_unhandled() { slider.requestFocus(); @@ -179,6 +155,30 @@ public abstract class SliderKeyTestCommon { assertThat(handledUp).isFalse(); } + @Test + public void testKeyPress_dPadUp_incrementsValue() { + slider.requestFocus(); + slider.setValues(50f); + slider.setStepSize(1f); + + KeyEventBuilder up = new KeyEventBuilder(KeyEvent.KEYCODE_DPAD_UP); + up.dispatchEvent(slider); + + assertThat(slider.getValues().get(0)).isEqualTo(51f); + } + + @Test + public void testKeyPress_dPadDown_decrementsValue() { + slider.requestFocus(); + slider.setValues(50f); + slider.setStepSize(1f); + + KeyEventBuilder down = new KeyEventBuilder(KeyEvent.KEYCODE_DPAD_DOWN); + down.dispatchEvent(slider); + + assertThat(slider.getValues().get(0)).isEqualTo(49f); + } + protected void sendKeyEventThereAndBack(KeyEventBuilder there, KeyEventBuilder back) { for (int i = 1; i < countTestValues(); i++) { there.dispatchEvent(slider); diff --git a/lib/javatests/com/google/android/material/slider/SliderKeyTestLtr.java b/lib/javatests/com/google/android/material/slider/SliderKeyTestLtr.java index cc2de919c..b195a5197 100644 --- a/lib/javatests/com/google/android/material/slider/SliderKeyTestLtr.java +++ b/lib/javatests/com/google/android/material/slider/SliderKeyTestLtr.java @@ -72,4 +72,28 @@ public final class SliderKeyTestLtr extends SliderKeyTestCommon { assertThat(slider.getValues()).doesNotContain(3.0f); } + + @Test + public void testKeyPress_dPadLeft_decrementsValue() { + slider.requestFocus(); + slider.setValues(50f); + slider.setStepSize(1f); + + KeyEventBuilder left = new KeyEventBuilder(KeyEvent.KEYCODE_DPAD_LEFT); + left.dispatchEvent(slider); + + assertThat(slider.getValues().get(0)).isEqualTo(49f); + } + + @Test + public void testKeyPress_dPadRight_incrementsValue() { + slider.requestFocus(); + slider.setValues(50f); + slider.setStepSize(1f); + + KeyEventBuilder right = new KeyEventBuilder(KeyEvent.KEYCODE_DPAD_RIGHT); + right.dispatchEvent(slider); + + assertThat(slider.getValues().get(0)).isEqualTo(51f); + } } diff --git a/lib/javatests/com/google/android/material/slider/SliderKeyTestRtl.java b/lib/javatests/com/google/android/material/slider/SliderKeyTestRtl.java index bb3a3b2d9..9ce5f9570 100644 --- a/lib/javatests/com/google/android/material/slider/SliderKeyTestRtl.java +++ b/lib/javatests/com/google/android/material/slider/SliderKeyTestRtl.java @@ -91,4 +91,28 @@ public final class SliderKeyTestRtl extends SliderKeyTestCommon { // Moving left increments in RTL. assertThat(slider.getValues()).doesNotContain(3.0f); } + + @Test + public void testKeyPress_dPadLeft_incrementsValue() { + slider.requestFocus(); + slider.setValues(50f); + slider.setStepSize(1f); + + KeyEventBuilder left = new KeyEventBuilder(KeyEvent.KEYCODE_DPAD_LEFT); + left.dispatchEvent(slider); + + assertThat(slider.getValues().get(0)).isEqualTo(51f); + } + + @Test + public void testKeyPress_dPadRight_decrementsValue() { + slider.requestFocus(); + slider.setValues(50f); + slider.setStepSize(1f); + + KeyEventBuilder right = new KeyEventBuilder(KeyEvent.KEYCODE_DPAD_RIGHT); + right.dispatchEvent(slider); + + assertThat(slider.getValues().get(0)).isEqualTo(49f); + } }