Bluetooth: Only show devices when their names are resolved

* Add a developer menu option to allow name-less devices to be shown
  when a Bluetooth developer needs it, but hide it for non-developer
  users.
* Set BluetoothDevicePreference to invisible when CachedBluetoothDevice
  does not have a name besides MAC address and the above developer option
  is false.
* This affects BluetoothPairingDetail and DevicePickerFragment, but does
  not affect BluetoothSettings. BluetoothSettings will show all paired
  devices regardless whether an user friendly name exists.

Bug: 34685932
Test: pair Bluetooth device, send file over Bluetooth, unit tests
Change-Id: Idd7ad4b1671dfdcf3204efb50eddb6dae1065aa5
This commit is contained in:
Jack He
2017-05-31 18:37:28 -07:00
parent 77bd8c3a73
commit 19ba320263
6 changed files with 102 additions and 5 deletions

View File

@@ -209,6 +209,11 @@
android:entries="@array/usb_configuration_titles" android:entries="@array/usb_configuration_titles"
android:entryValues="@array/usb_configuration_values" /> android:entryValues="@array/usb_configuration_values" />
<SwitchPreference
android:key="bluetooth_show_devices_without_names"
android:title="@string/bluetooth_show_devices_without_names"
android:summary="@string/bluetooth_show_devices_without_names_summary"/>
<SwitchPreference <SwitchPreference
android:key="bluetooth_disable_absolute_volume" android:key="bluetooth_disable_absolute_volume"
android:title="@string/bluetooth_disable_absolute_volume" android:title="@string/bluetooth_disable_absolute_volume"

View File

