[Audiosharing] Update the volume control registration flow.

Based on the framework API redesign, we will start/stop the volume control callback registration when user enter/leave the volume control page.
We will temporarily keep a map of the device to volume map and use the
volume value to set the initial state of the volume progress bar if it is valid.
If the volume value is null or invalid, we will read the music stream
volume from the audio manager to set the initial state.

Test: manual
Bug: 305620450
Change-Id: Iee3fba39af388782ac3498cb1610b248ced22c3c
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java
index 2f56f77..3396b8b 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java
@@ -37,22 +37,20 @@
 import com.android.settings.bluetooth.Utils;
 import com.android.settings.connecteddevice.DevicePreferenceCallback;
 import com.android.settings.dashboard.DashboardFragment;
-import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.VolumeControlProfile;
-import com.android.settingslib.utils.ThreadUtils;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 
 public class AudioSharingDeviceVolumeGroupController extends AudioSharingBasePreferenceController
         implements DevicePreferenceCallback {
-    private static final boolean DEBUG = BluetoothUtils.D;
-
     private static final String TAG = "AudioSharingDeviceVolumeGroupController";
     private static final String KEY = "audio_sharing_device_volume_group";
 
@@ -63,8 +61,43 @@
     private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
     private FragmentManager mFragmentManager;
     private PreferenceGroup mPreferenceGroup;
-    private Map<Preference, BluetoothVolumeControl.Callback> mCallbackMap =
-            new HashMap<Preference, BluetoothVolumeControl.Callback>();
+    private List<AudioSharingDeviceVolumePreference> mVolumePreferences = new ArrayList<>();
+    private Map<Integer, Integer> mValueMap = new HashMap<Integer, Integer>();
+
+    private BluetoothVolumeControl.Callback mVolumeControlCallback =
+            new BluetoothVolumeControl.Callback() {
+                @Override
+                public void onVolumeOffsetChanged(
+                        @NonNull BluetoothDevice device, int volumeOffset) {}
+
+                @Override
+                public void onDeviceVolumeChanged(
+                        @NonNull BluetoothDevice device,
+                        @IntRange(from = -255, to = 255) int volume) {
+                    CachedBluetoothDevice cachedDevice =
+                            mLocalBtManager.getCachedDeviceManager().findDevice(device);
+                    if (cachedDevice == null) return;
+                    mValueMap.put(cachedDevice.getGroupId(), volume);
+                    for (AudioSharingDeviceVolumePreference preference : mVolumePreferences) {
+                        if (preference.getCachedDevice() != null
+                                && preference.getCachedDevice().getGroupId()
+                                        == cachedDevice.getGroupId()) {
+                            // If the callback return invalid volume, try to
+                            // get the volume from AudioManager.STREAM_MUSIC
+                            int finalVolume = getAudioVolumeIfNeeded(volume);
+                            Log.d(
+                                    TAG,
+                                    "onDeviceVolumeChanged: set volume to "
+                                            + finalVolume
+                                            + " for "
+                                            + device.getAnonymizedAddress());
+                            mContext.getMainExecutor()
+                                    .execute(() -> preference.setProgress(finalVolume));
+                            break;
+                        }
+                    }
+                }
+            };
 
     private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
             new BluetoothLeBroadcastAssistant.Callback() {
@@ -176,6 +209,10 @@
         }
         mAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
         mBluetoothDeviceUpdater.registerCallback();
+        if (mVolumeControl != null) {
+            Log.d(TAG, "onStart() Registered volume control callback");
+            mVolumeControl.registerCallback(mExecutor, mVolumeControlCallback);
+        }
     }
 
     @Override
@@ -191,17 +228,16 @@
         }
         mAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
         mBluetoothDeviceUpdater.unregisterCallback();
+        if (mVolumeControl != null) {
+            Log.d(TAG, "onStop() Unregistered volume control callback");
+            mVolumeControl.unregisterCallback(mVolumeControlCallback);
+            mValueMap.clear();
+        }
     }
 
     @Override
     public void onDestroy(@NonNull LifecycleOwner owner) {
-        for (var entry : mCallbackMap.entrySet()) {
-            if (DEBUG) {
-                Log.d(TAG, "onDestroy: unregister callback for " + entry.getKey());
-            }
-            mVolumeControl.unregisterCallback(entry.getValue());
-        }
-        mCallbackMap.clear();
+        mVolumePreferences.clear();
     }
 
     @Override
