Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e547a870 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Show paired devices on Bluetooth device slice card" into rvc-dev

parents 42dcad9b 130629b1
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -123,7 +123,8 @@
                  android:label="@string/settings_label_launcher"
                  android:theme="@style/Theme.Settings.Home"
                  android:taskAffinity="com.android.settings.root"
                  android:launchMode="singleTask">
                  android:launchMode="singleTask"
                  android:configChanges="keyboard|keyboardHidden">
            <intent-filter android:priority="1">
                <action android:name="android.settings.SETTINGS" />
                <category android:name="android.intent.category.DEFAULT" />
+3 −4
Original line number Diff line number Diff line
@@ -69,18 +69,17 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback,

    public BluetoothDeviceUpdater(Context context, DashboardFragment fragment,
            DevicePreferenceCallback devicePreferenceCallback) {
        this(fragment, devicePreferenceCallback, Utils.getLocalBtManager(context));
        this(context, fragment, devicePreferenceCallback, Utils.getLocalBtManager(context));
    }

    @VisibleForTesting
    BluetoothDeviceUpdater(DashboardFragment fragment,
    BluetoothDeviceUpdater(Context context, DashboardFragment fragment,
            DevicePreferenceCallback devicePreferenceCallback, LocalBluetoothManager localManager) {
        mFragment = fragment;
        mDevicePreferenceCallback = devicePreferenceCallback;
        mPreferenceMap = new HashMap<>();
        mLocalManager = localManager;
        mMetricsFeatureProvider = FeatureFactory.getFactory(mFragment.getContext())
                .getMetricsFeatureProvider();
        mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
    }

    /**
+37 −28
Original line number Diff line number Diff line
@@ -39,8 +39,10 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.SubSettings;
import com.android.settings.Utils;
import com.android.settings.bluetooth.AvailableMediaBluetoothDeviceUpdater;
import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
import com.android.settings.bluetooth.BluetoothPairingDetail;
import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.slices.CustomSliceRegistry;
@@ -78,9 +80,15 @@ public class BluetoothDevicesSlice implements CustomSliceable {
    private static final String TAG = "BluetoothDevicesSlice";

    private final Context mContext;
    private final AvailableMediaBluetoothDeviceUpdater mAvailableMediaBtDeviceUpdater;
    private final SavedBluetoothDeviceUpdater mSavedBtDeviceUpdater;

    public BluetoothDevicesSlice(Context context) {
        mContext = context;
        mAvailableMediaBtDeviceUpdater = new AvailableMediaBluetoothDeviceUpdater(mContext,
                null /* fragment */, null /* devicePreferenceCallback */);
        mSavedBtDeviceUpdater = new SavedBluetoothDeviceUpdater(mContext,
                null /* fragment */, null /* devicePreferenceCallback */);
    }

    @Override
