diff options
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; |