diff options
5 files changed, 138 insertions, 10 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 9b20a7a8879b..79fb5b3c024a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -179,12 +179,6 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { @Override public void onBluetoothDevicesChanged() { - mUiHandler.post(new Runnable() { - @Override - public void run() { - mDetailAdapter.updateItems(); - } - }); refreshState(); if (isShowingDetail()) { mDetailAdapter.updateItems(); @@ -198,6 +192,9 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { } protected class BluetoothDetailAdapter implements DetailAdapter, QSDetailItems.Callback { + // We probably won't ever have space in the UI for more than 20 devices, so don't + // get info for them. + private static final int MAX_DEVICES = 20; private QSDetailItems mItems; @Override @@ -260,13 +257,14 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { final Collection<CachedBluetoothDevice> devices = mController.getDevices(); if (devices != null) { int connectedDevices = 0; + int count = 0; for (CachedBluetoothDevice device : devices) { - if (device.getBondState() == BluetoothDevice.BOND_NONE) continue; + if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue; final Item item = new Item(); item.icon = R.drawable.ic_qs_bluetooth_on; item.line1 = device.getName(); item.tag = device; - int state = device.getMaxConnectionState(); + int state = mController.getMaxConnectionState(device); if (state == BluetoothProfile.STATE_CONNECTED) { item.icon = R.drawable.ic_qs_bluetooth_connected; item.line2 = mContext.getString(R.string.quick_settings_connected); @@ -280,6 +278,9 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { } else { items.add(item); } + if (++count == MAX_DEVICES) { + break; + } } } mItems.setItems(items.toArray(new Item[items.size()])); 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 df30e20ca582..9daa199ee92a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java @@ -37,6 +37,9 @@ public interface BluetoothController extends CallbackController<Callback>, Dumpa void disconnect(CachedBluetoothDevice device); boolean canConfigBluetooth(); + int getMaxConnectionState(CachedBluetoothDevice device); + int getBondState(CachedBluetoothDevice device); + public interface Callback { void onBluetoothStateChange(boolean enabled); void onBluetoothDevicesChanged(); 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 36d24b370b00..dc4b1158c1cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.policy; import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; import android.content.Context; import android.os.Handler; import android.os.Looper; @@ -33,8 +35,10 @@ import com.android.systemui.Dependency; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; +import java.util.WeakHashMap; public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback, CachedBluetoothDevice.Callback { @@ -44,18 +48,22 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa private final LocalBluetoothManager mLocalBluetoothManager; private final UserManager mUserManager; private final int mCurrentUser; + private final WeakHashMap<CachedBluetoothDevice, ActuallyCachedState> mCachedState = + new WeakHashMap<>(); + private final Handler mBgHandler; private boolean mEnabled; private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED; private CachedBluetoothDevice mLastDevice; - private final H mHandler = new H(); + private final H mHandler = new H(Looper.getMainLooper()); private int mState; public BluetoothControllerImpl(Context context, Looper bgLooper) { mLocalBluetoothManager = Dependency.get(LocalBluetoothManager.class); + mBgHandler = new Handler(bgLooper); if (mLocalBluetoothManager != null) { - mLocalBluetoothManager.getEventManager().setReceiverHandler(new Handler(bgLooper)); + mLocalBluetoothManager.getEventManager().setReceiverHandler(mBgHandler); mLocalBluetoothManager.getEventManager().registerCallback(this); onBluetoothStateChanged( mLocalBluetoothManager.getBluetoothAdapter().getBluetoothState()); @@ -106,6 +114,16 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa } @Override + public int getBondState(CachedBluetoothDevice device) { + return getCachedState(device).mBondState; + } + + @Override + public int getMaxConnectionState(CachedBluetoothDevice device) { + return getCachedState(device).mMaxConnectionState; + } + + @Override public void addCallback(Callback cb) { mHandler.obtainMessage(H.MSG_ADD_CALLBACK, cb).sendToTarget(); mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED); @@ -225,12 +243,14 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa @Override public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) { + mCachedState.remove(cachedDevice); updateConnected(); mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED); } @Override public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { + mCachedState.remove(cachedDevice); updateConnected(); mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED); } @@ -243,11 +263,44 @@ 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); } + private ActuallyCachedState getCachedState(CachedBluetoothDevice device) { + ActuallyCachedState state = mCachedState.get(device); + if (state == null) { + state = new ActuallyCachedState(device, mHandler); + mBgHandler.post(state); + mCachedState.put(device, state); + return state; + } + return state; + } + + private static class ActuallyCachedState implements Runnable { + + private final WeakReference<CachedBluetoothDevice> mDevice; + private final Handler mUiHandler; + private int mBondState = BluetoothDevice.BOND_NONE; + private int mMaxConnectionState = BluetoothProfile.STATE_DISCONNECTED; + + private ActuallyCachedState(CachedBluetoothDevice device, Handler uiHandler) { + mDevice = new WeakReference<>(device); + mUiHandler = uiHandler; + } + + @Override + public void run() { + mBondState = mDevice.get().getBondState(); + mMaxConnectionState = mDevice.get().getMaxConnectionState(); + mUiHandler.removeMessages(H.MSG_PAIRED_DEVICES_CHANGED); + mUiHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED); + } + } + private final class H extends Handler { private final ArrayList<BluetoothController.Callback> mCallbacks = new ArrayList<>(); @@ -256,6 +309,10 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa private static final int MSG_ADD_CALLBACK = 3; private static final int MSG_REMOVE_CALLBACK = 4; + public H(Looper looper) { + super(looper); + } + @Override public void handleMessage(Message msg) { switch (msg.what) { 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 2eb95603cef0..4cc8bcae541a 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 @@ -14,16 +14,21 @@ package com.android.systemui.statusbar.policy; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; +import android.os.Looper; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import android.util.Log; import com.android.settingslib.bluetooth.BluetoothEventManager; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -80,4 +85,56 @@ public class BluetoothControllerImplTest extends SysuiTestCase { BluetoothAdapter.STATE_DISCONNECTED); assertTrue(mBluetoothControllerImpl.isBluetoothConnected()); } + + @Test + public void testDefaultConnectionState() { + CachedBluetoothDevice device = mock(CachedBluetoothDevice.class); + assertEquals(BluetoothDevice.BOND_NONE, mBluetoothControllerImpl.getBondState(device)); + assertEquals(BluetoothProfile.STATE_DISCONNECTED, + mBluetoothControllerImpl.getMaxConnectionState(device)); + } + + @Test + public void testAsyncBondState() throws Exception { + CachedBluetoothDevice device = mock(CachedBluetoothDevice.class); + when(device.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + BluetoothController.Callback callback = mock(BluetoothController.Callback.class); + mBluetoothControllerImpl.addCallback(callback); + + // Grab the main looper, we'll need it later. + TestableLooper mainLooper = new TestableLooper(Looper.getMainLooper()); + + // Trigger the state getting. + assertEquals(BluetoothDevice.BOND_NONE, mBluetoothControllerImpl.getBondState(device)); + + mTestableLooper.processMessages(1); + mainLooper.processAllMessages(); + + assertEquals(BluetoothDevice.BOND_BONDED, mBluetoothControllerImpl.getBondState(device)); + verify(callback).onBluetoothDevicesChanged(); + mainLooper.destroy(); + } + + @Test + public void testAsyncConnectionState() throws Exception { + CachedBluetoothDevice device = mock(CachedBluetoothDevice.class); + when(device.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_CONNECTED); + BluetoothController.Callback callback = mock(BluetoothController.Callback.class); + mBluetoothControllerImpl.addCallback(callback); + + // Grab the main looper, we'll need it later. + TestableLooper mainLooper = new TestableLooper(Looper.getMainLooper()); + + // Trigger the state getting. + assertEquals(BluetoothProfile.STATE_DISCONNECTED, + mBluetoothControllerImpl.getMaxConnectionState(device)); + + mTestableLooper.processMessages(1); + mainLooper.processAllMessages(); + + assertEquals(BluetoothProfile.STATE_CONNECTED, + mBluetoothControllerImpl.getMaxConnectionState(device)); + verify(callback).onBluetoothDevicesChanged(); + mainLooper.destroy(); + } } 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 0ba031908d90..9ec096ad49e0 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 @@ -83,4 +83,14 @@ public class FakeBluetoothController extends BaseLeakChecker<Callback> implement public boolean canConfigBluetooth() { return false; } + + @Override + public int getMaxConnectionState(CachedBluetoothDevice device) { + return 0; + } + + @Override + public int getBondState(CachedBluetoothDevice device) { + return 0; + } } |