From afbd9d9894967453841fe323e456b9ffa8c46b85 Mon Sep 17 00:00:00 2001 From: Yiyi Shen Date: Mon, 4 Dec 2023 16:05:28 +0800 Subject: [PATCH] [Audiosharing] Fix volume control. 1. Unregister the volume control callbacks in onDestroy 2. Reuse SeekBarPreference#setProgress 3. Fetch volume from AudioManager if got invalid volume from VC service. Flagged with enable_le_audio_sharing Bug: 305620450 Test: manual Change-Id: Id63d1dbff4c3cbdeb91c2537d951c39e1a932220 --- ...udioSharingDeviceVolumeControlUpdater.java | 4 +- ...dioSharingDeviceVolumeGroupController.java | 52 +++++++++++++++++-- .../AudioSharingDeviceVolumePreference.java | 33 +++--------- 3 files changed, 60 insertions(+), 29 deletions(-) diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdater.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdater.java index 0668f50db74..e60eabd015a 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdater.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdater.java @@ -102,7 +102,9 @@ public class AudioSharingDeviceVolumeControlUpdater extends BluetoothDeviceUpdat } }; AudioSharingDeviceVolumePreference vPreference = - new AudioSharingDeviceVolumePreference(mPrefContext, cachedDevice, listener); + new AudioSharingDeviceVolumePreference(mPrefContext, cachedDevice); + vPreference.initialize(); + vPreference.setOnSeekBarChangeListener(listener); vPreference.setKey(getPreferenceKey()); vPreference.setIcon(com.android.settingslib.R.drawable.ic_bt_untethered_earbuds); vPreference.setTitle(cachedDevice.getName()); diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java index bdaa534e1d1..f075048384c 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java @@ -23,6 +23,7 @@ import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.bluetooth.BluetoothVolumeControl; import android.content.Context; +import android.media.AudioManager; import android.util.Log; import androidx.annotation.NonNull; @@ -36,10 +37,12 @@ import com.android.settings.bluetooth.BluetoothDeviceUpdater; 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.HashMap; import java.util.Map; @@ -48,6 +51,7 @@ 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"; @@ -189,6 +193,17 @@ public class AudioSharingDeviceVolumeGroupController extends AudioSharingBasePre mBluetoothDeviceUpdater.unregisterCallback(); } + @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(); + } + @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); @@ -217,6 +232,9 @@ public class AudioSharingDeviceVolumeGroupController extends AudioSharingBasePre BluetoothVolumeControl.Callback callback = buildVcCallback((AudioSharingDeviceVolumePreference) preference); mCallbackMap.put(preference, callback); + if (DEBUG) { + Log.d(TAG, "onDeviceAdded: register callback for " + preference); + } mVolumeControl.registerCallback(mExecutor, callback); } } @@ -228,6 +246,9 @@ public class AudioSharingDeviceVolumeGroupController extends AudioSharingBasePre mPreferenceGroup.setVisible(false); } if (mVolumeControl != null && mCallbackMap.containsKey(preference)) { + if (DEBUG) { + Log.d(TAG, "onDeviceRemoved: unregister callback for " + preference); + } mVolumeControl.unregisterCallback(mCallbackMap.get(preference)); mCallbackMap.remove(preference); } @@ -266,16 +287,41 @@ public class AudioSharingDeviceVolumeGroupController extends AudioSharingBasePre @Override public void onDeviceVolumeChanged( @android.annotation.NonNull BluetoothDevice device, - @IntRange(from = 0, to = 255) int volume) { - Log.d(TAG, "onDeviceVolumeChanged changed " + device.getAnonymizedAddress()); + @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()) { - preference.setProgress(volume); + // 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 { + AudioManager audioManager = mContext.getSystemService(AudioManager.class); + int max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int min = audioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC); + return Math.round( + audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) * 255f / (max - min)); + } catch (RuntimeException e) { + Log.e(TAG, "Fail to fetch current music stream volume, error = " + e); + return volume; + } + } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumePreference.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumePreference.java index 3808b600af8..9dd9fb0568c 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumePreference.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumePreference.java @@ -21,33 +21,23 @@ import android.widget.SeekBar; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.preference.PreferenceViewHolder; import com.android.settings.R; -import com.android.settings.bluetooth.Utils; import com.android.settings.widget.SeekBarPreference; import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.bluetooth.LocalBluetoothManager; public class AudioSharingDeviceVolumePreference extends SeekBarPreference { - private static final String TAG = "AudioSharingDeviceVolumePreference"; public static final int MIN_VOLUME = 0; public static final int MAX_VOLUME = 255; protected SeekBar mSeekBar; - private final LocalBluetoothManager mLocalBtManager; private final CachedBluetoothDevice mCachedDevice; - private final SeekBar.OnSeekBarChangeListener mListener; public AudioSharingDeviceVolumePreference( - Context context, - @NonNull CachedBluetoothDevice device, - SeekBar.OnSeekBarChangeListener listener) { + Context context, @NonNull CachedBluetoothDevice device) { super(context); setLayoutResource(R.layout.preference_volume_slider); - mLocalBtManager = Utils.getLocalBtManager(context); mCachedDevice = device; - mListener = listener; } @Nullable @@ -55,19 +45,12 @@ public class AudioSharingDeviceVolumePreference extends SeekBarPreference { return mCachedDevice; } - @Override - public void onBindViewHolder(PreferenceViewHolder view) { - super.onBindViewHolder(view); - mSeekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar); - mSeekBar.setMax(MAX_VOLUME); - mSeekBar.setMin(MIN_VOLUME); - mSeekBar.setOnSeekBarChangeListener(mListener); - } - - /** Set the progress bar to target progress */ - public void setProgress(int progress) { - if (mSeekBar != null) { - mSeekBar.setProgress(progress); - } + /** + * Initialize {@link AudioSharingDeviceVolumePreference}. + * Need to be called after creating the preference. + */ + public void initialize() { + setMax(MAX_VOLUME); + setMin(MIN_VOLUME); } }