From 45fb7a75cf6d81f275303d2a3b74b4140fd694da Mon Sep 17 00:00:00 2001 From: Amin Shaikh Date: Wed, 18 Apr 2018 14:14:38 -0400 Subject: [PATCH] Update bluetooth QS tile secondary text. Only show detailed status when connected to exactly one device. Otherwise show "X devices". Also, listen for bluetooth profile service callbacks to initialize the bluetooth device state after systemui crashes. TODO: Update string to "X devices connected" after P Change-Id: I99a3484c3dfa7eb0d6d8836336ebb84cbdaadbe5 Fixes: 76102598 Fixes: 77498698 Test: manual --- .../systemui/qs/tiles/BluetoothTile.java | 18 ++++++-- .../statusbar/policy/BluetoothController.java | 5 ++- .../policy/BluetoothControllerImpl.java | 45 +++++++++++-------- .../policy/BluetoothControllerImplTest.java | 3 ++ .../utils/leaks/FakeBluetoothController.java | 8 ++-- 5 files changed, 53 insertions(+), 26 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index 0c7919d11042d..a25c466a2b403 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -51,6 +51,7 @@ import com.android.systemui.statusbar.policy.BluetoothController; import java.util.ArrayList; import java.util.Collection; +import java.util.List; /** Quick settings tile: Bluetooth **/ public class BluetoothTile extends QSTileImpl { @@ -137,7 +138,9 @@ public class BluetoothTile extends QSTileImpl { if (enabled) { if (connected) { state.icon = new BluetoothConnectedTileIcon(); - state.label = mController.getLastDeviceName(); + if (!TextUtils.isEmpty(mController.getConnectedDeviceName())) { + state.label = mController.getConnectedDeviceName(); + } state.contentDescription = mContext.getString(R.string.accessibility_bluetooth_name, state.label) + ", " + state.secondaryLabel; @@ -177,9 +180,18 @@ public class BluetoothTile extends QSTileImpl { if (isTransient) { return mContext.getString(R.string.quick_settings_bluetooth_secondary_label_transient); } - final CachedBluetoothDevice lastDevice = mController.getLastDevice(); - if (enabled && connected && lastDevice != null) { + List connectedDevices = mController.getConnectedDevices(); + if (enabled && connected && !connectedDevices.isEmpty()) { + if (connectedDevices.size() > 1) { + // TODO(b/76102598): add a new string for "X connected devices" after P + return mContext.getResources().getQuantityString( + R.plurals.quick_settings_hotspot_secondary_label_num_devices, + connectedDevices.size(), + connectedDevices.size()); + } + + CachedBluetoothDevice lastDevice = connectedDevices.get(0); final int batteryLevel = lastDevice.getBatteryLevel(); if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java index b693ebbf09a41..42e02d5846bc8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java @@ -21,6 +21,7 @@ import com.android.systemui.Dumpable; import com.android.systemui.statusbar.policy.BluetoothController.Callback; import java.util.Collection; +import java.util.List; public interface BluetoothController extends CallbackController, Dumpable { boolean isBluetoothSupported(); @@ -30,7 +31,7 @@ public interface BluetoothController extends CallbackController, Dumpa boolean isBluetoothConnected(); boolean isBluetoothConnecting(); - String getLastDeviceName(); + String getConnectedDeviceName(); void setBluetoothEnabled(boolean enabled); Collection getDevices(); void connect(CachedBluetoothDevice device); @@ -39,7 +40,7 @@ public interface BluetoothController extends CallbackController, Dumpa int getMaxConnectionState(CachedBluetoothDevice device); int getBondState(CachedBluetoothDevice device); - CachedBluetoothDevice getLastDevice(); + List getConnectedDevices(); public interface Callback { void onBluetoothStateChange(boolean enabled); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java index cd17cfcd0e9fb..44e87ff7b24ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -31,6 +31,7 @@ import android.util.Log; import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.systemui.Dependency; import java.io.FileDescriptor; @@ -38,10 +39,11 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.WeakHashMap; public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback, - CachedBluetoothDevice.Callback { + CachedBluetoothDevice.Callback, LocalBluetoothProfileManager.ServiceListener { private static final String TAG = "BluetoothController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -51,10 +53,10 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa private final WeakHashMap mCachedState = new WeakHashMap<>(); private final Handler mBgHandler; + private final List mConnectedDevices = new ArrayList<>(); private boolean mEnabled; private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED; - private CachedBluetoothDevice mLastDevice; private final H mHandler = new H(Looper.getMainLooper()); private int mState; @@ -65,6 +67,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa if (mLocalBluetoothManager != null) { mLocalBluetoothManager.getEventManager().setReceiverHandler(mBgHandler); mLocalBluetoothManager.getEventManager().registerCallback(this); + mLocalBluetoothManager.getProfileManager().addServiceListener(this); onBluetoothStateChanged( mLocalBluetoothManager.getBluetoothAdapter().getBluetoothState()); } @@ -88,11 +91,10 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa } pw.print(" mEnabled="); pw.println(mEnabled); pw.print(" mConnectionState="); pw.println(stateToString(mConnectionState)); - pw.print(" mLastDevice="); pw.println(mLastDevice); + pw.print(" mConnectedDevices="); pw.println(mConnectedDevices); pw.print(" mCallbacks.size="); pw.println(mHandler.mCallbacks.size()); pw.println(" Bluetooth Devices:"); - for (CachedBluetoothDevice device : - mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) { + for (CachedBluetoothDevice device : getDevices()) { pw.println(" " + getDeviceString(device)); } } @@ -121,8 +123,8 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa } @Override - public CachedBluetoothDevice getLastDevice() { - return mLastDevice; + public List getConnectedDevices() { + return mConnectedDevices; } @Override @@ -186,8 +188,11 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa } @Override - public String getLastDeviceName() { - return mLastDevice != null ? mLastDevice.getName() : null; + public String getConnectedDeviceName() { + if (mConnectedDevices.size() == 1) { + return mConnectedDevices.get(0).getName(); + } + return null; } @Override @@ -200,10 +205,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa private void updateConnected() { // Make sure our connection state is up to date. int state = mLocalBluetoothManager.getBluetoothAdapter().getConnectionState(); - if (mLastDevice != null && !mLastDevice.isConnected()) { - // Clear out last device if no longer connected. - mLastDevice = null; - } + mConnectedDevices.clear(); // If any of the devices are in a higher state than the adapter, move the adapter into // that state. for (CachedBluetoothDevice device : getDevices()) { @@ -211,13 +213,12 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa if (maxDeviceState > state) { state = maxDeviceState; } - if (mLastDevice == null && device.isConnected()) { - // Set as last connected device only if we don't have one. - mLastDevice = device; + if (device.isConnected()) { + mConnectedDevices.add(device); } } - if (mLastDevice == null && state == BluetoothAdapter.STATE_CONNECTED) { + if (mConnectedDevices.isEmpty() && state == BluetoothAdapter.STATE_CONNECTED) { // If somehow we think we are connected, but have no connected devices, we aren't // connected. state = BluetoothAdapter.STATE_DISCONNECTED; @@ -271,7 +272,6 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa @Override public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { mCachedState.remove(cachedDevice); - mLastDevice = cachedDevice; updateConnected(); mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED); } @@ -293,6 +293,15 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa return state; } + @Override + public void onServiceConnected() { + updateConnected(); + mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED); + } + + @Override + public void onServiceDisconnected() {} + private static class ActuallyCachedState implements Runnable { private final WeakReference mDevice; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java index 4e7550b206599..54153a75f1b12 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java @@ -35,6 +35,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.LocalBluetoothAdapter; import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.systemui.SysuiTestCase; import org.junit.Before; @@ -68,6 +69,8 @@ public class BluetoothControllerImplTest extends SysuiTestCase { mMockAdapter = mock(LocalBluetoothAdapter.class); when(mMockBluetoothManager.getBluetoothAdapter()).thenReturn(mMockAdapter); when(mMockBluetoothManager.getEventManager()).thenReturn(mock(BluetoothEventManager.class)); + when(mMockBluetoothManager.getProfileManager()) + .thenReturn(mock(LocalBluetoothProfileManager.class)); mBluetoothControllerImpl = new BluetoothControllerImpl(mContext, mTestableLooper.getLooper()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java index 44c49835def41..cac6bf7ca3f08 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java @@ -21,6 +21,8 @@ import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.BluetoothController.Callback; import java.util.Collection; +import java.util.Collections; +import java.util.List; public class FakeBluetoothController extends BaseLeakChecker implements BluetoothController { @@ -55,7 +57,7 @@ public class FakeBluetoothController extends BaseLeakChecker implement } @Override - public String getLastDeviceName() { + public String getConnectedDeviceName() { return null; } @@ -95,7 +97,7 @@ public class FakeBluetoothController extends BaseLeakChecker implement } @Override - public CachedBluetoothDevice getLastDevice() { - return null; + public List getConnectedDevices() { + return Collections.emptyList(); } }