[Ambient Volume] Show value with local data

Flag: com.android.settingslib.flags.hearing_devices_ambient_volume_control
Bug: 357878944
Test: atest BluetoothDetailsAmbientVolumePreferenceControllerTest
Change-Id: I3dad0f5424b44fee6d049fd778c4f8f71db0b58e
This commit is contained in:
Angela Wang
2024-10-31 02:45:07 +00:00
parent 0595aed386
commit c2ca7dadd9
3 changed files with 277 additions and 5 deletions

View File

@@ -16,11 +16,16 @@
package com.android.settings.bluetooth;
import static android.bluetooth.BluetoothDevice.BOND_BONDED;
import static com.android.settings.bluetooth.AmbientVolumePreference.SIDE_UNIFIED;
import static com.android.settings.bluetooth.AmbientVolumePreference.VALID_SIDES;
import static com.android.settings.bluetooth.BluetoothDetailsHearingDeviceController.KEY_HEARING_DEVICE_GROUP;
import static com.android.settings.bluetooth.BluetoothDetailsHearingDeviceController.ORDER_AMBIENT_VOLUME;
import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_INVALID;
import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_LEFT;
import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_RIGHT;
import static com.android.settingslib.bluetooth.HearingDeviceLocalDataManager.Data.INVALID_VOLUME;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
@@ -29,15 +34,21 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HearingDeviceLocalDataManager;
import com.android.settingslib.bluetooth.HearingDeviceLocalDataManager.Data;
import com.android.settingslib.bluetooth.VolumeControlProfile;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.utils.ThreadUtils;
import com.google.common.collect.BiMap;
@@ -47,7 +58,8 @@ import java.util.Set;
/** A {@link BluetoothDetailsController} that manages ambient volume control preferences. */
public class BluetoothDetailsAmbientVolumePreferenceController extends
BluetoothDetailsController implements Preference.OnPreferenceChangeListener {
BluetoothDetailsController implements Preference.OnPreferenceChangeListener,
HearingDeviceLocalDataManager.OnDeviceLocalDataChangeListener, OnStart, OnStop {
private static final boolean DEBUG = true;
private static final String TAG = "AmbientPrefController";
@@ -60,6 +72,7 @@ public class BluetoothDetailsAmbientVolumePreferenceController extends
private final Set<CachedBluetoothDevice> mCachedDevices = new ArraySet<>();
private final BiMap<Integer, BluetoothDevice> mSideToDeviceMap = HashBiMap.create();
private final BiMap<Integer, SeekBarPreference> mSideToSliderMap = HashBiMap.create();
private final HearingDeviceLocalDataManager mLocalDataManager;
@Nullable
private PreferenceCategory mDeviceControls;
@@ -71,6 +84,19 @@ public class BluetoothDetailsAmbientVolumePreferenceController extends
@NonNull CachedBluetoothDevice device,
@NonNull Lifecycle lifecycle) {
super(context, fragment, device, lifecycle);
mLocalDataManager = new HearingDeviceLocalDataManager(context);
mLocalDataManager.setOnDeviceLocalDataChangeListener(this,
ThreadUtils.getBackgroundExecutor());
}
@VisibleForTesting
BluetoothDetailsAmbientVolumePreferenceController(@NonNull Context context,
@NonNull PreferenceFragmentCompat fragment,
@NonNull CachedBluetoothDevice device,
@NonNull Lifecycle lifecycle,
@NonNull HearingDeviceLocalDataManager localSettings) {
super(context, fragment, device, lifecycle);
mLocalDataManager = localSettings;
}
@Override
@@ -82,13 +108,33 @@ public class BluetoothDetailsAmbientVolumePreferenceController extends
loadDevices();
}
@Override
public void onStart() {
ThreadUtils.postOnBackgroundThread(() -> {
mLocalDataManager.start();
mCachedDevices.forEach(device -> {
device.registerCallback(ThreadUtils.getBackgroundExecutor(), this);
});
});
}
@Override
public void onStop() {
ThreadUtils.postOnBackgroundThread(() -> {
mLocalDataManager.stop();
mCachedDevices.forEach(device -> {
device.unregisterCallback(this);
});
});
}
@Override
protected void refresh() {
if (!isAvailable()) {
return;
}
// TODO: load data from remote
refreshControlUi();
loadLocalDataToUi();
}
@Override
@@ -111,6 +157,8 @@ public class BluetoothDetailsAmbientVolumePreferenceController extends
if (DEBUG) {
Log.d(TAG, "onPreferenceChange: side=" + side + ", value=" + value);
}
setVolumeIfValid(side, value);
if (side == SIDE_UNIFIED) {
// TODO: set the value on the devices
} else {
@@ -139,15 +187,31 @@ public class BluetoothDetailsAmbientVolumePreferenceController extends
});
}
@Override
public void onDeviceLocalDataChange(@NonNull String address, @Nullable Data data) {
if (data == null) {
// The local data is removed because the device is unpaired, do nothing
return;
}
for (BluetoothDevice device : mSideToDeviceMap.values()) {
if (device.getAnonymizedAddress().equals(address)) {
mContext.getMainExecutor().execute(() -> loadLocalDataToUi(device));
return;
}
}
}
private void loadDevices() {
mSideToDeviceMap.clear();
mCachedDevices.clear();
if (VALID_SIDES.contains(mCachedDevice.getDeviceSide())) {
if (VALID_SIDES.contains(mCachedDevice.getDeviceSide())
&& mCachedDevice.getBondState() == BOND_BONDED) {
mSideToDeviceMap.put(mCachedDevice.getDeviceSide(), mCachedDevice.getDevice());
mCachedDevices.add(mCachedDevice);
}
for (CachedBluetoothDevice memberDevice : mCachedDevice.getMemberDevice()) {
if (VALID_SIDES.contains(memberDevice.getDeviceSide())) {
if (VALID_SIDES.contains(memberDevice.getDeviceSide())
&& memberDevice.getBondState() == BOND_BONDED) {
mSideToDeviceMap.put(memberDevice.getDeviceSide(), memberDevice.getDevice());
mCachedDevices.add(memberDevice);
}
@@ -164,9 +228,16 @@ public class BluetoothDetailsAmbientVolumePreferenceController extends
if (mPreference != null || mDeviceControls == null) {
return;
}
mPreference = new AmbientVolumePreference(mDeviceControls.getContext());
mPreference.setKey(KEY_AMBIENT_VOLUME);
mPreference.setOrder(ORDER_AMBIENT_VOLUME);
mPreference.setOnIconClickListener(() -> {
mSideToDeviceMap.forEach((s, d) -> {
// Update new value to local data
mLocalDataManager.updateAmbientControlExpanded(d, isControlExpanded());
});
});
if (mDeviceControls.findPreference(mPreference.getKey()) == null) {
mDeviceControls.addPreference(mPreference);
}
@@ -186,6 +257,12 @@ public class BluetoothDetailsAmbientVolumePreferenceController extends
preference.setKey(KEY_AMBIENT_VOLUME_SLIDER + "_" + side);
preference.setOrder(order);
preference.setOnPreferenceChangeListener(this);
if (side == SIDE_LEFT) {
preference.setTitle(mContext.getString(R.string.bluetooth_ambient_volume_control_left));
} else if (side == SIDE_RIGHT) {
preference.setTitle(
mContext.getString(R.string.bluetooth_ambient_volume_control_right));
}
mSideToSliderMap.put(side, preference);
}
@@ -195,4 +272,50 @@ public class BluetoothDetailsAmbientVolumePreferenceController extends
mPreference.updateLayout();
}
}
/** Sets the volume to the corresponding control slider. */
private void setVolumeIfValid(int side, int volume) {
if (volume == INVALID_VOLUME) {
return;
}
if (mPreference != null) {
mPreference.setSliderValue(side, volume);
}
// Update new value to local data
if (side == SIDE_UNIFIED) {
mSideToDeviceMap.forEach((s, d) -> mLocalDataManager.updateGroupAmbient(d, volume));
} else {
mLocalDataManager.updateAmbient(mSideToDeviceMap.get(side), volume);
}
}
private void loadLocalDataToUi() {
mSideToDeviceMap.forEach((s, d) -> loadLocalDataToUi(d));
}
private void loadLocalDataToUi(BluetoothDevice device) {
final Data data = mLocalDataManager.get(device);
if (DEBUG) {
Log.d(TAG, "loadLocalDataToUi, data=" + data + ", device=" + device);
}
final int side = mSideToDeviceMap.inverse().getOrDefault(device, SIDE_INVALID);
setVolumeIfValid(side, data.ambient());
setVolumeIfValid(SIDE_UNIFIED, data.groupAmbient());
setControlExpanded(data.ambientControlExpanded());
refreshControlUi();
}
private boolean isControlExpanded() {
return mPreference != null && mPreference.isExpanded();
}
private void setControlExpanded(boolean expanded) {
if (mPreference != null && mPreference.isExpanded() != expanded) {
mPreference.setExpanded(expanded);
}
mSideToDeviceMap.forEach((s, d) -> {
// Update new value to local data
mLocalDataManager.updateAmbientControlExpanded(d, expanded);
});
}
}