diff options
| author | 2024-04-12 16:25:59 +0000 | |
|---|---|---|
| committer | 2024-04-12 16:25:59 +0000 | |
| commit | 1e320e606e6de855e9499a9049cb55afc3d3ae7c (patch) | |
| tree | f92535bc89a6af1a1fa060e8c0cb673dec120c87 | |
| parent | d46319583fd20e7824c65fc6d24a250eb845542e (diff) | |
| parent | 605183403c05ddd4f9a44645338c17da0c27a715 (diff) | |
Merge "Change a11y step for the SeekBar in VolumeDialogImpl" into main
5 files changed, 153 insertions, 19 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Util.java b/packages/SystemUI/src/com/android/systemui/volume/Util.java index 7edb5a55a9ae..df19013ce2c6 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/Util.java +++ b/packages/SystemUI/src/com/android/systemui/volume/Util.java @@ -17,6 +17,7 @@ package com.android.systemui.volume; import android.media.AudioManager; +import android.util.MathUtils; import android.view.View; /** @@ -46,4 +47,27 @@ class Util extends com.android.settingslib.volume.Util { if (v == null || (v.getVisibility() == View.VISIBLE) == vis) return; v.setVisibility(vis ? View.VISIBLE : View.GONE); } + + /** + * Translates a value from one range to another. + * + * ``` + * Given: currentValue=3, currentRange=[0, 8], targetRange=[0, 100] + * Result: 37.5 + * ``` + */ + public static float translateToRange(float value, + float valueRangeStart, + float valueRangeEnd, + float targetRangeStart, + float targetRangeEnd) { + float currentRangeLength = valueRangeEnd - valueRangeStart; + float targetRangeLength = targetRangeEnd - targetRangeStart; + if (currentRangeLength == 0f || targetRangeLength == 0f) { + return targetRangeStart; + } + float valueFraction = (value - valueRangeStart) / currentRangeLength; + return MathUtils.constrain(targetRangeStart + valueFraction * targetRangeLength, + targetRangeStart, targetRangeEnd); + } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 1688b0b51b41..22455417b647 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -137,13 +137,13 @@ import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor; import com.android.systemui.volume.ui.navigation.VolumeNavigator; -import dagger.Lazy; - import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; +import dagger.Lazy; + /** * Visual presentation of the volume dialog. * @@ -166,6 +166,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, private static final int DRAWER_ANIMATION_DURATION_SHORT = 175; private static final int DRAWER_ANIMATION_DURATION = 250; + private static final int DISPLAY_RANGE_MULTIPLIER = 100; /** Shows volume dialog show animation. */ private static final String TYPE_SHOW = "show"; @@ -826,12 +827,14 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, writer.print(" mSilentMode: "); writer.println(mSilentMode); } - private static int getImpliedLevel(SeekBar seekBar, int progress) { - final int m = seekBar.getMax(); - final int n = m / 100 - 1; - final int level = progress == 0 ? 0 - : progress == m ? (m / 100) : (1 + (int) ((progress / (float) m) * n)); - return level; + private static int getVolumeFromProgress(StreamState state, SeekBar seekBar, int progress) { + return (int) Util.translateToRange(progress, seekBar.getMin(), seekBar.getMax(), + state.levelMin, state.levelMax); + } + + private static int getProgressFromVolume(StreamState state, SeekBar seekBar, int volume) { + return (int) Util.translateToRange(volume, state.levelMin, state.levelMax, seekBar.getMin(), + seekBar.getMax()); } @SuppressLint("InflateParams") @@ -854,6 +857,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, addSliderHapticsToRow(row); row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row)); row.number = row.view.findViewById(R.id.volume_number); + row.slider.setAccessibilityDelegate( + new VolumeDialogSeekBarAccessibilityDelegate(DISPLAY_RANGE_MULTIPLIER)); row.anim = null; @@ -1916,12 +1921,12 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, : false; // update slider max - final int max = ss.levelMax * 100; + final int max = ss.levelMax * DISPLAY_RANGE_MULTIPLIER; if (max != row.slider.getMax()) { row.slider.setMax(max); } // update slider min - final int min = ss.levelMin * 100; + final int min = ss.levelMin * DISPLAY_RANGE_MULTIPLIER; if (min != row.slider.getMin()) { row.slider.setMin(min); } @@ -2069,7 +2074,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, return; // don't update if user is sliding } final int progress = row.slider.getProgress(); - final int level = getImpliedLevel(row.slider, progress); + final int level = getVolumeFromProgress(row.ss, row.slider, progress); final boolean rowVisible = row.view.getVisibility() == VISIBLE; final boolean inGracePeriod = (SystemClock.uptimeMillis() - row.userAttempt) < USER_ATTEMPT_GRACE_PERIOD; @@ -2085,7 +2090,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, return; // don't clamp if visible } } - final int newProgress = vlevel * 100; + final int newProgress = getProgressFromVolume(row.ss, row.slider, vlevel); if (progress != newProgress) { if (mShowing && rowVisible) { // animate! @@ -2530,13 +2535,13 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, + " onProgressChanged " + progress + " fromUser=" + fromUser); if (!fromUser) return; if (mRow.ss.levelMin > 0) { - final int minProgress = mRow.ss.levelMin * 100; + final int minProgress = getProgressFromVolume(mRow.ss, seekBar, mRow.ss.levelMin); if (progress < minProgress) { seekBar.setProgress(minProgress); progress = minProgress; } } - final int userLevel = getImpliedLevel(seekBar, progress); + final int userLevel = getVolumeFromProgress(mRow.ss, seekBar, progress); if (mRow.ss.level != userLevel || mRow.ss.muted && userLevel > 0) { mRow.userAttempt = SystemClock.uptimeMillis(); if (mRow.requestedLevel != userLevel) { @@ -2569,7 +2574,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, } mRow.tracking = false; mRow.userAttempt = SystemClock.uptimeMillis(); - final int userLevel = getImpliedLevel(seekBar, seekBar.getProgress()); + final int userLevel = getVolumeFromProgress(mRow.ss, seekBar, seekBar.getProgress()); Events.writeEvent(Events.EVENT_TOUCH_LEVEL_DONE, mRow.stream, userLevel); if (mRow.ss.level != userLevel) { mHandler.sendMessageDelayed(mHandler.obtainMessage(H.RECHECK, mRow), diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogSeekBarAccessibilityDelegate.kt b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogSeekBarAccessibilityDelegate.kt new file mode 100644 index 000000000000..cd31a9531db6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogSeekBarAccessibilityDelegate.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume + +import android.os.Bundle +import android.view.View +import android.view.View.AccessibilityDelegate +import android.view.accessibility.AccessibilityNodeInfo +import android.widget.SeekBar +import com.android.internal.R + +class VolumeDialogSeekBarAccessibilityDelegate( + private val accessibilityStep: Int, +) : AccessibilityDelegate() { + + override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean { + require(host is SeekBar) { "This class only works with the SeekBar" } + val seekBar: SeekBar = host + if ( + action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD || + action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD + ) { + var increment = accessibilityStep + if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) { + increment = -increment + } + + return super.performAccessibilityAction( + host, + R.id.accessibilityActionSetProgress, + Bundle().apply { + putFloat( + AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE, + (seekBar.progress + increment).coerceIn(seekBar.min, seekBar.max).toFloat(), + ) + }, + ) + } + return super.performAccessibilityAction(host, action, args) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java index fb82b8fed76c..483dc0c9c974 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java @@ -15,14 +15,14 @@ */ package com.android.systemui.volume; +import static com.google.common.truth.Truth.assertThat; + import android.media.MediaMetadata; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import junit.framework.Assert; - import org.junit.Test; @SmallTest @@ -30,11 +30,59 @@ public class UtilTest extends SysuiTestCase { @Test public void testMediaMetadataToString_null() { - Assert.assertEquals(null, Util.mediaMetadataToString(null)); + assertThat(Util.mediaMetadataToString(null)).isNull(); } @Test public void testMediaMetadataToString_notNull() { - Assert.assertNotNull(Util.mediaMetadataToString(new MediaMetadata.Builder().build())); + assertThat(Util.mediaMetadataToString(new MediaMetadata.Builder().build())).isNotNull(); + } + + @Test + public void translateToRange_translatesStartToStart() { + assertThat( + (int) Util.translateToRange( + /* value= */ 0, + /* valueRangeStart= */ 0, + /* valueRangeEnd= */ 7, + /* targetRangeStart= */ 0, + /* targetRangeEnd= */700) + ).isEqualTo(0); + } + + @Test + public void translateToRange_translatesValueToValue() { + assertThat( + (int) Util.translateToRange( + /* value= */ 4, + /* valueRangeStart= */ 0, + /* valueRangeEnd= */ 7, + /* targetRangeStart= */ 0, + /* targetRangeEnd= */700) + ).isEqualTo(400); + } + + @Test + public void translateToRange_translatesEndToEnd() { + assertThat( + (int) Util.translateToRange( + /* value= */ 7, + /* valueRangeStart= */ 0, + /* valueRangeEnd= */ 7, + /* targetRangeStart= */ 0, + /* targetRangeEnd= */700) + ).isEqualTo(700); + } + + @Test + public void translateToRange_returnsStartForEmptyRange() { + assertThat( + (int) Util.translateToRange( + /* value= */ 7, + /* valueRangeStart= */ 7, + /* valueRangeEnd= */ 7, + /* targetRangeStart= */ 700, + /* targetRangeEnd= */700) + ).isEqualTo(700); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index ed2fb2c0cfc9..3b468aa011ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -248,6 +248,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState(); ss.name = STREAMS.get(i); ss.level = 1; + ss.levelMin = 0; + ss.levelMax = 25; state.states.append(i, ss); } return state; |