Use FastPair battery level in blueooth QS tile.
If FastPair battery level is not available, this will fallback to the regular battery level as provided by the bluetooth stack. Bug: 266467369 Test: BluetoothTileTest Change-Id: I97e2b80b2c39f5c622fe15a93b5dd4071952f4e5
This commit is contained in:
@@ -18,24 +18,26 @@ package com.android.systemui.qs.tiles;
|
||||
|
||||
import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.Looper;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Switch;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.dagger.qualifiers.Background;
|
||||
@@ -50,6 +52,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl;
|
||||
import com.android.systemui.statusbar.policy.BluetoothController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -60,8 +63,14 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
|
||||
|
||||
private static final Intent BLUETOOTH_SETTINGS = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
|
||||
|
||||
private static final String TAG = BluetoothTile.class.getSimpleName();
|
||||
|
||||
private final BluetoothController mController;
|
||||
|
||||
private CachedBluetoothDevice mMetadataRegisteredDevice = null;
|
||||
|
||||
private final Executor mExecutor;
|
||||
|
||||
@Inject
|
||||
public BluetoothTile(
|
||||
QSHost host,
|
||||
@@ -78,6 +87,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
|
||||
statusBarStateController, activityStarter, qsLogger);
|
||||
mController = bluetoothController;
|
||||
mController.observe(getLifecycle(), mCallback);
|
||||
mExecutor = new HandlerExecutor(mainHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,6 +126,15 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
|
||||
return mContext.getString(R.string.quick_settings_bluetooth_label);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleSetListening(boolean listening) {
|
||||
super.handleSetListening(listening);
|
||||
|
||||
if (!listening) {
|
||||
stopListeningToStaleDeviceMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleUpdateState(BooleanState state, Object arg) {
|
||||
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_BLUETOOTH);
|
||||
@@ -125,6 +144,9 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
|
||||
final boolean connecting = mController.isBluetoothConnecting();
|
||||
state.isTransient = transientEnabling || connecting ||
|
||||
mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON;
|
||||
if (!enabled || !connected || state.isTransient) {
|
||||
stopListeningToStaleDeviceMetadata();
|
||||
}
|
||||
state.dualTarget = true;
|
||||
state.value = enabled;
|
||||
if (state.slash == null) {
|
||||
@@ -187,23 +209,32 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
|
||||
List<CachedBluetoothDevice> connectedDevices = mController.getConnectedDevices();
|
||||
if (enabled && connected && !connectedDevices.isEmpty()) {
|
||||
if (connectedDevices.size() > 1) {
|
||||
stopListeningToStaleDeviceMetadata();
|
||||
return icuMessageFormat(mContext.getResources(),
|
||||
R.string.quick_settings_hotspot_secondary_label_num_devices,
|
||||
connectedDevices.size());
|
||||
}
|
||||
|
||||
CachedBluetoothDevice lastDevice = connectedDevices.get(0);
|
||||
final int batteryLevel = lastDevice.getBatteryLevel();
|
||||
CachedBluetoothDevice device = connectedDevices.get(0);
|
||||
|
||||
// Use battery level provided by FastPair metadata if available.
|
||||
// If not, fallback to the default battery level from bluetooth.
|
||||
int batteryLevel = getMetadataBatteryLevel(device);
|
||||
if (batteryLevel > BluetoothUtils.META_INT_ERROR) {
|
||||
listenToMetadata(device);
|
||||
} else {
|
||||
stopListeningToStaleDeviceMetadata();
|
||||
batteryLevel = device.getBatteryLevel();
|
||||
}
|
||||
|
||||
if (batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
|
||||
return mContext.getString(
|
||||
R.string.quick_settings_bluetooth_secondary_label_battery_level,
|
||||
Utils.formatPercentage(batteryLevel));
|
||||
|
||||
} else {
|
||||
final BluetoothClass bluetoothClass = lastDevice.getBtClass();
|
||||
final BluetoothClass bluetoothClass = device.getBtClass();
|
||||
if (bluetoothClass != null) {
|
||||
if (lastDevice.isHearingAidDevice()) {
|
||||
if (device.isHearingAidDevice()) {
|
||||
return mContext.getString(
|
||||
R.string.quick_settings_bluetooth_secondary_label_hearing_aids);
|
||||
} else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
|
||||
@@ -233,6 +264,36 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
|
||||
return mController.isBluetoothSupported();
|
||||
}
|
||||
|
||||
private int getMetadataBatteryLevel(CachedBluetoothDevice device) {
|
||||
return BluetoothUtils.getIntMetaData(device.getDevice(),
|
||||
BluetoothDevice.METADATA_MAIN_BATTERY);
|
||||
}
|
||||
|
||||
private void listenToMetadata(CachedBluetoothDevice cachedDevice) {
|
||||
if (cachedDevice == mMetadataRegisteredDevice) return;
|
||||
stopListeningToStaleDeviceMetadata();
|
||||
try {
|
||||
mController.addOnMetadataChangedListener(cachedDevice,
|
||||
mExecutor,
|
||||
mMetadataChangedListener);
|
||||
mMetadataRegisteredDevice = cachedDevice;
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(TAG, "Battery metadata listener already registered for device.");
|
||||
}
|
||||
}
|
||||
|
||||
private void stopListeningToStaleDeviceMetadata() {
|
||||
if (mMetadataRegisteredDevice == null) return;
|
||||
try {
|
||||
mController.removeOnMetadataChangedListener(
|
||||
mMetadataRegisteredDevice,
|
||||
mMetadataChangedListener);
|
||||
mMetadataRegisteredDevice = null;
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(TAG, "Battery metadata listener already unregistered for device.");
|
||||
}
|
||||
}
|
||||
|
||||
private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
|
||||
@Override
|
||||
public void onBluetoothStateChange(boolean enabled) {
|
||||
@@ -244,4 +305,9 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
|
||||
refreshState();
|
||||
}
|
||||
};
|
||||
|
||||
private final BluetoothAdapter.OnMetadataChangedListener mMetadataChangedListener =
|
||||
(device, key, value) -> {
|
||||
if (key == BluetoothDevice.METADATA_MAIN_BATTERY) refreshState();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,12 +16,15 @@
|
||||
|
||||
package com.android.systemui.statusbar.policy;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.systemui.Dumpable;
|
||||
import com.android.systemui.statusbar.policy.BluetoothController.Callback;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public interface BluetoothController extends CallbackController<Callback>, Dumpable {
|
||||
boolean isBluetoothSupported();
|
||||
@@ -44,6 +47,11 @@ public interface BluetoothController extends CallbackController<Callback>, Dumpa
|
||||
int getBondState(CachedBluetoothDevice device);
|
||||
List<CachedBluetoothDevice> getConnectedDevices();
|
||||
|
||||
void addOnMetadataChangedListener(CachedBluetoothDevice device, Executor executor,
|
||||
BluetoothAdapter.OnMetadataChangedListener listener);
|
||||
void removeOnMetadataChangedListener(CachedBluetoothDevice device,
|
||||
BluetoothAdapter.OnMetadataChangedListener listener);
|
||||
|
||||
public interface Callback {
|
||||
void onBluetoothStateChange(boolean enabled);
|
||||
void onBluetoothDevicesChanged();
|
||||
|
||||
@@ -48,6 +48,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -78,6 +79,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
|
||||
private final H mHandler;
|
||||
private int mState;
|
||||
|
||||
private final BluetoothAdapter mAdapter;
|
||||
/**
|
||||
*/
|
||||
@Inject
|
||||
@@ -88,7 +90,8 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
|
||||
BluetoothLogger logger,
|
||||
@Background Looper bgLooper,
|
||||
@Main Looper mainLooper,
|
||||
@Nullable LocalBluetoothManager localBluetoothManager) {
|
||||
@Nullable LocalBluetoothManager localBluetoothManager,
|
||||
@Nullable BluetoothAdapter bluetoothAdapter) {
|
||||
mDumpManager = dumpManager;
|
||||
mLogger = logger;
|
||||
mLocalBluetoothManager = localBluetoothManager;
|
||||
@@ -103,6 +106,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
|
||||
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
mCurrentUser = userTracker.getUserId();
|
||||
mDumpManager.registerDumpable(TAG, this);
|
||||
mAdapter = bluetoothAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -412,6 +416,30 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
|
||||
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
|
||||
}
|
||||
|
||||
public void addOnMetadataChangedListener(
|
||||
@NonNull CachedBluetoothDevice cachedDevice,
|
||||
Executor executor,
|
||||
BluetoothAdapter.OnMetadataChangedListener listener
|
||||
) {
|
||||
if (mAdapter == null) return;
|
||||
mAdapter.addOnMetadataChangedListener(
|
||||
cachedDevice.getDevice(),
|
||||
executor,
|
||||
listener
|
||||
);
|
||||
}
|
||||
|
||||
public void removeOnMetadataChangedListener(
|
||||
@NonNull CachedBluetoothDevice cachedDevice,
|
||||
BluetoothAdapter.OnMetadataChangedListener listener
|
||||
) {
|
||||
if (mAdapter == null) return;
|
||||
mAdapter.removeOnMetadataChangedListener(
|
||||
cachedDevice.getDevice(),
|
||||
listener
|
||||
);
|
||||
}
|
||||
|
||||
private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
|
||||
ActuallyCachedState state = mCachedState.get(device);
|
||||
if (state == null) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.android.systemui.qs.tiles
|
||||
|
||||
import android.content.Context
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.UserManager
|
||||
@@ -10,6 +10,8 @@ import android.testing.TestableLooper.RunWithLooper
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.logging.MetricsLogger
|
||||
import com.android.internal.logging.testing.UiEventLoggerFake
|
||||
import com.android.settingslib.Utils
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.classifier.FalsingManagerFake
|
||||
@@ -21,14 +23,18 @@ import com.android.systemui.qs.QSHost
|
||||
import com.android.systemui.qs.logging.QSLogger
|
||||
import com.android.systemui.qs.tileimpl.QSTileImpl
|
||||
import com.android.systemui.statusbar.policy.BluetoothController
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.eq
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@@ -36,21 +42,13 @@ import org.mockito.MockitoAnnotations
|
||||
@SmallTest
|
||||
class BluetoothTileTest : SysuiTestCase() {
|
||||
|
||||
@Mock
|
||||
private lateinit var mockContext: Context
|
||||
@Mock
|
||||
private lateinit var qsLogger: QSLogger
|
||||
@Mock
|
||||
private lateinit var qsHost: QSHost
|
||||
@Mock
|
||||
private lateinit var metricsLogger: MetricsLogger
|
||||
@Mock private lateinit var qsLogger: QSLogger
|
||||
@Mock private lateinit var qsHost: QSHost
|
||||
@Mock private lateinit var metricsLogger: MetricsLogger
|
||||
private val falsingManager = FalsingManagerFake()
|
||||
@Mock
|
||||
private lateinit var statusBarStateController: StatusBarStateController
|
||||
@Mock
|
||||
private lateinit var activityStarter: ActivityStarter
|
||||
@Mock
|
||||
private lateinit var bluetoothController: BluetoothController
|
||||
@Mock private lateinit var statusBarStateController: StatusBarStateController
|
||||
@Mock private lateinit var activityStarter: ActivityStarter
|
||||
@Mock private lateinit var bluetoothController: BluetoothController
|
||||
|
||||
private val uiEventLogger = UiEventLoggerFake()
|
||||
private lateinit var testableLooper: TestableLooper
|
||||
@@ -61,20 +59,21 @@ class BluetoothTileTest : SysuiTestCase() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
testableLooper = TestableLooper.get(this)
|
||||
|
||||
Mockito.`when`(qsHost.context).thenReturn(mockContext)
|
||||
Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
|
||||
whenever(qsHost.context).thenReturn(mContext)
|
||||
whenever(qsHost.uiEventLogger).thenReturn(uiEventLogger)
|
||||
|
||||
tile = FakeBluetoothTile(
|
||||
qsHost,
|
||||
testableLooper.looper,
|
||||
Handler(testableLooper.looper),
|
||||
falsingManager,
|
||||
metricsLogger,
|
||||
statusBarStateController,
|
||||
activityStarter,
|
||||
qsLogger,
|
||||
bluetoothController
|
||||
)
|
||||
tile =
|
||||
FakeBluetoothTile(
|
||||
qsHost,
|
||||
testableLooper.looper,
|
||||
Handler(testableLooper.looper),
|
||||
falsingManager,
|
||||
metricsLogger,
|
||||
statusBarStateController,
|
||||
activityStarter,
|
||||
qsLogger,
|
||||
bluetoothController,
|
||||
)
|
||||
|
||||
tile.initialize()
|
||||
testableLooper.processAllMessages()
|
||||
@@ -102,7 +101,7 @@ class BluetoothTileTest : SysuiTestCase() {
|
||||
tile.handleUpdateState(state, /* arg= */ null)
|
||||
|
||||
assertThat(state.icon)
|
||||
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
|
||||
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -114,7 +113,7 @@ class BluetoothTileTest : SysuiTestCase() {
|
||||
tile.handleUpdateState(state, /* arg= */ null)
|
||||
|
||||
assertThat(state.icon)
|
||||
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
|
||||
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -126,7 +125,7 @@ class BluetoothTileTest : SysuiTestCase() {
|
||||
tile.handleUpdateState(state, /* arg= */ null)
|
||||
|
||||
assertThat(state.icon)
|
||||
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_on))
|
||||
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_on))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -138,7 +137,76 @@ class BluetoothTileTest : SysuiTestCase() {
|
||||
tile.handleUpdateState(state, /* arg= */ null)
|
||||
|
||||
assertThat(state.icon)
|
||||
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_search))
|
||||
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_search))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSecondaryLabel_whenBatteryMetadataAvailable_isMetadataBatteryLevelState() {
|
||||
val cachedDevice = mock<CachedBluetoothDevice>()
|
||||
val state = QSTile.BooleanState()
|
||||
listenToDeviceMetadata(state, cachedDevice, 50)
|
||||
|
||||
tile.handleUpdateState(state, /* arg= */ null)
|
||||
|
||||
assertThat(state.secondaryLabel)
|
||||
.isEqualTo(
|
||||
mContext.getString(
|
||||
R.string.quick_settings_bluetooth_secondary_label_battery_level,
|
||||
Utils.formatPercentage(50)
|
||||
)
|
||||
)
|
||||
verify(bluetoothController)
|
||||
.addOnMetadataChangedListener(eq(cachedDevice), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSecondaryLabel_whenBatteryMetadataUnavailable_isBluetoothBatteryLevelState() {
|
||||
val state = QSTile.BooleanState()
|
||||
val cachedDevice = mock<CachedBluetoothDevice>()
|
||||
listenToDeviceMetadata(state, cachedDevice, 50)
|
||||
val cachedDevice2 = mock<CachedBluetoothDevice>()
|
||||
val btDevice = mock<BluetoothDevice>()
|
||||
whenever(cachedDevice2.device).thenReturn(btDevice)
|
||||
whenever(btDevice.getMetadata(BluetoothDevice.METADATA_MAIN_BATTERY)).thenReturn(null)
|
||||
whenever(cachedDevice2.batteryLevel).thenReturn(25)
|
||||
addConnectedDevice(cachedDevice2)
|
||||
|
||||
tile.handleUpdateState(state, /* arg= */ null)
|
||||
|
||||
assertThat(state.secondaryLabel)
|
||||
.isEqualTo(
|
||||
mContext.getString(
|
||||
R.string.quick_settings_bluetooth_secondary_label_battery_level,
|
||||
Utils.formatPercentage(25)
|
||||
)
|
||||
)
|
||||
verify(bluetoothController, times(1))
|
||||
.removeOnMetadataChangedListener(eq(cachedDevice), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMetadataListener_whenDisconnected_isUnregistered() {
|
||||
val state = QSTile.BooleanState()
|
||||
val cachedDevice = mock<CachedBluetoothDevice>()
|
||||
listenToDeviceMetadata(state, cachedDevice, 50)
|
||||
disableBluetooth()
|
||||
|
||||
tile.handleUpdateState(state, null)
|
||||
|
||||
verify(bluetoothController, times(1))
|
||||
.removeOnMetadataChangedListener(eq(cachedDevice), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMetadataListener_whenTileNotListening_isUnregistered() {
|
||||
val state = QSTile.BooleanState()
|
||||
val cachedDevice = mock<CachedBluetoothDevice>()
|
||||
listenToDeviceMetadata(state, cachedDevice, 50)
|
||||
|
||||
tile.handleSetListening(false)
|
||||
|
||||
verify(bluetoothController, times(1))
|
||||
.removeOnMetadataChangedListener(eq(cachedDevice), any())
|
||||
}
|
||||
|
||||
private class FakeBluetoothTile(
|
||||
@@ -150,18 +218,19 @@ class BluetoothTileTest : SysuiTestCase() {
|
||||
statusBarStateController: StatusBarStateController,
|
||||
activityStarter: ActivityStarter,
|
||||
qsLogger: QSLogger,
|
||||
bluetoothController: BluetoothController
|
||||
) : BluetoothTile(
|
||||
qsHost,
|
||||
backgroundLooper,
|
||||
mainHandler,
|
||||
falsingManager,
|
||||
metricsLogger,
|
||||
statusBarStateController,
|
||||
activityStarter,
|
||||
qsLogger,
|
||||
bluetoothController
|
||||
) {
|
||||
bluetoothController: BluetoothController,
|
||||
) :
|
||||
BluetoothTile(
|
||||
qsHost,
|
||||
backgroundLooper,
|
||||
mainHandler,
|
||||
falsingManager,
|
||||
metricsLogger,
|
||||
statusBarStateController,
|
||||
activityStarter,
|
||||
qsLogger,
|
||||
bluetoothController,
|
||||
) {
|
||||
var restrictionChecked: String? = null
|
||||
|
||||
override fun checkIfRestrictionEnforcedByAdminOnly(
|
||||
@@ -173,25 +242,44 @@ class BluetoothTileTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
fun enableBluetooth() {
|
||||
`when`(bluetoothController.isBluetoothEnabled).thenReturn(true)
|
||||
whenever(bluetoothController.isBluetoothEnabled).thenReturn(true)
|
||||
}
|
||||
|
||||
fun disableBluetooth() {
|
||||
`when`(bluetoothController.isBluetoothEnabled).thenReturn(false)
|
||||
whenever(bluetoothController.isBluetoothEnabled).thenReturn(false)
|
||||
}
|
||||
|
||||
fun setBluetoothDisconnected() {
|
||||
`when`(bluetoothController.isBluetoothConnecting).thenReturn(false)
|
||||
`when`(bluetoothController.isBluetoothConnected).thenReturn(false)
|
||||
whenever(bluetoothController.isBluetoothConnecting).thenReturn(false)
|
||||
whenever(bluetoothController.isBluetoothConnected).thenReturn(false)
|
||||
}
|
||||
|
||||
fun setBluetoothConnected() {
|
||||
`when`(bluetoothController.isBluetoothConnecting).thenReturn(false)
|
||||
`when`(bluetoothController.isBluetoothConnected).thenReturn(true)
|
||||
whenever(bluetoothController.isBluetoothConnecting).thenReturn(false)
|
||||
whenever(bluetoothController.isBluetoothConnected).thenReturn(true)
|
||||
}
|
||||
|
||||
fun setBluetoothConnecting() {
|
||||
`when`(bluetoothController.isBluetoothConnected).thenReturn(false)
|
||||
`when`(bluetoothController.isBluetoothConnecting).thenReturn(true)
|
||||
whenever(bluetoothController.isBluetoothConnected).thenReturn(false)
|
||||
whenever(bluetoothController.isBluetoothConnecting).thenReturn(true)
|
||||
}
|
||||
|
||||
fun addConnectedDevice(device: CachedBluetoothDevice) {
|
||||
whenever(bluetoothController.connectedDevices).thenReturn(listOf(device))
|
||||
}
|
||||
|
||||
fun listenToDeviceMetadata(
|
||||
state: QSTile.BooleanState,
|
||||
cachedDevice: CachedBluetoothDevice,
|
||||
batteryLevel: Int
|
||||
) {
|
||||
val btDevice = mock<BluetoothDevice>()
|
||||
whenever(cachedDevice.device).thenReturn(btDevice)
|
||||
whenever(btDevice.getMetadata(BluetoothDevice.METADATA_MAIN_BATTERY))
|
||||
.thenReturn(batteryLevel.toString().toByteArray())
|
||||
enableBluetooth()
|
||||
setBluetoothConnected()
|
||||
addConnectedDevice(cachedDevice)
|
||||
tile.handleUpdateState(state, /* arg= */ null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -44,6 +45,8 @@ import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.bluetooth.BluetoothLogger;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -51,6 +54,7 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper
|
||||
@@ -60,10 +64,11 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
|
||||
private UserTracker mUserTracker;
|
||||
private LocalBluetoothManager mMockBluetoothManager;
|
||||
private CachedBluetoothDeviceManager mMockDeviceManager;
|
||||
private LocalBluetoothAdapter mMockAdapter;
|
||||
private LocalBluetoothAdapter mMockLocalAdapter;
|
||||
private TestableLooper mTestableLooper;
|
||||
private DumpManager mMockDumpManager;
|
||||
private BluetoothControllerImpl mBluetoothControllerImpl;
|
||||
private BluetoothAdapter mMockAdapter;
|
||||
|
||||
private List<CachedBluetoothDevice> mDevices;
|
||||
|
||||
@@ -74,10 +79,11 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
|
||||
mDevices = new ArrayList<>();
|
||||
mUserTracker = mock(UserTracker.class);
|
||||
mMockDeviceManager = mock(CachedBluetoothDeviceManager.class);
|
||||
mMockAdapter = mock(BluetoothAdapter.class);
|
||||
when(mMockDeviceManager.getCachedDevicesCopy()).thenReturn(mDevices);
|
||||
when(mMockBluetoothManager.getCachedDeviceManager()).thenReturn(mMockDeviceManager);
|
||||
mMockAdapter = mock(LocalBluetoothAdapter.class);
|
||||
when(mMockBluetoothManager.getBluetoothAdapter()).thenReturn(mMockAdapter);
|
||||
mMockLocalAdapter = mock(LocalBluetoothAdapter.class);
|
||||
when(mMockBluetoothManager.getBluetoothAdapter()).thenReturn(mMockLocalAdapter);
|
||||
when(mMockBluetoothManager.getEventManager()).thenReturn(mock(BluetoothEventManager.class));
|
||||
when(mMockBluetoothManager.getProfileManager())
|
||||
.thenReturn(mock(LocalBluetoothProfileManager.class));
|
||||
@@ -89,7 +95,8 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
|
||||
mock(BluetoothLogger.class),
|
||||
mTestableLooper.getLooper(),
|
||||
mTestableLooper.getLooper(),
|
||||
mMockBluetoothManager);
|
||||
mMockBluetoothManager,
|
||||
mMockAdapter);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -98,7 +105,8 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
|
||||
when(device.isConnected()).thenReturn(true);
|
||||
when(device.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_CONNECTED);
|
||||
mDevices.add(device);
|
||||
when(mMockAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED);
|
||||
when(mMockLocalAdapter.getConnectionState())
|
||||
.thenReturn(BluetoothAdapter.STATE_DISCONNECTED);
|
||||
|
||||
mBluetoothControllerImpl.onConnectionStateChanged(null,
|
||||
BluetoothAdapter.STATE_DISCONNECTED);
|
||||
@@ -163,7 +171,7 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testOnServiceConnected_updatesConnectionState() {
|
||||
when(mMockAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING);
|
||||
when(mMockLocalAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING);
|
||||
|
||||
mBluetoothControllerImpl.onServiceConnected();
|
||||
|
||||
@@ -184,7 +192,7 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testOnBluetoothStateChange_updatesConnectionState() {
|
||||
when(mMockAdapter.getConnectionState()).thenReturn(
|
||||
when(mMockLocalAdapter.getConnectionState()).thenReturn(
|
||||
BluetoothAdapter.STATE_CONNECTING,
|
||||
BluetoothAdapter.STATE_DISCONNECTED);
|
||||
|
||||
@@ -240,6 +248,33 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
|
||||
assertTrue(mBluetoothControllerImpl.isBluetoothAudioProfileOnly());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddOnMetadataChangedListener_registersListenerOnAdapter() {
|
||||
CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
|
||||
BluetoothDevice device = mock(BluetoothDevice.class);
|
||||
when(cachedDevice.getDevice()).thenReturn(device);
|
||||
Executor executor = new FakeExecutor(new FakeSystemClock());
|
||||
BluetoothAdapter.OnMetadataChangedListener listener = (bluetoothDevice, i, bytes) -> {
|
||||
};
|
||||
|
||||
mBluetoothControllerImpl.addOnMetadataChangedListener(cachedDevice, executor, listener);
|
||||
|
||||
verify(mMockAdapter, times(1)).addOnMetadataChangedListener(device, executor, listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveOnMetadataChangedListener_removesListenerFromAdapter() {
|
||||
CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
|
||||
BluetoothDevice device = mock(BluetoothDevice.class);
|
||||
when(cachedDevice.getDevice()).thenReturn(device);
|
||||
BluetoothAdapter.OnMetadataChangedListener listener = (bluetoothDevice, i, bytes) -> {
|
||||
};
|
||||
|
||||
mBluetoothControllerImpl.removeOnMetadataChangedListener(cachedDevice, listener);
|
||||
|
||||
verify(mMockAdapter, times(1)).removeOnMetadataChangedListener(device, listener);
|
||||
}
|
||||
|
||||
/** Regression test for b/246876230. */
|
||||
@Test
|
||||
public void testOnActiveDeviceChanged_null_noCrash() {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.android.systemui.utils.leaks;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.testing.LeakCheck;
|
||||
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
@@ -23,6 +24,7 @@ import com.android.systemui.statusbar.policy.BluetoothController.Callback;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class FakeBluetoothController extends BaseLeakChecker<Callback> implements
|
||||
BluetoothController {
|
||||
@@ -110,4 +112,16 @@ public class FakeBluetoothController extends BaseLeakChecker<Callback> implement
|
||||
public List<CachedBluetoothDevice> getConnectedDevices() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOnMetadataChangedListener(CachedBluetoothDevice device, Executor executor,
|
||||
BluetoothAdapter.OnMetadataChangedListener listener) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOnMetadataChangedListener(CachedBluetoothDevice device,
|
||||
BluetoothAdapter.OnMetadataChangedListener listener) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user