@@ -228,14 +264,22 @@
             mPreferenceGroup.setVisible(true);
         }
         mPreferenceGroup.addPreference(preference);
-        if (mVolumeControl != null && preference instanceof AudioSharingDeviceVolumePreference) {
-            BluetoothVolumeControl.Callback callback =
-                    buildVcCallback((AudioSharingDeviceVolumePreference) preference);
-            mCallbackMap.put(preference, callback);
-            if (DEBUG) {
-                Log.d(TAG, "onDeviceAdded: register callback for " + preference);
-            }
-            mVolumeControl.registerCallback(mExecutor, callback);
+        if (preference instanceof AudioSharingDeviceVolumePreference) {
+            var volumePref = (AudioSharingDeviceVolumePreference) preference;
+            mVolumePreferences.add(volumePref);
+            if (volumePref.getProgress() > 0) return;
+            CachedBluetoothDevice device = volumePref.getCachedDevice();
+            if (device == null) return;
+            int volume = mValueMap.getOrDefault(device.getGroupId(), -1);
+            // If the volume is invalid, try to get the volume from AudioManager.STREAM_MUSIC
+            int finalVolume = getAudioVolumeIfNeeded(volume);
+            Log.d(
+                    TAG,
+                    "onDeviceAdded: set volume to "
+                            + finalVolume
+                            + " for "
+                            + device.getDevice().getAnonymizedAddress());
+            mContext.getMainExecutor().execute(() -> volumePref.setProgress(finalVolume));
         }
     }
 
@@ -245,12 +289,18 @@
         if (mPreferenceGroup.getPreferenceCount() == 0) {
             mPreferenceGroup.setVisible(false);
         }
-        if (mVolumeControl != null && mCallbackMap.containsKey(preference)) {
-            if (DEBUG) {
-                Log.d(TAG, "onDeviceRemoved: unregister callback for " + preference);
+        if (preference instanceof AudioSharingDeviceVolumePreference) {
+            var volumePref = (AudioSharingDeviceVolumePreference) preference;
+            if (mVolumePreferences.contains(volumePref)) {
+                mVolumePreferences.remove(volumePref);
             }
-            mVolumeControl.unregisterCallback(mCallbackMap.get(preference));
-            mCallbackMap.remove(preference);
+            CachedBluetoothDevice device = volumePref.getCachedDevice();
+            Log.d(
+                    TAG,
+                    "onDeviceRemoved: "
+                            + (device == null
+                                    ? "null"
+                                    : device.getDevice().getAnonymizedAddress()));
         }
     }
 
@@ -278,39 +328,6 @@
                         fragment.getMetricsCategory());
     }
 
-    private BluetoothVolumeControl.Callback buildVcCallback(
-            AudioSharingDeviceVolumePreference preference) {
-        return new BluetoothVolumeControl.Callback() {
-            @Override
-            public void onVolumeOffsetChanged(BluetoothDevice device, int volumeOffset) {}
-
-            @Override
-            public void onDeviceVolumeChanged(
-                    @NonNull BluetoothDevice device,
-                    @IntRange(from = -255, to = 255) int volume) {
-                CachedBluetoothDevice cachedDevice =
-                        mLocalBtManager.getCachedDeviceManager().findDevice(device);
-                if (cachedDevice == null) return;
-                if (preference.getCachedDevice() != null
-                        && preference.getCachedDevice().getGroupId() == cachedDevice.getGroupId()) {
-                    // If the callback return invalid volume, try to get the volume from
-                    // AudioManager.STREAM_MUSIC
-                    int finalVolume = getAudioVolumeIfNeeded(volume);
-                    Log.d(
-                            TAG,
-                            "onDeviceVolumeChanged: set volume to "
-                                    + finalVolume
-                                    + " for "
-                                    + device.getAnonymizedAddress());
-                    ThreadUtils.postOnMainThread(
-                            () -> {
-                                preference.setProgress(finalVolume);
-                            });
-                }
-            }
-        };
-    }
-
     private int getAudioVolumeIfNeeded(int volume) {
         if (volume >= 0) return volume;
         try {