@@ -54,16 +54,17 @@ public final class BluetoothDevicePreference extends GearPreference implements
private final UserManager mUserManager; private final UserManager mUserManager;
private AlertDialog mDisconnectDialog; private AlertDialog mDisconnectDialog;
private String contentDescription = null; private String contentDescription = null;
private DeviceListPreferenceFragment mDeviceListPreferenceFragment;
/* Talk-back descriptions for various BT icons */ /* Talk-back descriptions for various BT icons */
Resources mResources; Resources mResources;
public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) { public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice,
DeviceListPreferenceFragment deviceListPreferenceFragment) {
super(context, null); super(context, null);
mResources = getContext().getResources(); mResources = getContext().getResources();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mDeviceListPreferenceFragment = deviceListPreferenceFragment;
if (sDimAlpha == Integer.MIN_VALUE) { if (sDimAlpha == Integer.MIN_VALUE) {
TypedValue outValue = new TypedValue(); TypedValue outValue = new TypedValue();
@@ -131,6 +132,11 @@ public final class BluetoothDevicePreference extends GearPreference implements
// Used to gray out the item // Used to gray out the item
setEnabled(!mCachedDevice.isBusy()); setEnabled(!mCachedDevice.isBusy());
// Device is only visible in the UI if it has a valid name besides MAC address or when user
// allows showing devices without user-friendly name in developer settings
setVisible(mDeviceListPreferenceFragment.shouldShowDevicesWithoutNames()
|| mCachedDevice.hasHumanReadableName());
// This could affect ordering, so notify that // This could affect ordering, so notify that
notifyHierarchyChanged(); notifyHierarchyChanged();
} }

View File

@@ -140,6 +140,8 @@ public class BluetoothSettings extends DeviceListPreferenceFragment implements I
mBluetoothEnabler.resume(getActivity()); mBluetoothEnabler.resume(getActivity());
} }
super.onStart(); super.onStart();
// Always show paired devices regardless whether user-friendly name exists
mShowDevicesWithoutNames = true;
if (isUiRestricted()) { if (isUiRestricted()) {
getPreferenceScreen().removeAll(); getPreferenceScreen().removeAll();
if (!isUiRestrictedByOnlyAdmin()) { if (!isUiRestrictedByOnlyAdmin()) {

View File

@@ -19,6 +19,7 @@ package com.android.settings.bluetooth;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.os.Bundle; import android.os.Bundle;
import android.os.SystemProperties;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory; import android.support.v7.preference.PreferenceCategory;
@@ -52,6 +53,10 @@ public abstract class DeviceListPreferenceFragment extends
private static final String KEY_BT_SCAN = "bt_scan"; private static final String KEY_BT_SCAN = "bt_scan";
// Copied from DevelopmentSettings.java
private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
"persist.bluetooth.showdeviceswithoutnames";
private BluetoothDeviceFilter.Filter mFilter; private BluetoothDeviceFilter.Filter mFilter;
@VisibleForTesting @VisibleForTesting
@@ -68,6 +73,8 @@ public abstract class DeviceListPreferenceFragment extends
final WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap = final WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
new WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference>(); new WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference>();
boolean mShowDevicesWithoutNames;
DeviceListPreferenceFragment(String restrictedKey) { DeviceListPreferenceFragment(String restrictedKey) {
super(restrictedKey); super(restrictedKey);
mFilter = BluetoothDeviceFilter.ALL_FILTER; mFilter = BluetoothDeviceFilter.ALL_FILTER;
@@ -103,6 +110,8 @@ public abstract class DeviceListPreferenceFragment extends
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
mShowDevicesWithoutNames = SystemProperties.getBoolean(
BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
if (mLocalManager == null || isUiRestricted()) return; if (mLocalManager == null || isUiRestricted()) return;
mLocalManager.setForegroundActivity(getActivity()); mLocalManager.setForegroundActivity(getActivity());
@@ -181,7 +190,7 @@ public abstract class DeviceListPreferenceFragment extends
BluetoothDevicePreference preference = (BluetoothDevicePreference) getCachedPreference(key); BluetoothDevicePreference preference = (BluetoothDevicePreference) getCachedPreference(key);
if (preference == null) { if (preference == null) {
preference = new BluetoothDevicePreference(getPrefContext(), cachedDevice); preference = new BluetoothDevicePreference(getPrefContext(), cachedDevice, this);
preference.setKey(key); preference.setKey(key);
mDeviceListGroup.addPreference(preference); mDeviceListGroup.addPreference(preference);
} else { } else {
@@ -271,4 +280,8 @@ public abstract class DeviceListPreferenceFragment extends
* Return the key of the {@link PreferenceGroup} that contains the bluetooth devices * Return the key of the {@link PreferenceGroup} that contains the bluetooth devices
*/ */
public abstract String getDeviceListKey(); public abstract String getDeviceListKey();
public boolean shouldShowDevicesWithoutNames() {
return mShowDevicesWithoutNames;
}
} }

View File

@@ -199,6 +199,10 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
private static final String FORCE_RESIZABLE_KEY = "force_resizable_activities"; private static final String FORCE_RESIZABLE_KEY = "force_resizable_activities";
private static final String COLOR_TEMPERATURE_KEY = "color_temperature"; private static final String COLOR_TEMPERATURE_KEY = "color_temperature";
private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_KEY =
"bluetooth_show_devices_without_names";
private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
"persist.bluetooth.showdeviceswithoutnames";
private static final String BLUETOOTH_DISABLE_ABSOLUTE_VOLUME_KEY = private static final String BLUETOOTH_DISABLE_ABSOLUTE_VOLUME_KEY =
"bluetooth_disable_absolute_volume"; "bluetooth_disable_absolute_volume";
private static final String BLUETOOTH_DISABLE_ABSOLUTE_VOLUME_PROPERTY = private static final String BLUETOOTH_DISABLE_ABSOLUTE_VOLUME_PROPERTY =
@@ -282,6 +286,7 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
private SwitchPreference mWifiAggressiveHandover; private SwitchPreference mWifiAggressiveHandover;
private SwitchPreference mMobileDataAlwaysOn; private SwitchPreference mMobileDataAlwaysOn;
private SwitchPreference mTetheringHardwareOffload; private SwitchPreference mTetheringHardwareOffload;
private SwitchPreference mBluetoothShowDevicesWithoutNames;
private SwitchPreference mBluetoothDisableAbsVolume; private SwitchPreference mBluetoothDisableAbsVolume;
private SwitchPreference mBluetoothEnableInbandRinging; private SwitchPreference mBluetoothEnableInbandRinging;
@@ -498,6 +503,8 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
mLogpersist = null; mLogpersist = null;
} }
mUsbConfiguration = addListPreference(USB_CONFIGURATION_KEY); mUsbConfiguration = addListPreference(USB_CONFIGURATION_KEY);
mBluetoothShowDevicesWithoutNames =
findAndInitSwitchPref(BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_KEY);
mBluetoothDisableAbsVolume = findAndInitSwitchPref(BLUETOOTH_DISABLE_ABSOLUTE_VOLUME_KEY); mBluetoothDisableAbsVolume = findAndInitSwitchPref(BLUETOOTH_DISABLE_ABSOLUTE_VOLUME_KEY);
mBluetoothEnableInbandRinging = findAndInitSwitchPref(BLUETOOTH_ENABLE_INBAND_RINGING_KEY); mBluetoothEnableInbandRinging = findAndInitSwitchPref(BLUETOOTH_ENABLE_INBAND_RINGING_KEY);
if (!BluetoothHeadset.isInbandRingingSupported(getContext())) { if (!BluetoothHeadset.isInbandRingingSupported(getContext())) {
@@ -838,6 +845,7 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
if (mColorTemperaturePreference != null) { if (mColorTemperaturePreference != null) {
updateColorTemperature(); updateColorTemperature();
} }
updateBluetoothShowDevicesWithoutUserFriendlyNameOptions();
updateBluetoothDisableAbsVolumeOptions(); updateBluetoothDisableAbsVolumeOptions();
updateBluetoothEnableInbandRingingOptions(); updateBluetoothEnableInbandRingingOptions();
updateBluetoothA2dpConfigurationValues(); updateBluetoothA2dpConfigurationValues();
@@ -1468,6 +1476,17 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
mWifiManager.setAllowScansWithTraffic(mWifiAllowScansWithTraffic.isChecked() ? 1 : 0); mWifiManager.setAllowScansWithTraffic(mWifiAllowScansWithTraffic.isChecked() ? 1 : 0);
} }
private void updateBluetoothShowDevicesWithoutUserFriendlyNameOptions() {
updateSwitchPreference(mBluetoothShowDevicesWithoutNames,
SystemProperties.getBoolean(
BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false));
}
private void writeBluetoothShowDevicesWithoutUserFriendlyNameOptions() {
SystemProperties.set(BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY,
mBluetoothShowDevicesWithoutNames.isChecked() ? "true" : "false");
}
private void updateBluetoothDisableAbsVolumeOptions() { private void updateBluetoothDisableAbsVolumeOptions() {
updateSwitchPreference(mBluetoothDisableAbsVolume, updateSwitchPreference(mBluetoothDisableAbsVolume,
SystemProperties.getBoolean(BLUETOOTH_DISABLE_ABSOLUTE_VOLUME_PROPERTY, false)); SystemProperties.getBoolean(BLUETOOTH_DISABLE_ABSOLUTE_VOLUME_PROPERTY, false));
@@ -2532,6 +2551,8 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
writeUSBAudioOptions(); writeUSBAudioOptions();
} else if (preference == mForceResizable) { } else if (preference == mForceResizable) {
writeForceResizableOptions(); writeForceResizableOptions();
} else if (preference == mBluetoothShowDevicesWithoutNames) {
writeBluetoothShowDevicesWithoutUserFriendlyNameOptions();
} else if (preference == mBluetoothDisableAbsVolume) { } else if (preference == mBluetoothDisableAbsVolume) {
writeBluetoothDisableAbsVolumeOptions(); writeBluetoothDisableAbsVolumeOptions();
} else if (preference == mBluetoothEnableInbandRinging) { } else if (preference == mBluetoothEnableInbandRinging) {

View File

@@ -39,6 +39,8 @@ import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers; import org.robolectric.util.ReflectionHelpers;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -52,6 +54,8 @@ public class BluetoothDevicePreferenceTest {
private Context mContext; private Context mContext;
@Mock @Mock
private CachedBluetoothDevice mCachedBluetoothDevice; private CachedBluetoothDevice mCachedBluetoothDevice;
@Mock
private DeviceListPreferenceFragment mDeviceListPreferenceFragment;
private FakeFeatureFactory mFakeFeatureFactory; private FakeFeatureFactory mFakeFeatureFactory;
private MetricsFeatureProvider mMetricsFeatureProvider; private MetricsFeatureProvider mMetricsFeatureProvider;
@@ -64,7 +68,8 @@ public class BluetoothDevicePreferenceTest {
FakeFeatureFactory.setupForTest(mContext); FakeFeatureFactory.setupForTest(mContext);
mFakeFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); mFakeFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
mMetricsFeatureProvider = mFakeFeatureFactory.getMetricsFeatureProvider(); mMetricsFeatureProvider = mFakeFeatureFactory.getMetricsFeatureProvider();
mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice); mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
mDeviceListPreferenceFragment);
} }
@Test @Test
@@ -151,4 +156,49 @@ public class BluetoothDevicePreferenceTest {
assertThat(mPreference.getIcon()).isEqualTo( assertThat(mPreference.getIcon()).isEqualTo(
mContext.getDrawable(R.drawable.ic_settings_print)); mContext.getDrawable(R.drawable.ic_settings_print));
} }
@Test
public void testVisible_notVisibleThenVisible() {
when(mDeviceListPreferenceFragment.shouldShowDevicesWithoutNames()).thenReturn(false);
final boolean[] humanReadableName = {false};
doAnswer(invocation -> humanReadableName[0]).when(mCachedBluetoothDevice)
.hasHumanReadableName();
BluetoothDevicePreference preference =
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
mDeviceListPreferenceFragment);
assertThat(preference.isVisible()).isFalse();
humanReadableName[0] = true;
preference.onDeviceAttributesChanged();
assertThat(preference.isVisible()).isTrue();
}
@Test
public void testVisible_visibleThenNotVisible() {
when(mDeviceListPreferenceFragment.shouldShowDevicesWithoutNames()).thenReturn(false);
final boolean[] humanReadableName = {true};
doAnswer(invocation -> humanReadableName[0]).when(mCachedBluetoothDevice)
.hasHumanReadableName();
BluetoothDevicePreference preference =
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
mDeviceListPreferenceFragment);
assertThat(preference.isVisible()).isTrue();
humanReadableName[0] = false;
preference.onDeviceAttributesChanged();
assertThat(preference.isVisible()).isFalse();
}
@Test
public void testVisible_alwaysVisibleWhenEnabled() {
when(mDeviceListPreferenceFragment.shouldShowDevicesWithoutNames()).thenReturn(true);
final boolean[] humanReadableName = {true};
doAnswer(invocation -> humanReadableName[0]).when(mCachedBluetoothDevice)
.hasHumanReadableName();
BluetoothDevicePreference preference =
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
mDeviceListPreferenceFragment);
assertThat(preference.isVisible()).isTrue();
humanReadableName[0] = false;
preference.onDeviceAttributesChanged();
assertThat(preference.isVisible()).isTrue();
}
} }