- dedup volume stream - run UI update from UI thread
am: ebcc0e52fd

Change-Id: Ibb8fe4b45c067015b6081e357a1a4a217acb2f65
diff --git a/src/com/android/car/settings/sound/SoundSettingsFragment.java b/src/com/android/car/settings/sound/SoundSettingsFragment.java
index c729381..9390a33 100644
--- a/src/com/android/car/settings/sound/SoundSettingsFragment.java
+++ b/src/com/android/car/settings/sound/SoundSettingsFragment.java
@@ -15,16 +15,20 @@
  */
 package com.android.car.settings.sound;
 
+import android.app.Activity;
 import android.car.Car;
 import android.car.CarNotConnectedException;
 import android.car.media.CarAudioManager;
+import android.annotation.DrawableRes;
+import android.annotation.StringRes;
 import android.content.ComponentName;
 import android.content.ServiceConnection;
 import android.media.AudioAttributes;
-import android.media.AudioManager;
 import android.media.IVolumeController;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -34,6 +38,7 @@
 import com.android.car.view.PagedListView;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 
 /**
  * Activity hosts sound related settings.
@@ -46,42 +51,50 @@
     private TypedPagedListAdapter mPagedListAdapter;
 
     private final ArrayList<VolumeLineItem> mVolumeLineItems = new ArrayList<>();
-    private final SoundSettingsFragment.VolumnCallback
-            mVolumeCallback = new SoundSettingsFragment.VolumnCallback();
+    private final VolumeCallback
+            mVolumeCallback = new VolumeCallback();
+
+    private final Handler handler = new Handler(Looper.getMainLooper());
+    private final HashSet<StreamItem> mUniqueStreamItems = new HashSet<>();
 
     private final ServiceConnection mServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            AudioAttributes naviAudioAttributes;
-            AudioAttributes systemAudioAttributes;
-            AudioAttributes mediaAudioAttributes;
             try {
                 mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
                 mCarAudioManager.setVolumeController(mVolumeCallback);
 
-                systemAudioAttributes = mCarAudioManager.getAudioAttributesForCarUsage(
-                        mCarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SOUND);
-                mediaAudioAttributes = mCarAudioManager.getAudioAttributesForCarUsage(
-                        mCarAudioManager.CAR_AUDIO_USAGE_MUSIC);
+                mUniqueStreamItems.add(new StreamItem(
+                        mCarAudioManager.getAudioAttributesForCarUsage(
+                                mCarAudioManager.CAR_AUDIO_USAGE_MUSIC)
+                                .getVolumeControlStream(),
+                        R.string.media_volume_title,
+                        com.android.internal.R.drawable.ic_audio_media));
+                mUniqueStreamItems.add(new StreamItem(
+                        mCarAudioManager.getAudioAttributesForCarUsage(
+                                mCarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SOUND)
+                                .getVolumeControlStream(),
+                        R.string.ring_volume_title,
+                        com.android.internal.R.drawable.ic_audio_ring_notif));
+                mUniqueStreamItems.add(new StreamItem(
+                        mCarAudioManager.getAudioAttributesForCarUsage(
+                                mCarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE)
+                                .getVolumeControlStream(),
+                        R.string.navi_volume_title,
+                        R.drawable.ic_audio_navi));
             } catch (CarNotConnectedException e) {
                 Log.e(TAG, "Car is not connected!", e);
                 return;
             }
 
-            // It turns out that the stream id for system and navigation are the same.
-            // skip navi for now
-            mVolumeLineItems.add(new VolumeLineItem(
-                    getContext(),
-                    mCarAudioManager,
-                    mediaAudioAttributes.getVolumeControlStream(),
-                    R.string.media_volume_title,
-                    com.android.internal.R.drawable.ic_audio_media));
-            mVolumeLineItems.add(new VolumeLineItem(
-                    getContext(),
-                    mCarAudioManager,
-                    systemAudioAttributes.getVolumeControlStream(),
-                    R.string.ring_volume_title,
-                    com.android.internal.R.drawable.ic_audio_ring_notif));
+            for (StreamItem streamItem : mUniqueStreamItems) {
+                mVolumeLineItems.add(new VolumeLineItem(
+                        getContext(),
+                        mCarAudioManager,
+                        streamItem.volumeStream,
+                        streamItem.nameStringId,
+                        streamItem.iconId));
+            }
             // if list is already initiated, update it's content.
             if (mPagedListAdapter != null) {
                 mPagedListAdapter.updateList(new ArrayList<>(mVolumeLineItems));
@@ -90,6 +103,12 @@
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
+            try {
+                mCarAudioManager.setVolumeController(null);
+            } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Car is not connected!", e);
+            return;
+            }
             mCarAudioManager = null;
         }
     };
@@ -141,20 +160,26 @@
     /**
      * The interface has a terrible name, it is actually a callback, so here name it accordingly.
      */
-    private final class VolumnCallback extends IVolumeController.Stub {
+    private final class VolumeCallback extends IVolumeController.Stub {
         @Override
         public void displaySafeVolumeWarning(int flags) throws RemoteException {
         }
 
         @Override
         public void volumeChanged(int streamType, int flags) throws RemoteException {
-            for (VolumeLineItem item : mVolumeLineItems) {
-                if (streamType == item.getStreamType()) {
-                    break;
-                }
+            Activity activity = getActivity();
+            if (activity == null) {
+                Log.w(TAG, "no activity attached.");
                 return;
             }
-            mPagedListAdapter.notifyDataSetChanged();
+
+            for (VolumeLineItem item : mVolumeLineItems) {
+                if (streamType == item.getStreamType()) {
+                    handler.post(() -> mPagedListAdapter.notifyDataSetChanged());
+                    return;
+                }
+            }
+
         }
 
         // this is not mute of this stream
@@ -174,4 +199,42 @@
         public void setA11yMode(int mode) {
         }
     }
+
+    /**
+     * Wraps information needed to render an audio stream, keyed by volumeControlStream.
+     */
+    private static final class StreamItem {
+        final int volumeStream;
+
+        @StringRes
+        final int nameStringId;
+
+        @DrawableRes
+        final int iconId;
+
+        StreamItem(
+                int volumeStream,
+                @StringRes int nameStringId,
+                @DrawableRes int iconId) {
+            this.volumeStream = volumeStream;
+            this.nameStringId = nameStringId;
+            this.iconId = iconId;
+        }
+
+        @Override
+        public int hashCode() {
+            return volumeStream;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof  StreamItem)) {
+                return false;
+            }
+            return volumeStream == ((StreamItem) o).volumeStream;
+        }
+    }
 }