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

Commit 5b260b0b authored by Jason Chiu's avatar Jason Chiu Committed by Android (Google) Code Review
Browse files

Merge "Refine contextual Bluetooth card behavior when Bluetooth is off" into rvc-dev

parents 136c1010 d304c20a
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
<!--
  ~ Copyright (C) 2020 The Android Open Source Project
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24"
        android:viewportHeight="24"
        android:tint="?android:attr/colorControlNormal">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M13,5.83l1.88,1.88 -1.6,1.6 1.41,1.41 3.02,-3.02L12,2h-1v5.03l2,2v-3.2zM5.41,4L4,5.41 10.59,12 5,17.59 6.41,19 11,14.41V22h1l4.29,-4.29 2.3,2.29L20,18.59 5.41,4zM13,18.17v-3.76l1.88,1.88L13,18.17z"/>
</vector>
 No newline at end of file
+5 −0
Original line number Diff line number Diff line
@@ -208,6 +208,11 @@
    <!-- Item in bluetooth settings screen, used to show the list of Files received via Bluetooth [CHAR LIMIT=NONE] -->
    <string name="bluetooth_show_files_received_via_bluetooth">Files received via Bluetooth</string>
    <!-- Title for contextual Bluetooth devices card when Bluetooth is off [CHAR LIMIT=NONE]-->
    <string name="bluetooth_devices_card_off_title">Bluetooth is off</string>
    <!-- Description about contextual Bluetooth devices card when Bluetooth is off [CHAR LIMIT=NONE]-->
    <string name="bluetooth_devices_card_off_summary">Tap to turn it on</string>
    <!-- Strings for BluetoothDevicePicker [CHAR LIMIT=40]-->
    <string name="device_picker">Choose Bluetooth device</string>
