Changed VolumeSettingsPreference to be updated when volume is updated
via the volume knob or the drop-down notification.

Bug: 147844631
Test: Robolectric
Change-Id: Ie0d486e7ac31ab801c435944fe6fce7ca01d4335
diff --git a/src/com/android/car/settings/sound/VolumeSettingsPreferenceController.java b/src/com/android/car/settings/sound/VolumeSettingsPreferenceController.java
index fae748e..eaa9e0e 100644
--- a/src/com/android/car/settings/sound/VolumeSettingsPreferenceController.java
+++ b/src/com/android/car/settings/sound/VolumeSettingsPreferenceController.java
@@ -61,6 +61,35 @@
     private final List<SeekBarPreference> mVolumePreferences = new ArrayList<>();
     private final VolumeSettingsRingtoneManager mRingtoneManager;
 
+    @VisibleForTesting
+    final CarAudioManager.CarVolumeCallback mVolumeChangeCallback =
+            new CarAudioManager.CarVolumeCallback() {
+                @Override
+                public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {
+                    if (mCarAudioManager != null) {
+                        int value = mCarAudioManager.getGroupVolume(groupId);
+
+                        for (SeekBarPreference volumePreference : mVolumePreferences) {
+                            Bundle extras = volumePreference.getExtras();
+                            if (extras.getInt(VOLUME_GROUP_KEY) == groupId) {
+                                // Only setValue if the value is different, since changing the
+                                // seekbar of the volume directly will trigger CarVolumeCallback as
+                                // well, causing janky movement.
+                                if (volumePreference.getValue() != value) {
+                                    volumePreference.setValue(value);
+                                }
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                @Override
+                public void onMasterMuteChanged(int zoneId, int flags) {
+                    // Mute is not being used yet
+                }
+            };
+
     private final ServiceConnection mServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -77,6 +106,7 @@
                             volumeItem.getTitle());
                     mVolumePreferences.add(volumePreference);
                 }
+                mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
 
                 refreshUi();
             } catch (CarNotConnectedException e) {
@@ -177,6 +207,7 @@
 
     private void cleanupAudioManager() {
         cleanUpVolumePreferences();
+        mCarAudioManager.unregisterCarVolumeCallback(mVolumeChangeCallback);
         mCarAudioManager = null;
     }
 
diff --git a/tests/robotests/src/com/android/car/settings/sound/VolumeSettingsPreferenceControllerTest.java b/tests/robotests/src/com/android/car/settings/sound/VolumeSettingsPreferenceControllerTest.java
index db54af2..5813c0d 100644
--- a/tests/robotests/src/com/android/car/settings/sound/VolumeSettingsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/car/settings/sound/VolumeSettingsPreferenceControllerTest.java
@@ -18,6 +18,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -53,6 +56,7 @@
 @Config(shadows = {ShadowRingtoneManager.class})
 public class VolumeSettingsPreferenceControllerTest {
 
+    private static final int ZONE_ID = 1;
     private static final int GROUP_ID = 0;
     private static final int TEST_MIN_VOLUME = 0;
     private static final int TEST_VOLUME = 40;
@@ -122,6 +126,14 @@
     }
 
     @Test
+    public void onServiceConnected_registersVolumeCallback() {
+        mPreferenceControllerHelper.markState(Lifecycle.State.CREATED);
+        mController.refreshUi();
+
+        verify(mCarAudioManager).registerCarVolumeCallback(mController.mVolumeChangeCallback);
+    }
+
+    @Test
     public void testRefreshUi_serviceStarted_multipleCalls() {
         mPreferenceControllerHelper.markState(Lifecycle.State.CREATED);
 
@@ -158,4 +170,25 @@
         preference.getOnPreferenceChangeListener().onPreferenceChange(preference, TEST_NEW_VOLUME);
         verify(mCarAudioManager).setGroupVolume(GROUP_ID, TEST_NEW_VOLUME, 0);
     }
+
+    @Test
+    public void onGroupVolumeChanged_sameValue_doesNotUpdateVolumeSeekbar() {
+        mPreferenceControllerHelper.markState(Lifecycle.State.CREATED);
+        mController.refreshUi();
+        SeekBarPreference preference = spy((SeekBarPreference) mPreferenceGroup.getPreference(0));
+        mController.mVolumeChangeCallback.onGroupVolumeChanged(ZONE_ID, GROUP_ID, /* flags= */ 0);
+
+        verify(preference, never()).setValue(any(Integer.class));
+    }
+
+    @Test
+    public void onGroupVolumeChanged_differentValue_updatesVolumeSeekbar() {
+        mPreferenceControllerHelper.markState(Lifecycle.State.CREATED);
+        mController.refreshUi();
+        when(mCarAudioManager.getGroupVolume(GROUP_ID)).thenReturn(TEST_NEW_VOLUME);
+        mController.mVolumeChangeCallback.onGroupVolumeChanged(ZONE_ID, GROUP_ID, /* flags= */ 0);
+
+        SeekBarPreference preference = (SeekBarPreference) mPreferenceGroup.getPreference(0);
+        assertThat(preference.getValue()).isEqualTo(TEST_NEW_VOLUME);
+    }
 }