@@ -123,10 +131,10 @@ public class BluetoothDevicesSlice implements CustomSliceable {
        final List<ListBuilder.RowBuilder> rows = getBluetoothRowBuilder();

        // Get displayable device count.
        final int deviceCount = Math.min(rows.size(), DEFAULT_EXPANDED_ROW_COUNT);
        final int displayableCount = Math.min(rows.size(), DEFAULT_EXPANDED_ROW_COUNT);

        // According to the displayable device count to add bluetooth device rows.
        for (int i = 0; i < deviceCount; i++) {
        for (int i = 0; i < displayableCount; i++) {
            listBuilder.addRow(rows.get(i));
        }

@@ -148,11 +156,14 @@ public class BluetoothDevicesSlice implements CustomSliceable {

    @Override
    public void onNotifyChange(Intent intent) {
        // Activate available media device.
        final int bluetoothDeviceHashCode = intent.getIntExtra(BLUETOOTH_DEVICE_HASH_CODE, -1);
        for (CachedBluetoothDevice cachedBluetoothDevice : getConnectedBluetoothDevices()) {
            if (cachedBluetoothDevice.hashCode() == bluetoothDeviceHashCode) {
                cachedBluetoothDevice.setActive();
        for (CachedBluetoothDevice device : getPairedBluetoothDevices()) {
            if (device.hashCode() == bluetoothDeviceHashCode) {
                if (device.isConnected()) {
                    device.setActive();
                } else if (!device.isBusy()) {
                    device.connect();
                }
                return;
            }
        }
@@ -164,7 +175,7 @@ public class BluetoothDevicesSlice implements CustomSliceable {
    }

    @VisibleForTesting
    List<CachedBluetoothDevice> getConnectedBluetoothDevices() {
    List<CachedBluetoothDevice> getPairedBluetoothDevices() {
        final List<CachedBluetoothDevice> bluetoothDeviceList = new ArrayList<>();

        // If Bluetooth is disable, skip to get the Bluetooth devices.
@@ -174,19 +185,18 @@ public class BluetoothDevicesSlice implements CustomSliceable {
        }

        // Get the Bluetooth devices from LocalBluetoothManager.
        final LocalBluetoothManager bluetoothManager =
        final LocalBluetoothManager localBtManager =
                com.android.settings.bluetooth.Utils.getLocalBtManager(mContext);
        if (bluetoothManager == null) {
        if (localBtManager == null) {
            Log.i(TAG, "Cannot get Bluetooth devices, Bluetooth is unsupported.");
            return bluetoothDeviceList;
        }
        final Collection<CachedBluetoothDevice> cachedDevices =
                bluetoothManager.getCachedDeviceManager().getCachedDevicesCopy();
                localBtManager.getCachedDeviceManager().getCachedDevicesCopy();

        // Get all connected devices and sort them.
        // Get all paired devices and sort them.
        return cachedDevices.stream()
                .filter(device -> device.getDevice().getBondState() == BluetoothDevice.BOND_BONDED
                        && device.getDevice().isConnected())
                .filter(device -> device.getDevice().getBondState() == BluetoothDevice.BOND_BONDED)
                .sorted(COMPARATOR).collect(Collectors.toList());
    }

@@ -242,21 +252,21 @@ public class BluetoothDevicesSlice implements CustomSliceable {
    private List<ListBuilder.RowBuilder> getBluetoothRowBuilder() {
        // According to Bluetooth devices to create row builders.
        final List<ListBuilder.RowBuilder> bluetoothRows = new ArrayList<>();
        final List<CachedBluetoothDevice> bluetoothDevices = getConnectedBluetoothDevices();
        for (CachedBluetoothDevice bluetoothDevice : bluetoothDevices) {
        for (CachedBluetoothDevice device : getPairedBluetoothDevices()) {
            final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder()
                    .setTitleItem(getBluetoothDeviceIcon(bluetoothDevice), ListBuilder.ICON_IMAGE)
                    .setTitle(bluetoothDevice.getName())
                    .setSubtitle(bluetoothDevice.getConnectionSummary());

            if (bluetoothDevice.isConnectedA2dpDevice()) {
                // For available media devices, the primary action is to activate audio stream and
                // add setting icon to the end to link detail page.
                rowBuilder.setPrimaryAction(buildMediaBluetoothAction(bluetoothDevice));
                rowBuilder.addEndItem(buildBluetoothDetailDeepLinkAction(bluetoothDevice));
                    .setTitleItem(getBluetoothDeviceIcon(device), ListBuilder.ICON_IMAGE)
                    .setTitle(device.getName())
                    .setSubtitle(device.getConnectionSummary());

            if (mAvailableMediaBtDeviceUpdater.isFilterMatched(device)
                    || mSavedBtDeviceUpdater.isFilterMatched(device)) {
                // For all available media devices and previously connected devices, the primary
                // action is to activate or connect, and the end gear icon links to detail page.
                rowBuilder.setPrimaryAction(buildPrimaryBluetoothAction(device));
                rowBuilder.addEndItem(buildBluetoothDetailDeepLinkAction(device));
            } else {
                // For other devices, the primary action is to link detail page.
                rowBuilder.setPrimaryAction(buildBluetoothDetailDeepLinkAction(bluetoothDevice));
                // For other devices, the primary action is to link to detail page.
                rowBuilder.setPrimaryAction(buildBluetoothDetailDeepLinkAction(device));
            }

            bluetoothRows.add(rowBuilder);
@@ -266,8 +276,7 @@ public class BluetoothDevicesSlice implements CustomSliceable {
    }

    @VisibleForTesting
    SliceAction buildMediaBluetoothAction(CachedBluetoothDevice bluetoothDevice) {
        // Send broadcast to activate available media device.
    SliceAction buildPrimaryBluetoothAction(CachedBluetoothDevice bluetoothDevice) {
        final Intent intent = new Intent(getUri().toString())
                .setClass(mContext, SliceBroadcastReceiver.class)
                .putExtra(BLUETOOTH_DEVICE_HASH_CODE, bluetoothDevice.hashCode());
+1 −1
Original line number Diff line number Diff line
@@ -102,7 +102,7 @@ public class BluetoothDeviceUpdaterTest {
        mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
                false, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
        mBluetoothDeviceUpdater =
            new BluetoothDeviceUpdater(mDashboardFragment, mDevicePreferenceCallback,
            new BluetoothDeviceUpdater(mContext, mDashboardFragment, mDevicePreferenceCallback,
                    mLocalManager) {
                @Override
                public boolean isFilterMatched(CachedBluetoothDevice cachedBluetoothDevice) {
+72 −26
Original line number Diff line number Diff line
@@ -23,12 +23,15 @@ import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;

@@ -62,6 +65,7 @@ import java.util.ArrayList;
import java.util.List;

@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowBluetoothAdapter.class)
public class BluetoothDevicesSliceTest {

    private static final String BLUETOOTH_MOCK_ADDRESS = "00:11:00:11:00:11";
@@ -96,6 +100,13 @@ public class BluetoothDevicesSliceTest {

        // Initial Bluetooth device list.
        mBluetoothDeviceList = new ArrayList<>();

        final BluetoothAdapter defaultAdapter = BluetoothAdapter.getDefaultAdapter();
        if (defaultAdapter != null) {
            final ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract(defaultAdapter);
            shadowBluetoothAdapter.setEnabled(true);
            shadowBluetoothAdapter.setState(BluetoothAdapter.STATE_ON);
        }
    }

    @After
@@ -114,7 +125,6 @@ public class BluetoothDevicesSliceTest {
    }

    @Test
    @Config(shadows = ShadowBluetoothAdapter.class)
    public void getSlice_hasBluetoothHardware_shouldHaveBluetoothDevicesTitleAndPairNewDevice() {
        final Slice slice = mBluetoothDevicesSlice.getSlice();

@@ -127,12 +137,9 @@ public class BluetoothDevicesSliceTest {
    }

    @Test
    @Config(shadows = ShadowBluetoothAdapter.class)
    public void getSlice_hasBluetoothDevices_shouldMatchBluetoothMockTitle() {
        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
        adapter.setState(BluetoothAdapter.STATE_ON);
        mockBluetoothDeviceList(1);
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getPairedBluetoothDevices();

        final Slice slice = mBluetoothDevicesSlice.getSlice();

@@ -141,39 +148,43 @@ public class BluetoothDevicesSliceTest {
    }

    @Test
    @Config(shadows = ShadowBluetoothAdapter.class)
    public void getSlice_hasMediaBluetoothDevice_shouldBuildMediaBluetoothAction() {
        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
        adapter.setState(BluetoothAdapter.STATE_ON);
        mockBluetoothDeviceList(1 /* deviceCount */);
        doReturn(true).when(mBluetoothDeviceList.get(0)).isConnectedA2dpDevice();
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
    public void getSlice_hasAvailableMediaDevice_shouldBuildPrimaryBluetoothAction() {
        mockBluetoothDeviceList(1);
        when(mBluetoothDeviceList.get(0).getDevice().isConnected()).thenReturn(true);
        doReturn(true).when(mBluetoothDeviceList.get(0)).isConnectedHearingAidDevice();
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getPairedBluetoothDevices();

        mBluetoothDevicesSlice.getSlice();

        verify(mBluetoothDevicesSlice).buildMediaBluetoothAction(any());
        verify(mBluetoothDevicesSlice).buildPrimaryBluetoothAction(any());
    }

    @Test
    @Config(shadows = ShadowBluetoothAdapter.class)
    public void getSlice_noMediaBluetoothDevice_shouldNotBuildMediaBluetoothAction() {
        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
        adapter.setState(BluetoothAdapter.STATE_ON);
        mockBluetoothDeviceList(1 /* deviceCount */);
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
    public void getSlice_hasPreviouslyConnectedDevice_shouldBuildPrimaryBluetoothAction() {
        mockBluetoothDeviceList(1);
        when(mBluetoothDeviceList.get(0).getDevice().isConnected()).thenReturn(false);
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getPairedBluetoothDevices();

        mBluetoothDevicesSlice.getSlice();

        verify(mBluetoothDevicesSlice, never()).buildMediaBluetoothAction(any());
        verify(mBluetoothDevicesSlice).buildPrimaryBluetoothAction(any());
    }

    @Test
    public void getSlice_hasNonMediaDeviceConnected_shouldNotBuildPrimaryBluetoothAction() {
        mockBluetoothDeviceList(1);
        when(mBluetoothDeviceList.get(0).getDevice().isConnected()).thenReturn(true);
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getPairedBluetoothDevices();

        mBluetoothDevicesSlice.getSlice();

        verify(mBluetoothDevicesSlice, never()).buildPrimaryBluetoothAction(any());
    }

    @Test
    @Config(shadows = ShadowBluetoothAdapter.class)
    public void getSlice_exceedDefaultRowCount_shouldOnlyShowDefaultRows() {
        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
        adapter.setState(BluetoothAdapter.STATE_ON);
        mockBluetoothDeviceList(BluetoothDevicesSlice.DEFAULT_EXPANDED_ROW_COUNT + 1);
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getPairedBluetoothDevices();

        final Slice slice = mBluetoothDevicesSlice.getSlice();

@@ -183,9 +194,10 @@ public class BluetoothDevicesSliceTest {
    }

    @Test
    public void onNotifyChange_mediaDevice_shouldActivateDevice() {
    public void onNotifyChange_connectedDevice_shouldActivateDevice() {
        mockBluetoothDeviceList(1);
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
        doReturn(true).when(mBluetoothDeviceList.get(0)).isConnected();
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getPairedBluetoothDevices();
        final Intent intent = new Intent().putExtra(
                BluetoothDevicesSlice.BLUETOOTH_DEVICE_HASH_CODE,
                mCachedBluetoothDevice.hashCode());
@@ -195,7 +207,41 @@ public class BluetoothDevicesSliceTest {
        verify(mCachedBluetoothDevice).setActive();
    }

    @Test
    public void onNotifyChange_availableDisconnectedDevice_shouldConnectToDevice() {
        mockBluetoothDeviceList(1);
        doReturn(false).when(mBluetoothDeviceList.get(0)).isConnected();
        doReturn(false).when(mBluetoothDeviceList.get(0)).isBusy();
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getPairedBluetoothDevices();
        final Intent intent = new Intent().putExtra(
                BluetoothDevicesSlice.BLUETOOTH_DEVICE_HASH_CODE,
                mCachedBluetoothDevice.hashCode());

        mBluetoothDevicesSlice.onNotifyChange(intent);

        verify(mCachedBluetoothDevice).connect();
    }

    @Test
    public void onNotifyChange_busyDisconnectedDevice_shouldDoNothing() {
        mockBluetoothDeviceList(1);
        doReturn(false).when(mBluetoothDeviceList.get(0)).isConnected();
        doReturn(true).when(mBluetoothDeviceList.get(0)).isBusy();
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getPairedBluetoothDevices();
        final Intent intent = new Intent().putExtra(
                BluetoothDevicesSlice.BLUETOOTH_DEVICE_HASH_CODE,
                mCachedBluetoothDevice.hashCode());

        mBluetoothDevicesSlice.onNotifyChange(intent);

        verify(mCachedBluetoothDevice, never()).setActive();
        verify(mCachedBluetoothDevice, never()).connect();
    }

    private void mockBluetoothDeviceList(int deviceCount) {
        final BluetoothDevice device = mock(BluetoothDevice.class);
        doReturn(BluetoothDevice.BOND_BONDED).when(device).getBondState();
        doReturn(device).when(mCachedBluetoothDevice).getDevice();
        doReturn(BLUETOOTH_MOCK_TITLE).when(mCachedBluetoothDevice).getName();
        doReturn(BLUETOOTH_MOCK_SUMMARY).when(mCachedBluetoothDevice).getConnectionSummary();
        doReturn(BLUETOOTH_MOCK_ADDRESS).when(mCachedBluetoothDevice).getAddress();