diff --git a/aconfig/accessibility/accessibility_flags.aconfig b/aconfig/accessibility/accessibility_flags.aconfig
index 2c92547474d..3ed618bd6e0 100644
--- a/aconfig/accessibility/accessibility_flags.aconfig
+++ b/aconfig/accessibility/accessibility_flags.aconfig
@@ -10,6 +10,16 @@ flag {
bug: "332974327"
}
+flag {
+ name: "audio_balance_state_description"
+ namespace: "accessibility"
+ description: "Provides a more valuable state description to the audio balance slider."
+ bug: "319575109"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
flag {
name: "edit_shortcuts_in_full_screen"
namespace: "accessibility"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fd02785108d..6481193d493 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -13483,6 +13483,10 @@
Off
On
+
+ Audio %1$d%% left, %2$d%% right
+
+ Audio %1$d%% right, %2$d%% left
Your device name is visible to apps you installed. It may also be seen by other people when you connect to Bluetooth devices, connect to a Wi-Fi network or set up a Wi-Fi hotspot.
diff --git a/src/com/android/settings/accessibility/BalanceSeekBar.java b/src/com/android/settings/accessibility/BalanceSeekBar.java
index 19301ae9ea6..7441d6fe9e2 100644
--- a/src/com/android/settings/accessibility/BalanceSeekBar.java
+++ b/src/com/android/settings/accessibility/BalanceSeekBar.java
@@ -20,6 +20,7 @@ import static android.view.HapticFeedbackConstants.CLOCK_TICK;
import static com.android.settings.Utils.isNightMode;
+import android.annotation.StringRes;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -86,6 +87,14 @@ public class BalanceSeekBar extends SeekBar {
Settings.System.putFloatForUser(mContext.getContentResolver(),
Settings.System.MASTER_BALANCE, balance, UserHandle.USER_CURRENT);
}
+ final int max = getMax();
+ if (Flags.audioBalanceStateDescription() && max > 0) {
+ seekBar.setStateDescription(createStateDescription(mContext,
+ R.string.audio_seek_bar_state_left_first,
+ R.string.audio_seek_bar_state_right_first,
+ progress,
+ max));
+ }
// If fromUser is false, the call is a set from the framework on creation or on
// internal update. The progress may be zero, ignore (don't change system settings).
@@ -161,5 +170,19 @@ public class BalanceSeekBar extends SeekBar {
canvas.restore();
super.onDraw(canvas);
}
+
+ private static CharSequence createStateDescription(Context context,
+ @StringRes int resIdLeftFirst, @StringRes int resIdRightFirst,
+ int progress, float max) {
+ final boolean isLayoutRtl = context.getResources().getConfiguration().getLayoutDirection()
+ == LAYOUT_DIRECTION_RTL;
+ final int rightPercent = (int) (100 * (progress / max));
+ final int leftPercent = 100 - rightPercent;
+ if (rightPercent > leftPercent || (rightPercent == leftPercent && isLayoutRtl)) {
+ return context.getString(resIdRightFirst, rightPercent, leftPercent);
+ } else {
+ return context.getString(resIdLeftFirst, leftPercent, rightPercent);
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/BalanceSeekBarTest.java b/tests/robotests/src/com/android/settings/accessibility/BalanceSeekBarTest.java
index ce2a5712434..d74794f0363 100644
--- a/tests/robotests/src/com/android/settings/accessibility/BalanceSeekBarTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/BalanceSeekBarTest.java
@@ -28,23 +28,32 @@ import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;
import android.content.Context;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.AttributeSet;
import android.widget.SeekBar;
+import com.android.settings.R;
import com.android.settings.testutils.shadow.ShadowSystemSettings;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import java.util.Locale;
+
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
ShadowSystemSettings.class,
})
public class BalanceSeekBarTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
// Fix the maximum process value to 200 for testing the BalanceSeekBar.
// It affects the SeekBar value of center(100) and snapThreshold(200 * SNAP_TO_PERCENTAGE).
private static final int MAX_PROGRESS_VALUE = 200;
@@ -143,6 +152,58 @@ public class BalanceSeekBarTest {
assertThat(mSeekBar.getProgress()).isEqualTo(progressWithoutThreshold);
}
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_BALANCE_STATE_DESCRIPTION)
+ public void onProgressChanged_getStateDescription_centered_leftFirst() {
+ // Seek bar centered
+ int progress = (int) (0.50f * MAX_PROGRESS_VALUE);
+ mSeekBar.setMax(MAX_PROGRESS_VALUE);
+
+ mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
+
+ assertThat(mSeekBar.getStateDescription()).isEqualTo(
+ mContext.getString(R.string.audio_seek_bar_state_left_first, 50, 50));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_BALANCE_STATE_DESCRIPTION)
+ public void onProgressChanged_getStateDescription_centered_rtl_rightFirst() {
+ // RTL layout
+ mContext.getResources().getConfiguration().setLayoutDirection(new Locale("iw", "IL"));
+ // Seek bar centered
+ int progress = (int) (0.50f * MAX_PROGRESS_VALUE);
+ mSeekBar.setMax(MAX_PROGRESS_VALUE);
+
+ mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
+
+ assertThat(mSeekBar.getStateDescription()).isEqualTo(
+ mContext.getString(R.string.audio_seek_bar_state_right_first, 50, 50));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_BALANCE_STATE_DESCRIPTION)
+ public void onProgressChanged_getStateDescription_25percent_leftFirst() {
+ // Seek bar 3/4th toward the left
+ int progress = (int) (0.25f * MAX_PROGRESS_VALUE);
+ mSeekBar.setMax(MAX_PROGRESS_VALUE);
+ mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
+
+ assertThat(mSeekBar.getStateDescription()).isEqualTo(
+ mContext.getString(R.string.audio_seek_bar_state_left_first, 75, 25));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUDIO_BALANCE_STATE_DESCRIPTION)
+ public void onProgressChanged_getStateDescription_75percent_rightFirst() {
+ // Seek bar 3/4th toward the right
+ int progress = (int) (0.75f * MAX_PROGRESS_VALUE);
+ mSeekBar.setMax(MAX_PROGRESS_VALUE);
+ mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
+
+ assertThat(mSeekBar.getStateDescription()).isEqualTo(
+ mContext.getString(R.string.audio_seek_bar_state_right_first, 75, 25));
+ }
+
// method to get the center from BalanceSeekBar for testing setMax().
private int getBalanceSeekBarCenter(BalanceSeekBar seekBar) {
return seekBar.getMax() / 2;