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