[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:
@@ -170,6 +170,10 @@
|
||||
<string name="bluetooth_ambient_volume_control_expand">Expand to left and right separated controls</string>
|
||||
<!-- Connected devices settings. Content description for the icon to collapse the left and right separated ambient volume controls to unified control. [CHAR LIMIT=NONE] -->
|
||||
<string name="bluetooth_ambient_volume_control_collapse">Collapse to unified control</string>
|
||||
<!-- Connected devices settings. The text to show the control is for left side device. [CHAR LIMIT=30] -->
|
||||
<string name="bluetooth_ambient_volume_control_left">Left</string>
|
||||
<!-- Connected devices settings. The text to show the control is for right side device. [CHAR LIMIT=30] -->
|
||||
<string name="bluetooth_ambient_volume_control_right">Right</string>
|
||||
<!-- Connected devices settings. Title of the preference to show the entrance of the audio output page. It can change different types of audio are played on phone or other bluetooth devices. [CHAR LIMIT=35] -->
|
||||
<string name="bluetooth_audio_routing_title">Audio output</string>
|
||||
<!-- Title for bluetooth audio routing page footer. [CHAR LIMIT=30] -->
|
||||
|
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.settings.bluetooth;
|
||||
|
||||
import static android.bluetooth.BluetoothDevice.BOND_BONDED;
|
||||
|
||||
import static com.android.settings.bluetooth.BluetoothDetailsAmbientVolumePreferenceController.KEY_AMBIENT_VOLUME;
|
||||
import static com.android.settings.bluetooth.BluetoothDetailsAmbientVolumePreferenceController.KEY_AMBIENT_VOLUME_SLIDER;
|
||||
import static com.android.settings.bluetooth.BluetoothDetailsHearingDeviceController.KEY_HEARING_DEVICE_GROUP;
|
||||
@@ -24,17 +26,26 @@ import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_R
|
||||
|
||||
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.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.ContentResolver;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.PreferenceCategory;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowThreadUtils;
|
||||
import com.android.settings.widget.SeekBarPreference;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.HearingDeviceLocalDataManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@@ -45,12 +56,19 @@ import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.shadows.ShadowSettings;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/** Tests for {@link BluetoothDetailsAmbientVolumePreferenceController}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {
|
||||
BluetoothDetailsAmbientVolumePreferenceControllerTest.ShadowGlobal.class,
|
||||
ShadowThreadUtils.class
|
||||
})
|
||||
public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
|
||||
@@ -70,6 +88,8 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
|
||||
private BluetoothDevice mDevice;
|
||||
@Mock
|
||||
private BluetoothDevice mMemberDevice;
|
||||
@Mock
|
||||
private HearingDeviceLocalDataManager mLocalDataManager;
|
||||
|
||||
private BluetoothDetailsAmbientVolumePreferenceController mController;
|
||||
|
||||
@@ -81,7 +101,7 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
|
||||
deviceControls.setKey(KEY_HEARING_DEVICE_GROUP);
|
||||
mScreen.addPreference(deviceControls);
|
||||
mController = new BluetoothDetailsAmbientVolumePreferenceController(mContext, mFragment,
|
||||
mCachedDevice, mLifecycle);
|
||||
mCachedDevice, mLifecycle, mLocalDataManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -106,6 +126,88 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
|
||||
assertThat(preference.isExpandable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceLocalDataChange_noMemberAndExpanded_uiCorrectAndDataUpdated() {
|
||||
prepareDevice(/* hasMember= */ false, /* controlExpanded= */ true);
|
||||
|
||||
mController.init(mScreen);
|
||||
mController.onDeviceLocalDataChange(TEST_ADDRESS, prepareEmptyData());
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
AmbientVolumePreference preference = mScreen.findPreference(KEY_AMBIENT_VOLUME);
|
||||
assertThat(preference).isNotNull();
|
||||
assertThat(preference.isExpanded()).isFalse();
|
||||
verifyDeviceDataUpdated(mDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceLocalDataChange_noMemberAndCollapsed_uiCorrectAndDataUpdated() {
|
||||
prepareDevice(/* hasMember= */ false, /* controlExpanded= */ false);
|
||||
|
||||
mController.init(mScreen);
|
||||
mController.onDeviceLocalDataChange(TEST_ADDRESS, prepareEmptyData());
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
AmbientVolumePreference preference = mScreen.findPreference(KEY_AMBIENT_VOLUME);
|
||||
assertThat(preference).isNotNull();
|
||||
assertThat(preference.isExpanded()).isFalse();
|
||||
verifyDeviceDataUpdated(mDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceLocalDataChange_hasMemberAndExpanded_uiCorrectAndDataUpdated() {
|
||||
prepareDevice(/* hasMember= */ true, /* controlExpanded= */ true);
|
||||
|
||||
mController.init(mScreen);
|
||||
mController.onDeviceLocalDataChange(TEST_ADDRESS, prepareEmptyData());
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
AmbientVolumePreference preference = mScreen.findPreference(KEY_AMBIENT_VOLUME);
|
||||
assertThat(preference).isNotNull();
|
||||
assertThat(preference.isExpanded()).isTrue();
|
||||
verifyDeviceDataUpdated(mDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceLocalDataChange_hasMemberAndCollapsed_uiCorrectAndDataUpdated() {
|
||||
prepareDevice(/* hasMember= */ true, /* controlExpanded= */ false);
|
||||
|
||||
mController.init(mScreen);
|
||||
mController.onDeviceLocalDataChange(TEST_ADDRESS, prepareEmptyData());
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
AmbientVolumePreference preference = mScreen.findPreference(KEY_AMBIENT_VOLUME);
|
||||
assertThat(preference).isNotNull();
|
||||
assertThat(preference.isExpanded()).isFalse();
|
||||
verifyDeviceDataUpdated(mDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_localDataManagerStartAndCallbackRegistered() {
|
||||
prepareDevice(/* hasMember= */ true);
|
||||
|
||||
mController.init(mScreen);
|
||||
mController.onStart();
|
||||
|
||||
verify(mLocalDataManager, atLeastOnce()).start();
|
||||
verify(mCachedDevice).registerCallback(any(Executor.class),
|
||||
any(CachedBluetoothDevice.Callback.class));
|
||||
verify(mCachedMemberDevice).registerCallback(any(Executor.class),
|
||||
any(CachedBluetoothDevice.Callback.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_localDataManagerStopAndCallbackUnregistered() {
|
||||
prepareDevice(/* hasMember= */ true);
|
||||
|
||||
mController.init(mScreen);
|
||||
mController.onStop();
|
||||
|
||||
verify(mLocalDataManager).stop();
|
||||
verify(mCachedDevice).unregisterCallback(any(CachedBluetoothDevice.Callback.class));
|
||||
verify(mCachedMemberDevice).unregisterCallback(any(CachedBluetoothDevice.Callback.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceAttributesChanged_newDevice_newPreference() {
|
||||
prepareDevice(/* hasMember= */ false);
|
||||
@@ -130,14 +232,57 @@ public class BluetoothDetailsAmbientVolumePreferenceControllerTest extends
|
||||
}
|
||||
|
||||
private void prepareDevice(boolean hasMember) {
|
||||
prepareDevice(hasMember, false);
|
||||
}
|
||||
|
||||
private void prepareDevice(boolean hasMember, boolean controlExpanded) {
|
||||
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);
|
||||
if (hasMember) {
|
||||
when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mCachedMemberDevice));
|
||||
when(mCachedMemberDevice.getDeviceSide()).thenReturn(SIDE_RIGHT);
|
||||
when(mCachedMemberDevice.getDevice()).thenReturn(mMemberDevice);
|
||||
when(mCachedMemberDevice.getBondState()).thenReturn(BOND_BONDED);
|
||||
when(mMemberDevice.getAddress()).thenReturn(TEST_MEMBER_ADDRESS);
|
||||
when(mMemberDevice.getAnonymizedAddress()).thenReturn(TEST_MEMBER_ADDRESS);
|
||||
}
|
||||
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) {
|
||||
verify(mLocalDataManager, atLeastOnce()).updateAmbient(eq(device), anyInt());
|
||||
verify(mLocalDataManager, atLeastOnce()).updateGroupAmbient(eq(device), anyInt());
|
||||
verify(mLocalDataManager, atLeastOnce()).updateAmbientControlExpanded(eq(device),
|
||||
anyBoolean());
|
||||
}
|
||||
|
||||
@Implements(value = Settings.Global.class)
|
||||
public static class ShadowGlobal extends ShadowSettings.ShadowGlobal {
|
||||
private static final Map<ContentResolver, Map<String, String>> sDataMap = new HashMap<>();
|
||||
|
||||
@Implementation
|
||||
protected static boolean putStringForUser(
|
||||
ContentResolver cr, String name, String value, int userHandle) {
|
||||
get(cr).put(name, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected static String getStringForUser(ContentResolver cr, String name, int userHandle) {
|
||||
return get(cr).get(name);
|
||||
}
|
||||
|
||||
private static Map<String, String> get(ContentResolver cr) {
|
||||
return sDataMap.computeIfAbsent(cr, k -> new HashMap<>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user