From 903b3025a7e5caaddae848376bd12b62e698315c Mon Sep 17 00:00:00 2001 From: Behnam Heydarshahi Date: Mon, 7 Nov 2022 20:43:43 +0000 Subject: [PATCH] Change volume ringer icon based on device config When device config volume_separate_notification is enabled, change the ringer drawer icon, so it covers both ring and notification, and not just one. When not enabled, the ringer icon should remain/switch to the old bell icon. Also, the icon on the secondary vertical progressbar follows the device config flag, matching the horizontal slider for ring: https://screenshot.googleplex.com/6N5CPjSCR4P5DbZ.png Bug: b/38477228 Test: atest VolumeDialogImplTest Change-Id: I2d1d7348d3d6506fda98394a2ddf18461f575fc2 --- .../SystemUI/res/drawable/ic_ring_volume.xml | 26 ++++ .../res/drawable/ic_ring_volume_off.xml | 34 ++++++ .../SystemUI/res/drawable/ic_speaker_mute.xml | 25 ++++ .../SystemUI/res/drawable/ic_speaker_on.xml | 25 ++++ .../systemui/volume/VolumeDialogImpl.java | 111 ++++++++++++++++-- .../systemui/volume/dagger/VolumeModule.java | 12 +- .../systemui/volume/VolumeDialogImplTest.java | 47 +++++++- 7 files changed, 268 insertions(+), 12 deletions(-) create mode 100644 packages/SystemUI/res/drawable/ic_ring_volume.xml create mode 100644 packages/SystemUI/res/drawable/ic_ring_volume_off.xml create mode 100644 packages/SystemUI/res/drawable/ic_speaker_mute.xml create mode 100644 packages/SystemUI/res/drawable/ic_speaker_on.xml diff --git a/packages/SystemUI/res/drawable/ic_ring_volume.xml b/packages/SystemUI/res/drawable/ic_ring_volume.xml new file mode 100644 index 0000000000000..343fe5d4cb698 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_ring_volume.xml @@ -0,0 +1,26 @@ + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_ring_volume_off.xml b/packages/SystemUI/res/drawable/ic_ring_volume_off.xml new file mode 100644 index 0000000000000..74f30d1a44d25 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_ring_volume_off.xml @@ -0,0 +1,34 @@ + + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_speaker_mute.xml b/packages/SystemUI/res/drawable/ic_speaker_mute.xml new file mode 100644 index 0000000000000..4e402cf530e4b --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_speaker_mute.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_speaker_on.xml b/packages/SystemUI/res/drawable/ic_speaker_on.xml new file mode 100644 index 0000000000000..2a90e051b83bc --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_speaker_on.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 0d9627248a287..63b98bb841358 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -72,6 +72,7 @@ import android.os.Message; import android.os.SystemClock; import android.os.Trace; import android.os.VibrationEffect; +import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.Global; import android.text.InputFilter; @@ -108,6 +109,8 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.graphics.drawable.BackgroundBlurDrawable; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.view.RotationPolicy; @@ -125,11 +128,15 @@ import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.util.AlphaTintDrawableWrapper; +import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.RoundedCornerProgressDrawable; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.Executor; import java.util.function.Consumer; /** @@ -186,6 +193,9 @@ public class VolumeDialogImpl implements VolumeDialog, private ViewGroup mDialogRowsView; private ViewGroup mRinger; + private DeviceConfigProxy mDeviceConfigProxy; + private Executor mExecutor; + /** * Container for the top part of the dialog, which contains the ringer, the ringer drawer, the * volume rows, and the ellipsis button. This does not include the live caption button. @@ -274,6 +284,13 @@ public class VolumeDialogImpl implements VolumeDialog, private BackgroundBlurDrawable mDialogRowsViewBackground; private final InteractionJankMonitor mInteractionJankMonitor; + private boolean mSeparateNotification; + + @VisibleForTesting + int mVolumeRingerIconDrawableId; + @VisibleForTesting + int mVolumeRingerMuteIconDrawableId; + public VolumeDialogImpl( Context context, VolumeDialogController volumeDialogController, @@ -283,7 +300,9 @@ public class VolumeDialogImpl implements VolumeDialog, MediaOutputDialogFactory mediaOutputDialogFactory, VolumePanelFactory volumePanelFactory, ActivityStarter activityStarter, - InteractionJankMonitor interactionJankMonitor) { + InteractionJankMonitor interactionJankMonitor, + DeviceConfigProxy deviceConfigProxy, + Executor executor) { mContext = new ContextThemeWrapper(context, R.style.volume_dialog_theme); mController = volumeDialogController; @@ -323,6 +342,50 @@ public class VolumeDialogImpl implements VolumeDialog, } initDimens(); + + mDeviceConfigProxy = deviceConfigProxy; + mExecutor = executor; + mSeparateNotification = mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false); + updateRingerModeIconSet(); + } + + /** + * If ringer and notification are the same stream (T and earlier), use notification-like bell + * icon set. + * If ringer and notification are separated, then use generic speaker icons. + */ + private void updateRingerModeIconSet() { + if (mSeparateNotification) { + mVolumeRingerIconDrawableId = R.drawable.ic_speaker_on; + mVolumeRingerMuteIconDrawableId = R.drawable.ic_speaker_mute; + } else { + mVolumeRingerIconDrawableId = R.drawable.ic_volume_ringer; + mVolumeRingerMuteIconDrawableId = R.drawable.ic_volume_ringer_mute; + } + + if (mRingerDrawerMuteIcon != null) { + mRingerDrawerMuteIcon.setImageResource(mVolumeRingerMuteIconDrawableId); + } + if (mRingerDrawerNormalIcon != null) { + mRingerDrawerNormalIcon.setImageResource(mVolumeRingerIconDrawableId); + } + } + + /** + * Change icon for ring stream (not ringer mode icon) + */ + private void updateRingRowIcon() { + Optional volumeRow = mRows.stream().filter(row -> row.stream == STREAM_RING) + .findFirst(); + if (volumeRow.isPresent()) { + VolumeRow volRow = volumeRow.get(); + volRow.iconRes = mSeparateNotification ? R.drawable.ic_ring_volume + : R.drawable.ic_volume_ringer; + volRow.iconMuteRes = mSeparateNotification ? R.drawable.ic_ring_volume_off + : R.drawable.ic_volume_ringer_mute; + volRow.setIcon(volRow.iconRes, mContext.getTheme()); + } } @Override @@ -339,6 +402,9 @@ public class VolumeDialogImpl implements VolumeDialog, mController.getState(); mConfigurationController.addCallback(this); + + mDeviceConfigProxy.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, + mExecutor, this::onDeviceConfigChange); } @Override @@ -346,6 +412,24 @@ public class VolumeDialogImpl implements VolumeDialog, mController.removeCallback(mControllerCallbackH); mHandler.removeCallbacksAndMessages(null); mConfigurationController.removeCallback(this); + mDeviceConfigProxy.removeOnPropertiesChangedListener(this::onDeviceConfigChange); + } + + /** + * Update ringer mode icon based on the config + */ + private void onDeviceConfigChange(DeviceConfig.Properties properties) { + Set changeSet = properties.getKeyset(); + if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) { + boolean newVal = properties.getBoolean( + SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false); + if (newVal != mSeparateNotification) { + mSeparateNotification = newVal; + updateRingerModeIconSet(); + updateRingRowIcon(); + + } + } } @Override @@ -552,6 +636,9 @@ public class VolumeDialogImpl implements VolumeDialog, mRingerDrawerNormalIcon = mDialog.findViewById(R.id.volume_drawer_normal_icon); mRingerDrawerNewSelectionBg = mDialog.findViewById(R.id.volume_drawer_selection_background); + mRingerDrawerMuteIcon.setImageResource(mVolumeRingerMuteIconDrawableId); + mRingerDrawerNormalIcon.setImageResource(mVolumeRingerIconDrawableId); + setupRingerDrawer(); mODICaptionsView = mDialog.findViewById(R.id.odi_captions); @@ -575,8 +662,14 @@ public class VolumeDialogImpl implements VolumeDialog, addRow(AudioManager.STREAM_MUSIC, R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true); if (!AudioSystem.isSingleVolume(mContext)) { - addRow(AudioManager.STREAM_RING, - R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, false); + if (mSeparateNotification) { + addRow(AudioManager.STREAM_RING, R.drawable.ic_ring_volume, + R.drawable.ic_ring_volume_off, true, false); + } else { + addRow(AudioManager.STREAM_RING, R.drawable.ic_volume_ringer, + R.drawable.ic_volume_ringer, true, false); + } + addRow(STREAM_ALARM, R.drawable.ic_alarm, R.drawable.ic_volume_alarm_mute, true, false); addRow(AudioManager.STREAM_VOICE_CALL, @@ -1532,8 +1625,8 @@ public class VolumeDialogImpl implements VolumeDialog, mRingerIcon.setTag(Events.ICON_STATE_VIBRATE); break; case AudioManager.RINGER_MODE_SILENT: - mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute); - mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute); + mRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId); + mSelectedRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId); mRingerIcon.setTag(Events.ICON_STATE_MUTE); addAccessibilityDescription(mRingerIcon, RINGER_MODE_SILENT, mContext.getString(R.string.volume_ringer_hint_unmute)); @@ -1542,14 +1635,14 @@ public class VolumeDialogImpl implements VolumeDialog, default: boolean muted = (mAutomute && ss.level == 0) || ss.muted; if (!isZenMuted && muted) { - mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute); - mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute); + mRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId); + mSelectedRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId); addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL, mContext.getString(R.string.volume_ringer_hint_unmute)); mRingerIcon.setTag(Events.ICON_STATE_MUTE); } else { - mRingerIcon.setImageResource(R.drawable.ic_volume_ringer); - mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer); + mRingerIcon.setImageResource(mVolumeRingerIconDrawableId); + mSelectedRingerIcon.setImageResource(mVolumeRingerIconDrawableId); if (mController.hasVibrator()) { addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL, mContext.getString(R.string.volume_ringer_hint_vibrate)); diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java index c5792b923e485..8f10fa619c140 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -20,6 +20,7 @@ import android.content.Context; import android.media.AudioManager; import com.android.internal.jank.InteractionJankMonitor; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.VolumeDialog; @@ -27,11 +28,14 @@ import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.volume.VolumeComponent; import com.android.systemui.volume.VolumeDialogComponent; import com.android.systemui.volume.VolumeDialogImpl; import com.android.systemui.volume.VolumePanelFactory; +import java.util.concurrent.Executor; + import dagger.Binds; import dagger.Module; import dagger.Provides; @@ -55,7 +59,9 @@ public interface VolumeModule { MediaOutputDialogFactory mediaOutputDialogFactory, VolumePanelFactory volumePanelFactory, ActivityStarter activityStarter, - InteractionJankMonitor interactionJankMonitor) { + InteractionJankMonitor interactionJankMonitor, + DeviceConfigProxy deviceConfigProxy, + @Main Executor executor) { VolumeDialogImpl impl = new VolumeDialogImpl( context, volumeDialogController, @@ -65,7 +71,9 @@ public interface VolumeModule { mediaOutputDialogFactory, volumePanelFactory, activityStarter, - interactionJankMonitor); + interactionJankMonitor, + deviceConfigProxy, + executor); impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false); impl.setAutomute(true); impl.setSilentMode(false); 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 2e74bf5474f98..a0b4eab1d5df7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -18,6 +18,7 @@ package com.android.systemui.volume; import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS; +import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -28,6 +29,7 @@ import static org.mockito.Mockito.verify; import android.app.KeyguardManager; import android.media.AudioManager; import android.os.SystemClock; +import android.provider.DeviceConfig; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.InputDevice; @@ -38,6 +40,7 @@ import android.view.accessibility.AccessibilityManager; import androidx.test.filters.SmallTest; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.jank.InteractionJankMonitor; import com.android.systemui.Prefs; import com.android.systemui.R; @@ -49,6 +52,9 @@ import com.android.systemui.plugins.VolumeDialogController.State; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.util.DeviceConfigProxyFake; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; @@ -71,6 +77,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { View mDrawerVibrate; View mDrawerMute; View mDrawerNormal; + private DeviceConfigProxyFake mDeviceConfigProxy; + private FakeExecutor mExecutor; @Mock VolumeDialogController mVolumeDialogController; @@ -97,6 +105,9 @@ public class VolumeDialogImplTest extends SysuiTestCase { getContext().addMockSystemService(KeyguardManager.class, mKeyguard); + mDeviceConfigProxy = new DeviceConfigProxyFake(); + mExecutor = new FakeExecutor(new FakeSystemClock()); + mDialog = new VolumeDialogImpl( getContext(), mVolumeDialogController, @@ -106,7 +117,9 @@ public class VolumeDialogImplTest extends SysuiTestCase { mMediaOutputDialogFactory, mVolumePanelFactory, mActivityStarter, - mInteractionJankMonitor); + mInteractionJankMonitor, + mDeviceConfigProxy, + mExecutor); mDialog.init(0, null); State state = createShellState(); mDialog.onStateChangedH(state); @@ -123,6 +136,9 @@ public class VolumeDialogImplTest extends SysuiTestCase { VolumePrefs.SHOW_RINGER_TOAST_COUNT + 1); Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false); + + mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false); } private State createShellState() { @@ -292,6 +308,35 @@ public class VolumeDialogImplTest extends SysuiTestCase { AudioManager.RINGER_MODE_NORMAL, false); } + /** + * Ideally we would look at the ringer ImageView and check its assigned drawable id, but that + * API does not exist. So we do the next best thing; we check the cached icon id. + */ + @Test + public void notificationVolumeSeparated_theRingerIconChanges() { + mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false); + + mExecutor.runAllReady(); // for the config change to take effect + + // assert icon is new based on res id + assertEquals(mDialog.mVolumeRingerIconDrawableId, + R.drawable.ic_speaker_on); + assertEquals(mDialog.mVolumeRingerMuteIconDrawableId, + R.drawable.ic_speaker_mute); + } + + @Test + public void notificationVolumeNotSeparated_theRingerIconRemainsTheSame() { + mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false); + + mExecutor.runAllReady(); + + assertEquals(mDialog.mVolumeRingerIconDrawableId, R.drawable.ic_volume_ringer); + assertEquals(mDialog.mVolumeRingerMuteIconDrawableId, R.drawable.ic_volume_ringer_mute); + } + /* @Test public void testContentDescriptions() {