[Ambient Volume] Show value with remote data

Sync local data with remote data when UI need to refresh and set the
corresponding local value to remote when the control expanded/collapsed.

Flag: com.android.settingslib.flags.hearing_devices_ambient_volume_control
Bug: 357878944
Test: atest BluetoothDetailsAmbientVolumePreferenceControllerTest
Change-Id: If748e696eb62b199d4fd9abafa2300d301a8079c
This commit is contained in:
Angela Wang
2024-11-11 07:23:46 +00:00
parent c2ca7dadd9
commit 46537a6576
4 changed files with 315 additions and 36 deletions

View File

@@ -29,14 +29,19 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.ContentResolver;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
@@ -44,8 +49,13 @@ import androidx.preference.PreferenceCategory;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.bluetooth.AmbientVolumeController;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HearingDeviceLocalDataManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.bluetooth.VolumeControlProfile;
import org.junit.Before;
import org.junit.Rule;
@@ -90,6 +100,18 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
private BluetoothDevice mMemberDevice;
@Mock
private HearingDeviceLocalDataManager mLocalDataManager;
@Mock
private LocalBluetoothManager mBluetoothManager;
@Mock
private BluetoothEventManager mEventManager;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private VolumeControlProfile mVolumeControlProfile;
@Mock
private AmbientVolumeController mVolumeController;
@Mock
private Handler mTestHandler;
private BluetoothDetailsAmbientVolumePreferenceController mController;
@@ -97,11 +119,29 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
public void setUp() {
super.setUp();
mContext = spy(mContext);
PreferenceCategory deviceControls = new PreferenceCategory(mContext);
deviceControls.setKey(KEY_HEARING_DEVICE_GROUP);
mScreen.addPreference(deviceControls);
mController = new BluetoothDetailsAmbientVolumePreferenceController(mContext, mFragment,
mCachedDevice, mLifecycle, mLocalDataManager);
mController = spy(
new BluetoothDetailsAmbientVolumePreferenceController(mContext, mBluetoothManager,
mFragment, mCachedDevice, mLifecycle, mLocalDataManager,
mVolumeController));
when(mBluetoothManager.getEventManager()).thenReturn(mEventManager);
when(mBluetoothManager.getProfileManager()).thenReturn(mProfileManager);
when(mProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControlProfile);
when(mVolumeControlProfile.getConnectionStatus(mDevice)).thenReturn(
BluetoothProfile.STATE_CONNECTED);
when(mVolumeControlProfile.getConnectionStatus(mMemberDevice)).thenReturn(
BluetoothProfile.STATE_CONNECTED);
when(mContext.getMainThreadHandler()).thenReturn(mTestHandler);
when(mTestHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(
invocationOnMock -> {
invocationOnMock.getArgument(0, Runnable.class).run();
return null;
});
}
@Test
@@ -128,10 +168,13 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
@Test
public void onDeviceLocalDataChange_noMemberAndExpanded_uiCorrectAndDataUpdated() {
prepareDevice(/* hasMember= */ false, /* controlExpanded= */ true);
prepareDevice(/* hasMember= */ false);
mController.init(mScreen);
mController.onDeviceLocalDataChange(TEST_ADDRESS, prepareEmptyData());
HearingDeviceLocalDataManager.Data data = new HearingDeviceLocalDataManager.Data.Builder()
.ambient(0).groupAmbient(0).ambientControlExpanded(true).build();
when(mLocalDataManager.get(mDevice)).thenReturn(data);
mController.onDeviceLocalDataChange(TEST_ADDRESS, data);
shadowOf(Looper.getMainLooper()).idle();
AmbientVolumePreference preference = mScreen.findPreference(KEY_AMBIENT_VOLUME);
@@ -142,10 +185,13 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
@Test
public void onDeviceLocalDataChange_noMemberAndCollapsed_uiCorrectAndDataUpdated() {
prepareDevice(/* hasMember= */ false, /* controlExpanded= */ false);
prepareDevice(/* hasMember= */ false);
mController.init(mScreen);
mController.onDeviceLocalDataChange(TEST_ADDRESS, prepareEmptyData());
HearingDeviceLocalDataManager.Data data = new HearingDeviceLocalDataManager.Data.Builder()
.ambient(0).groupAmbient(0).ambientControlExpanded(false).build();
when(mLocalDataManager.get(mDevice)).thenReturn(data);
mController.onDeviceLocalDataChange(TEST_ADDRESS, data);
shadowOf(Looper.getMainLooper()).idle();
AmbientVolumePreference preference = mScreen.findPreference(KEY_AMBIENT_VOLUME);
@@ -156,10 +202,13 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
@Test
public void onDeviceLocalDataChange_hasMemberAndExpanded_uiCorrectAndDataUpdated() {
prepareDevice(/* hasMember= */ true, /* controlExpanded= */ true);
prepareDevice(/* hasMember= */ true);
mController.init(mScreen);
mController.onDeviceLocalDataChange(TEST_ADDRESS, prepareEmptyData());
HearingDeviceLocalDataManager.Data data = new HearingDeviceLocalDataManager.Data.Builder()
.ambient(0).groupAmbient(0).ambientControlExpanded(true).build();
when(mLocalDataManager.get(mDevice)).thenReturn(data);
mController.onDeviceLocalDataChange(TEST_ADDRESS, data);
shadowOf(Looper.getMainLooper()).idle();
AmbientVolumePreference preference = mScreen.findPreference(KEY_AMBIENT_VOLUME);
@@ -170,10 +219,13 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
@Test
public void onDeviceLocalDataChange_hasMemberAndCollapsed_uiCorrectAndDataUpdated() {
prepareDevice(/* hasMember= */ true, /* controlExpanded= */ false);
prepareDevice(/* hasMember= */ true);
mController.init(mScreen);
mController.onDeviceLocalDataChange(TEST_ADDRESS, prepareEmptyData());
HearingDeviceLocalDataManager.Data data = new HearingDeviceLocalDataManager.Data.Builder()
.ambient(0).groupAmbient(0).ambientControlExpanded(false).build();
when(mLocalDataManager.get(mDevice)).thenReturn(data);
mController.onDeviceLocalDataChange(TEST_ADDRESS, data);
shadowOf(Looper.getMainLooper()).idle();
AmbientVolumePreference preference = mScreen.findPreference(KEY_AMBIENT_VOLUME);
@@ -185,11 +237,13 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
@Test
public void onStart_localDataManagerStartAndCallbackRegistered() {
prepareDevice(/* hasMember= */ true);
mController.init(mScreen);
mController.onStart();
verify(mLocalDataManager, atLeastOnce()).start();
verify(mVolumeController).registerCallback(any(Executor.class), eq(mDevice));
verify(mVolumeController).registerCallback(any(Executor.class), eq(mMemberDevice));
verify(mCachedDevice).registerCallback(any(Executor.class),
any(CachedBluetoothDevice.Callback.class));
verify(mCachedMemberDevice).registerCallback(any(Executor.class),
@@ -199,11 +253,13 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
@Test
public void onStop_localDataManagerStopAndCallbackUnregistered() {
prepareDevice(/* hasMember= */ true);
mController.init(mScreen);
mController.onStop();
verify(mLocalDataManager).stop();
verify(mVolumeController).unregisterCallback(mDevice);
verify(mVolumeController).unregisterCallback(mMemberDevice);
verify(mCachedDevice).unregisterCallback(any(CachedBluetoothDevice.Callback.class));
verify(mCachedMemberDevice).unregisterCallback(any(CachedBluetoothDevice.Callback.class));
}
@@ -211,7 +267,6 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
@Test
public void onDeviceAttributesChanged_newDevice_newPreference() {
prepareDevice(/* hasMember= */ false);
mController.init(mScreen);
// check the right control is null before onDeviceAttributesChanged()
@@ -231,16 +286,34 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
assertThat(updatedRightControl).isNotNull();
}
private void prepareDevice(boolean hasMember) {
prepareDevice(hasMember, false);
@Test
public void onAmbientChanged_refreshWhenNotInitiateFromUi() {
prepareDevice(/* hasMember= */ false);
mController.init(mScreen);
final int testAmbient = 10;
HearingDeviceLocalDataManager.Data data = new HearingDeviceLocalDataManager.Data.Builder()
.ambient(testAmbient)
.groupAmbient(testAmbient)
.ambientControlExpanded(false)
.build();
when(mLocalDataManager.get(mDevice)).thenReturn(data);
getPreference().setExpanded(true);
mController.onAmbientChanged(mDevice, testAmbient);
verify(mController, never()).refresh();
final int updatedTestAmbient = 20;
mController.onAmbientChanged(mDevice, updatedTestAmbient);
verify(mController).refresh();
}
private void prepareDevice(boolean hasMember, boolean controlExpanded) {
private void prepareDevice(boolean hasMember) {
when(mCachedDevice.getDeviceSide()).thenReturn(SIDE_LEFT);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mCachedDevice.getBondState()).thenReturn(BOND_BONDED);
when(mDevice.getAddress()).thenReturn(TEST_ADDRESS);
when(mDevice.getAnonymizedAddress()).thenReturn(TEST_ADDRESS);
when(mDevice.isConnected()).thenReturn(true);
if (hasMember) {
when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mCachedMemberDevice));
when(mCachedMemberDevice.getDeviceSide()).thenReturn(SIDE_RIGHT);
@@ -248,14 +321,8 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
when(mCachedMemberDevice.getBondState()).thenReturn(BOND_BONDED);
when(mMemberDevice.getAddress()).thenReturn(TEST_MEMBER_ADDRESS);
when(mMemberDevice.getAnonymizedAddress()).thenReturn(TEST_MEMBER_ADDRESS);
when(mMemberDevice.isConnected()).thenReturn(true);
}
HearingDeviceLocalDataManager.Data data = new HearingDeviceLocalDataManager.Data.Builder()
.ambient(0).groupAmbient(0).ambientControlExpanded(controlExpanded).build();
when(mLocalDataManager.get(any(BluetoothDevice.class))).thenReturn(data);
}
private HearingDeviceLocalDataManager.Data prepareEmptyData() {
return new HearingDeviceLocalDataManager.Data.Builder().build();
}
private void verifyDeviceDataUpdated(BluetoothDevice device) {
@@ -265,6 +332,10 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
anyBoolean());
}
private AmbientVolumePreference getPreference() {
return mScreen.findPreference(KEY_AMBIENT_VOLUME);
}
@Implements(value = Settings.Global.class)
public static class ShadowGlobal extends ShadowSettings.ShadowGlobal {
private static final Map<ContentResolver, Map<String, String>> sDataMap = new HashMap<>();