+78 −34
Original line number Diff line number Diff line
@@ -22,8 +22,6 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
@@ -64,13 +62,12 @@ public class BluetoothDevicesSlice implements CustomSliceable {
    @VisibleForTesting
    static final String BLUETOOTH_DEVICE_HASH_CODE = "bluetooth_device_hash_code";

    /**
     * Add the "Pair new device" in the end of slice, when the number of Bluetooth devices is less
     * than {@link #DEFAULT_EXPANDED_ROW_COUNT}.
     */
    @VisibleForTesting
    static final int DEFAULT_EXPANDED_ROW_COUNT = 2;

    @VisibleForTesting
    static final String EXTRA_ENABLE_BLUETOOTH = "enable_bluetooth";

    /**
     * Refer {@link com.android.settings.bluetooth.BluetoothDevicePreference#compareTo} to sort the
     * Bluetooth devices by {@link CachedBluetoothDevice}.
@@ -79,6 +76,10 @@ public class BluetoothDevicesSlice implements CustomSliceable {

    private static final String TAG = "BluetoothDevicesSlice";

    // For seamless UI transition after tapping this slice to enable Bluetooth, this flag is to
    // update the layout promptly since it takes time for Bluetooth to reflect the enabling state.
    private static boolean sBluetoothEnabling;

    private final Context mContext;
    private final AvailableMediaBluetoothDeviceUpdater mAvailableMediaBtDeviceUpdater;
    private final SavedBluetoothDeviceUpdater mSavedBtDeviceUpdater;
@@ -107,33 +108,27 @@ public class BluetoothDevicesSlice implements CustomSliceable {
        // Reload theme for switching dark mode on/off
        mContext.getTheme().applyStyle(R.style.Theme_Settings_Home, true /* force */);

        final IconCompat icon = IconCompat.createWithResource(mContext,
                com.android.internal.R.drawable.ic_settings_bluetooth);
        final CharSequence title = mContext.getText(R.string.bluetooth_devices);
        final PendingIntent primaryActionIntent = PendingIntent.getActivity(mContext, 0,
                getIntent(), 0);
        final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryActionIntent, icon,
                ListBuilder.ICON_IMAGE, title);
        final SliceAction pairNewDeviceAction = getPairNewDeviceAction();
        final ListBuilder listBuilder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
                .setAccentColor(COLOR_NOT_TINTED)
                .addAction(pairNewDeviceAction)
                .setHeader(new ListBuilder.HeaderBuilder()
                        .setTitle(title)
                        .setPrimaryAction(primarySliceAction));
                .setAccentColor(COLOR_NOT_TINTED);

        // Only show a header when Bluetooth is off.
        if (!isBluetoothEnabled(btAdapter)) {
            return listBuilder.build();
        // Only show this header when Bluetooth is off and not turning on.
        if (!isBluetoothEnabled(btAdapter) && !sBluetoothEnabling) {
            return listBuilder.addRow(getBluetoothOffHeader()).build();
        }

        // Get row builders by Bluetooth devices.
        final List<ListBuilder.RowBuilder> rows = getBluetoothRowBuilder();
        // Always reset this flag when showing the layout of Bluetooth on
        sBluetoothEnabling = false;

        // Add the header of Bluetooth on
        listBuilder.addRow(getBluetoothOnHeader());

        // Get displayable device count.
        // Get row builders of Bluetooth devices.
        final List<ListBuilder.RowBuilder> rows = getBluetoothRowBuilders();

        // Determine the displayable row count.
        final int displayableCount = Math.min(rows.size(), DEFAULT_EXPANDED_ROW_COUNT);

        // According to the displayable device count to add bluetooth device rows.
        // Add device rows up to the count.
        for (int i = 0; i < displayableCount; i++) {
            listBuilder.addRow(rows.get(i));
        }
@@ -156,6 +151,17 @@ public class BluetoothDevicesSlice implements CustomSliceable {

    @Override
    public void onNotifyChange(Intent intent) {
        final boolean enableBluetooth = intent.getBooleanExtra(EXTRA_ENABLE_BLUETOOTH, false);
        if (enableBluetooth) {
            final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
            if (!isBluetoothEnabled(btAdapter)) {
                sBluetoothEnabling = true;
                btAdapter.enable();
                mContext.getContentResolver().notifyChange(getUri(), null);
            }
            return;
        }

        final int bluetoothDeviceHashCode = intent.getIntExtra(BLUETOOTH_DEVICE_HASH_CODE, -1);
        for (CachedBluetoothDevice device : getPairedBluetoothDevices()) {
            if (device.hashCode() == bluetoothDeviceHashCode) {
@@ -224,7 +230,7 @@ public class BluetoothDevicesSlice implements CustomSliceable {
                BluetoothUtils.getBtRainbowDrawableWithDescription(mContext, device);
        final Drawable drawable = pair.first;

        // Use default bluetooth icon if can't get icon.
        // Use default Bluetooth icon if we can't get one.
        if (drawable == null) {
            return IconCompat.createWithResource(mContext,
                    com.android.internal.R.drawable.ic_settings_bluetooth);
@@ -233,11 +239,49 @@ public class BluetoothDevicesSlice implements CustomSliceable {
        return Utils.createIconWithDrawable(drawable);
    }

    private ListBuilder.RowBuilder getBluetoothOffHeader() {
        final Drawable drawable = mContext.getDrawable(R.drawable.ic_bluetooth_disabled);
        final int tint = Utils.getDisabled(mContext, Utils.getColorAttrDefaultColor(mContext,
                android.R.attr.colorControlNormal));
        drawable.setTint(tint);
        final IconCompat icon = Utils.createIconWithDrawable(drawable);
        final CharSequence title = mContext.getText(R.string.bluetooth_devices_card_off_title);
        final CharSequence summary = mContext.getText(R.string.bluetooth_devices_card_off_summary);
        final Intent intent = new Intent(getUri().toString())
                .setClass(mContext, SliceBroadcastReceiver.class)
                .putExtra(EXTRA_ENABLE_BLUETOOTH, true);
        final SliceAction action = SliceAction.create(PendingIntent.getBroadcast(mContext,
                0 /* requestCode */, intent, 0 /* flags */), icon, ListBuilder.ICON_IMAGE, title);

        return new ListBuilder.RowBuilder()
                .setTitleItem(icon, ListBuilder.ICON_IMAGE)
                .setTitle(title)
                .setSubtitle(summary)
                .setPrimaryAction(action);
    }

    private ListBuilder.RowBuilder getBluetoothOnHeader() {
        final Drawable drawable = mContext.getDrawable(
                com.android.internal.R.drawable.ic_settings_bluetooth);
        drawable.setTint(Utils.getColorAccentDefaultColor(mContext));
        final IconCompat icon = Utils.createIconWithDrawable(drawable);
        final CharSequence title = mContext.getText(R.string.bluetooth_devices);
        final PendingIntent primaryActionIntent = PendingIntent.getActivity(mContext,
                0 /* requestCode */, getIntent(), 0 /* flags */);
        final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryActionIntent, icon,
                ListBuilder.ICON_IMAGE, title);

        return new ListBuilder.RowBuilder()
                .setTitleItem(icon, ListBuilder.ICON_IMAGE)
                .setTitle(title)
                .setPrimaryAction(primarySliceAction)
                .addEndItem(getPairNewDeviceAction());
    }

    private SliceAction getPairNewDeviceAction() {
        final Drawable d = mContext.getDrawable(R.drawable.ic_add_24dp);
        d.setColorFilter(new PorterDuffColorFilter(Utils.getColorAccentDefaultColor(mContext),
                PorterDuff.Mode.SRC_IN));
        final IconCompat icon = Utils.createIconWithDrawable(d);
        final Drawable drawable = mContext.getDrawable(R.drawable.ic_add_24dp);
        drawable.setTint(Utils.getColorAccentDefaultColor(mContext));
        final IconCompat icon = Utils.createIconWithDrawable(drawable);
        final String title = mContext.getString(R.string.bluetooth_pairing_pref_title);
        final Intent intent = new SubSettingLauncher(mContext)
                .setDestination(BluetoothPairingDetail.class.getName())
@@ -249,9 +293,9 @@ public class BluetoothDevicesSlice implements CustomSliceable {
        return SliceAction.createDeeplink(pi, icon, ListBuilder.ICON_IMAGE, title);
    }

    private List<ListBuilder.RowBuilder> getBluetoothRowBuilder() {
        // According to Bluetooth devices to create row builders.
    private List<ListBuilder.RowBuilder> getBluetoothRowBuilders() {
        final List<ListBuilder.RowBuilder> bluetoothRows = new ArrayList<>();
        // Create row builders based on paired devices.
        for (CachedBluetoothDevice device : getPairedBluetoothDevices()) {
            final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder()
                    .setTitleItem(getBluetoothDeviceIcon(device), ListBuilder.ICON_IMAGE)
+41 −7
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.settings.homepage.contextualcards.slices;
import static android.app.slice.Slice.HINT_LIST_ITEM;
import static android.app.slice.SliceItem.FORMAT_SLICE;

import static com.android.settings.homepage.contextualcards.slices.BluetoothDevicesSlice.EXTRA_ENABLE_BLUETOOTH;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
@@ -75,6 +77,7 @@ public class BluetoothDevicesSliceTest {
    @Mock
    private CachedBluetoothDevice mCachedBluetoothDevice;

    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
    private List<CachedBluetoothDevice> mBluetoothDeviceList;
    private BluetoothDevicesSlice mBluetoothDevicesSlice;
    private Context mContext;
@@ -103,9 +106,9 @@ public class BluetoothDevicesSliceTest {

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

@@ -125,7 +128,38 @@ public class BluetoothDevicesSliceTest {
    }

    @Test
    public void getSlice_hasBluetoothHardware_shouldHaveBluetoothDevicesTitleAndPairNewDevice() {
    public void getSlice_bluetoothOff_shouldHaveBluetoothOffTitleAndSummary() {
        mShadowBluetoothAdapter.setEnabled(false);
        mShadowBluetoothAdapter.setState(BluetoothAdapter.STATE_OFF);

        final Slice slice = mBluetoothDevicesSlice.getSlice();

        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
        assertThat(metadata.getTitle()).isEqualTo(mContext.getString(
                R.string.bluetooth_devices_card_off_title));
        assertThat(metadata.getSummary()).isEqualTo(mContext.getString(
                R.string.bluetooth_devices_card_off_summary));
    }

    @Test
    public void getSlice_bluetoothTurningOn_shouldHaveBluetoothDevicesTitleAndPairNewDevice() {
        mShadowBluetoothAdapter.setEnabled(false);
        mShadowBluetoothAdapter.setState(BluetoothAdapter.STATE_OFF);
        final Intent intent = new Intent().putExtra(EXTRA_ENABLE_BLUETOOTH, true);

        mBluetoothDevicesSlice.onNotifyChange(intent);
        final Slice slice = mBluetoothDevicesSlice.getSlice();

        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
        assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.bluetooth_devices));

        final List<SliceItem> sliceItems = slice.getItems();
        SliceTester.assertAnySliceItemContainsTitle(sliceItems, mContext.getString(
                R.string.bluetooth_pairing_pref_title));
    }

    @Test
    public void getSlice_bluetoothOn_shouldHaveBluetoothDevicesTitleAndPairNewDevice() {
        final Slice slice = mBluetoothDevicesSlice.getSlice();

        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
@@ -182,15 +216,15 @@ public class BluetoothDevicesSliceTest {
    }

    @Test
    public void getSlice_exceedDefaultRowCount_shouldOnlyShowDefaultRows() {
        mockBluetoothDeviceList(BluetoothDevicesSlice.DEFAULT_EXPANDED_ROW_COUNT + 1);
    public void getSlice_exceedDefaultRowCount_shouldOnlyShowHeaderAndDefaultRowCount() {
        mockBluetoothDeviceList(BluetoothDevicesSlice.DEFAULT_EXPANDED_ROW_COUNT + 2);
        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getPairedBluetoothDevices();

        final Slice slice = mBluetoothDevicesSlice.getSlice();

        // Get the number of RowBuilders from Slice.
        final int rows = SliceQuery.findAll(slice, FORMAT_SLICE, HINT_LIST_ITEM, null).size();
        assertThat(rows).isEqualTo(BluetoothDevicesSlice.DEFAULT_EXPANDED_ROW_COUNT);
        assertThat(rows).isEqualTo(BluetoothDevicesSlice.DEFAULT_EXPANDED_ROW_COUNT + 1);
    }

    @Test