Add a new Bluetooth device details page

Bug: 35877479
Test: make RunSettingsRoboTests

The existing behavior is to bring up a dialog with Bluetooth device
details with checkboxes for each supported profile. This adds a new page
that serves the same purpose with a switch for each profile and a footer
containing the MAC address.

Whether to use the new page or old dialog is controlled by a flag
accessible via BluetoothFeatureProvider.

Change-Id: I026c363d4cd33932a84017a67cbef51c258bad10
This commit is contained in:
Antony Sargent
2017-05-04 15:06:32 -07:00
parent 8d9177a06e
commit 04a3b2199e
19 changed files with 1823 additions and 63 deletions

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2017 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.
*/
package com.android.settings.bluetooth;
import android.content.Context;
import android.support.v14.preference.PreferenceFragment;
import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
import android.widget.Button;
import com.android.settings.R;
import com.android.settings.applications.LayoutPreference;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.core.lifecycle.Lifecycle;
/**
* This class adds two buttons: one to connect/disconnect from a device (depending on the current
* connected state), and one to "forget" (ie unpair) the device.
*/
public class BluetoothDetailsButtonsController extends BluetoothDetailsController {
private static final String KEY_ACTION_BUTTONS = "action_buttons";
private boolean mIsConnected;
private LayoutPreference mActionButtons;
public BluetoothDetailsButtonsController(Context context, PreferenceFragment fragment,
CachedBluetoothDevice device, Lifecycle lifecycle) {
super(context, fragment, device, lifecycle);
mIsConnected = device.isConnected();
}
@Override
protected void init(PreferenceScreen screen) {
mActionButtons = (LayoutPreference) screen.findPreference(getPreferenceKey());
Button rightButton = (Button) mActionButtons.findViewById(R.id.right_button);
rightButton.setText(R.string.forget);
rightButton.setOnClickListener((view) -> {
mCachedDevice.unpair();
mFragment.getActivity().finish();
});
}
@Override
protected void refresh() {
Button leftButton = (Button) mActionButtons.findViewById(R.id.left_button);
leftButton.setEnabled(!mCachedDevice.isBusy());
boolean notInitialized = TextUtils.isEmpty(leftButton.getText());
boolean previouslyConnected = mIsConnected;
mIsConnected = mCachedDevice.isConnected();
if (mIsConnected) {
if (notInitialized || !previouslyConnected) {
leftButton.setText(R.string.bluetooth_device_context_disconnect);
leftButton.setOnClickListener((view) -> {
mCachedDevice.disconnect();
});
}
} else {
if (notInitialized || previouslyConnected) {
leftButton.setText(R.string.bluetooth_device_context_connect);
leftButton.setOnClickListener((view) -> {
mCachedDevice.connect(true);
});
}
}
}
@Override
public String getPreferenceKey() {
return KEY_ACTION_BUTTONS;
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2017 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.
*/
package com.android.settings.bluetooth;
import android.content.Context;
import android.support.v14.preference.PreferenceFragment;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.core.PreferenceController;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
/**
* This class provides common lifecycle and bluetooth device event registration for Bluetooth device
* details controllers.
*/
public abstract class BluetoothDetailsController extends PreferenceController
implements CachedBluetoothDevice.Callback, LifecycleObserver, OnPause, OnResume {
protected final Context mContext;
protected final PreferenceFragment mFragment;
protected final CachedBluetoothDevice mCachedDevice;
public BluetoothDetailsController(Context context, PreferenceFragment fragment,
CachedBluetoothDevice device, Lifecycle lifecycle) {
super(context);
mContext = context;
mFragment = fragment;
mCachedDevice = device;
lifecycle.addObserver(this);
}
@Override
public void onPause() {
mCachedDevice.unregisterCallback(this);
}
@Override
public void onResume() {
mCachedDevice.registerCallback(this);
refresh();
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public void onDeviceAttributesChanged() {
refresh();
}
@Override
public final void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
init(screen);
}
/**
* This is a method to do one-time initialization when the screen is first created, such as
* adding preferences.
* @param screen the screen where this controller's preferences should be added
*/
protected abstract void init(PreferenceScreen screen);
/**
* This method is called when something about the bluetooth device has changed, and this object
* should update the preferences it manages based on the new state.
*/
protected abstract void refresh();
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2017 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.
*/
package com.android.settings.bluetooth;
import android.content.Context;
import android.support.v14.preference.PreferenceFragment;
import android.support.v7.preference.PreferenceScreen;
import android.util.Pair;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.core.lifecycle.Lifecycle;
/**
* This class adds a header with device name and status (connected/disconnected, etc.).
*/
public class BluetoothDetailsHeaderController extends BluetoothDetailsController {
private EntityHeaderController mHeaderController;
public BluetoothDetailsHeaderController(Context context, PreferenceFragment fragment,
CachedBluetoothDevice device, Lifecycle lifecycle) {
super(context, fragment, device, lifecycle);
}
@Override
protected void init(PreferenceScreen screen) {
mHeaderController = EntityHeaderController.newInstance(mFragment.getActivity(), mFragment,
null);
LayoutPreference pref = mHeaderController.done(mFragment.getActivity(), mContext);
screen.addPreference(pref);
}
protected void setHeaderProperties() {
Pair<Integer, String> pair = Utils.getBtClassDrawableWithDescription
(mContext.getResources(), mCachedDevice);
int summaryResourceId = mCachedDevice.getConnectionSummary();
mHeaderController.setLabel(mCachedDevice.getName());
mHeaderController.setIcon(mContext.getDrawable(pair.first));
mHeaderController.setIconContentDescription(pair.second);
mHeaderController.setSummary(
summaryResourceId > 0 ? mContext.getString(summaryResourceId) : null);
}
@Override
protected void refresh() {
setHeaderProperties();
mHeaderController.done(mFragment.getActivity(), false);
}
@Override
public String getPreferenceKey() {
return EntityHeaderController.PREF_KEY_APP_HEADER;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2017 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.
*/
package com.android.settings.bluetooth;
import android.content.Context;
import android.support.v14.preference.PreferenceFragment;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.FooterPreferenceMixin;
/**
* This class adds the device MAC address to a footer.
*/
public class BluetoothDetailsMacAddressController extends BluetoothDetailsController {
FooterPreferenceMixin mFooterPreferenceMixin;
FooterPreference mFooterPreference;
public BluetoothDetailsMacAddressController(Context context,
PreferenceFragment fragment,
CachedBluetoothDevice device,
Lifecycle lifecycle) {
super(context, fragment, device, lifecycle);
mFooterPreferenceMixin = new FooterPreferenceMixin(fragment, lifecycle);
}
@Override
protected void init(PreferenceScreen screen) {
mFooterPreference = mFooterPreferenceMixin.createFooterPreference();
mFooterPreference.setTitle(mContext.getString(
R.string.bluetooth_device_mac_address, mCachedDevice.getDevice().getAddress()));
}
@Override
protected void refresh() {}
@Override
public String getPreferenceKey() {
return mFooterPreference.getKey();
}
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright (C) 2017 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.
*/
package com.android.settings.bluetooth;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
import android.support.v14.preference.PreferenceFragment;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.bluetooth.MapProfile;
import com.android.settingslib.bluetooth.PanProfile;
import com.android.settingslib.bluetooth.PbapServerProfile;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.List;
/**
* This class adds switches for toggling the individual profiles that a Bluetooth device
* supports, such as "Phone audio", "Media audio", "Contact sharing", etc.
*/
public class BluetoothDetailsProfilesController extends BluetoothDetailsController
implements Preference.OnPreferenceClickListener {
private static final String KEY_PROFILES_GROUP = "bluetooth_profiles";
@VisibleForTesting
static final String HIGH_QUALITY_AUDIO_PREF_TAG = "A2dpProfileHighQualityAudio";
private LocalBluetoothManager mManager;
private LocalBluetoothProfileManager mProfileManager;
private CachedBluetoothDevice mCachedDevice;
private PreferenceCategory mProfilesContainer;
public BluetoothDetailsProfilesController(Context context, PreferenceFragment fragment,
LocalBluetoothManager manager, CachedBluetoothDevice device, Lifecycle lifecycle) {
super(context, fragment, device, lifecycle);
mManager = manager;
mProfileManager = mManager.getProfileManager();
mCachedDevice = device;
lifecycle.addObserver(this);
}
@Override
protected void init(PreferenceScreen screen) {
mProfilesContainer = (PreferenceCategory)screen.findPreference(getPreferenceKey());
// Call refresh here even though it will get called later in onResume, to avoid the
// list of switches appearing to "pop" into the page.
refresh();
}
/**
* Creates a switch preference for the particular profile.
*
* @param context The context to use when creating the SwitchPreference
* @param profile The profile for which the preference controls.
* @return A preference that allows the user to choose whether this profile
* will be connected to.
*/
private SwitchPreference createProfilePreference(Context context,
LocalBluetoothProfile profile) {
SwitchPreference pref = new SwitchPreference(context);
pref.setKey(profile.toString());
pref.setTitle(profile.getNameResource(mCachedDevice.getDevice()));
pref.setOnPreferenceClickListener(this);
return pref;
}
/**
* Refreshes the state for an existing SwitchPreference for a profile.
*/
private void refreshProfilePreference(SwitchPreference profilePref,
LocalBluetoothProfile profile) {
BluetoothDevice device = mCachedDevice.getDevice();
profilePref.setEnabled(!mCachedDevice.isBusy());
if (profile instanceof MapProfile) {
profilePref.setChecked(mCachedDevice.getMessagePermissionChoice()
== CachedBluetoothDevice.ACCESS_ALLOWED);
} else if (profile instanceof PbapServerProfile) {
profilePref.setChecked(mCachedDevice.getPhonebookPermissionChoice()
== CachedBluetoothDevice.ACCESS_ALLOWED);
} else if (profile instanceof PanProfile) {
profilePref.setChecked(profile.getConnectionStatus(device) ==
BluetoothProfile.STATE_CONNECTED);
} else {
profilePref.setChecked(profile.isPreferred(device));
}
if (profile instanceof A2dpProfile) {
A2dpProfile a2dp = (A2dpProfile) profile;
SwitchPreference highQualityPref = (SwitchPreference) mProfilesContainer.findPreference(
HIGH_QUALITY_AUDIO_PREF_TAG);
if (highQualityPref != null) {
if (a2dp.isPreferred(device) && a2dp.supportsHighQualityAudio(device)) {
highQualityPref.setVisible(true);
highQualityPref.setTitle(a2dp.getHighQualityAudioOptionLabel(device));
highQualityPref.setChecked(a2dp.isHighQualityAudioEnabled(device));
highQualityPref.setEnabled(!mCachedDevice.isBusy());
} else {
highQualityPref.setVisible(false);
}
}
}
}
/**
* Helper method to enable a profile for a device.
*/
private void enableProfile(LocalBluetoothProfile profile, BluetoothDevice device,
SwitchPreference profilePref) {
if (profile instanceof PbapServerProfile) {
mCachedDevice.setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
// We don't need to do the additional steps below for this profile.
return;
}
if (profile instanceof MapProfile) {
mCachedDevice.setMessagePermissionChoice(BluetoothDevice.ACCESS_ALLOWED);
}
profile.setPreferred(device, true);
mCachedDevice.connectProfile(profile);
}
/**
* Helper method to disable a profile for a device
*/
private void disableProfile(LocalBluetoothProfile profile, BluetoothDevice device,
SwitchPreference profilePref) {
if (profile instanceof PbapServerProfile) {
mCachedDevice.setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_REJECTED);
// We don't need to do the additional steps below for this profile.
return;
}
mCachedDevice.disconnect(profile);
profile.setPreferred(device, false);
if (profile instanceof MapProfile) {
mCachedDevice.setMessagePermissionChoice(BluetoothDevice.ACCESS_REJECTED);
}
}
/**
* When the pref for a bluetooth profile is clicked on, we want to toggle the enabled/disabled
* state for that profile.
*/
@Override
public boolean onPreferenceClick(Preference preference) {
LocalBluetoothProfile profile = mProfileManager.getProfileByName(preference.getKey());
if (profile == null) {
// It might be the PbapServerProfile, which is not stored by name.
PbapServerProfile psp = mManager.getProfileManager().getPbapProfile();
if (TextUtils.equals(preference.getKey(), psp.toString())) {
profile = psp;
} else {
return false;
}
}
SwitchPreference profilePref = (SwitchPreference) preference;
BluetoothDevice device = mCachedDevice.getDevice();
if (profilePref.isChecked()) {
enableProfile(profile, device, profilePref);
} else {
disableProfile(profile, device, profilePref);
}
refreshProfilePreference(profilePref, profile);
return true;
}
/**
* Helper to get the list of connectable and special profiles.
*/
private List<LocalBluetoothProfile> getProfiles() {
List<LocalBluetoothProfile> result = mCachedDevice.getConnectableProfiles();
final int pbapPermission = mCachedDevice.getPhonebookPermissionChoice();
// Only provide PBAP cabability if the client device has requested PBAP.
if (pbapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) {
final PbapServerProfile psp = mManager.getProfileManager().getPbapProfile();
result.add(psp);
}
final MapProfile mapProfile = mManager.getProfileManager().getMapProfile();
final int mapPermission = mCachedDevice.getMessagePermissionChoice();
if (mapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) {
result.add(mapProfile);
}
return result;
}
/**
* This is a helper method to be called after adding a Preference for a profile. If that
* profile happened to be A2dp and the device supports high quality audio, it will add a
* separate preference for controlling whether to actually use high quality audio.
*
* @param profile the profile just added
*/
private void maybeAddHighQualityAudioPref(LocalBluetoothProfile profile) {
if (!(profile instanceof A2dpProfile)) {
return;
}
BluetoothDevice device = mCachedDevice.getDevice();
A2dpProfile a2dp = (A2dpProfile) profile;
if (a2dp.supportsHighQualityAudio(device)) {
SwitchPreference highQualityAudioPref = new SwitchPreference(
mProfilesContainer.getContext());
highQualityAudioPref.setKey(HIGH_QUALITY_AUDIO_PREF_TAG);
highQualityAudioPref.setVisible(false);
highQualityAudioPref.setOnPreferenceClickListener(clickedPref -> {
boolean enable = ((SwitchPreference) clickedPref).isChecked();
a2dp.setHighQualityAudioEnabled(mCachedDevice.getDevice(), enable);
return true;
});
mProfilesContainer.addPreference(highQualityAudioPref);
}
}
/**
* Refreshes the state of the switches for all profiles, possibly adding or removing switches as
* needed.
*/
@Override
protected void refresh() {
for (LocalBluetoothProfile profile : getProfiles()) {
SwitchPreference pref = (SwitchPreference) mProfilesContainer.findPreference(
profile.toString());
if (pref == null) {
pref = createProfilePreference(mProfilesContainer.getContext(), profile);
mProfilesContainer.addPreference(pref);
maybeAddHighQualityAudioPref(profile);
}
refreshProfilePreference(pref, profile);
}
for (LocalBluetoothProfile removedProfile : mCachedDevice.getRemovedProfiles()) {
SwitchPreference pref = (SwitchPreference) mProfilesContainer.findPreference(
removedProfile.toString());
if (pref != null) {
mProfilesContainer.removePreference(pref);
}
}
}
@Override
public String getPreferenceKey() {
return KEY_PROFILES_GROUP;
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2017 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.
*/
package com.android.settings.bluetooth;
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.PreferenceController;
import com.android.settings.dashboard.RestrictedDashboardFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.ArrayList;
import java.util.List;
public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment {
public static final String KEY_DEVICE_ADDRESS = "device_address";
private static final String TAG = "BTDeviceDetailsFrg";
private String mDeviceAddress;
public BluetoothDeviceDetailsFragment() {
super(DISALLOW_CONFIG_BLUETOOTH);
}
@Override
public void onAttach(Context context) {
mDeviceAddress = getArguments().getString(KEY_DEVICE_ADDRESS);
super.onAttach(context);
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.BLUETOOTH_DEVICE_DETAILS;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.bluetooth_device_details_fragment;
}
@Override
protected List<PreferenceController> getPreferenceControllers(Context context) {
ArrayList<PreferenceController> controllers = new ArrayList<>();
LocalBluetoothManager manager = Utils.getLocalBtManager(context);
BluetoothDevice remoteDevice = manager.getBluetoothAdapter().getRemoteDevice(
mDeviceAddress);
CachedBluetoothDevice device = manager.getCachedDeviceManager().findDevice(remoteDevice);
if (device != null) {
Lifecycle lifecycle = getLifecycle();
controllers.add(new BluetoothDetailsHeaderController(context, this, device, lifecycle));
controllers.add(new BluetoothDetailsButtonsController(context, this, device,
lifecycle));
controllers.add(new BluetoothDetailsProfilesController(context, this, manager, device,
lifecycle));
controllers.add(new BluetoothDetailsMacAddressController(context, this, device,
lifecycle));
}
return controllers;
}
}

View File

@@ -17,7 +17,6 @@
package com.android.settings.bluetooth;
import android.app.AlertDialog;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.DialogInterface;
@@ -27,7 +26,6 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.util.TypedValue;
import android.widget.ImageView;
@@ -38,10 +36,6 @@ import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.GearPreference;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import java.util.List;
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
@@ -63,18 +57,11 @@ public final class BluetoothDevicePreference extends GearPreference implements
private String contentDescription = null;
/* Talk-back descriptions for various BT icons */
Resources r = getContext().getResources();
public final String COMPUTER = r.getString(R.string.bluetooth_talkback_computer);
public final String INPUT_PERIPHERAL = r.getString(
R.string.bluetooth_talkback_input_peripheral);
public final String HEADSET = r.getString(R.string.bluetooth_talkback_headset);
public final String PHONE = r.getString(R.string.bluetooth_talkback_phone);
public final String IMAGING = r.getString(R.string.bluetooth_talkback_imaging);
public final String HEADPHONE = r.getString(R.string.bluetooth_talkback_headphone);
public final String BLUETOOTH = r.getString(R.string.bluetooth_talkback_bluetooth);
Resources mResources;
public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) {
super(context, null);
mResources = getContext().getResources();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
if (sDimAlpha == Integer.MIN_VALUE) {
@@ -139,7 +126,8 @@ public final class BluetoothDevicePreference extends GearPreference implements
}
Pair<Integer, String> pair = getBtClassDrawableWithDescription();
Pair<Integer, String> pair = Utils.getBtClassDrawableWithDescription(mResources,
mCachedDevice);
if (pair.first != 0) {
setIcon(pair.first);
contentDescription = pair.second;
@@ -246,45 +234,4 @@ public final class BluetoothDevicePreference extends GearPreference implements
}
}
private Pair<Integer, String> getBtClassDrawableWithDescription() {
BluetoothClass btClass = mCachedDevice.getBtClass();
if (btClass != null) {
switch (btClass.getMajorDeviceClass()) {
case BluetoothClass.Device.Major.COMPUTER:
return new Pair<Integer, String>(R.drawable.ic_bt_laptop, COMPUTER);
case BluetoothClass.Device.Major.PHONE:
return new Pair<Integer, String>(R.drawable.ic_bt_cellphone, PHONE);
case BluetoothClass.Device.Major.PERIPHERAL:
return new Pair<Integer, String>(HidProfile.getHidClassDrawable(btClass),
INPUT_PERIPHERAL);
case BluetoothClass.Device.Major.IMAGING:
return new Pair<Integer, String>(R.drawable.ic_bt_imaging, IMAGING);
default:
// unrecognized device class; continue
}
} else {
Log.w(TAG, "mBtClass is null");
}
List<LocalBluetoothProfile> profiles = mCachedDevice.getProfiles();
for (LocalBluetoothProfile profile : profiles) {
int resId = profile.getDrawableResource(btClass);
if (resId != 0) {
return new Pair<Integer, String>(resId, null);
}
}
if (btClass != null) {
if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
return new Pair<Integer, String>(R.drawable.ic_bt_headset_hfp, HEADSET);
}
if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
return new Pair<Integer, String>(R.drawable.ic_bt_headphones_a2dp, HEADPHONE);
}
}
return new Pair<Integer, String>(R.drawable.ic_settings_bluetooth, BLUETOOTH);
}
}

View File

@@ -17,6 +17,7 @@
package com.android.settings.bluetooth;
import android.app.Activity;
import android.app.Fragment;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ContentResolver;
@@ -25,6 +26,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.SystemProperties;
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
@@ -352,12 +354,28 @@ public class BluetoothSettings extends DeviceListPreferenceFragment implements I
return;
}
final Bundle args = new Bundle();
args.putString(DeviceProfilesSettings.ARG_DEVICE_ADDRESS,
device.getDevice().getAddress());
final DeviceProfilesSettings profileSettings = new DeviceProfilesSettings();
profileSettings.setArguments(args);
profileSettings.show(getFragmentManager(),
DeviceProfilesSettings.class.getSimpleName());
Context context = getActivity();
boolean useDetailPage = FeatureFactory.getFactory(context).getBluetoothFeatureProvider(
context).isDeviceDetailPageEnabled();
if (!useDetailPage) {
// Old version - uses a dialog.
args.putString(DeviceProfilesSettings.ARG_DEVICE_ADDRESS,
device.getDevice().getAddress());
final DeviceProfilesSettings profileSettings = new DeviceProfilesSettings();
profileSettings.setArguments(args);
profileSettings.show(getFragmentManager(),
DeviceProfilesSettings.class.getSimpleName());
} else {
// New version - uses a separate screen.
args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS,
device.getDevice().getAddress());
BluetoothDeviceDetailsFragment fragment = new BluetoothDeviceDetailsFragment();
final SettingsActivity activity =
(SettingsActivity) BluetoothSettings.this.getActivity();
activity.startPreferencePanel(this,
BluetoothDeviceDetailsFragment.class.getName(), args,
R.string.device_details_title, null, null, 0);
}
};
/**

View File

@@ -17,20 +17,28 @@
package com.android.settings.bluetooth;
import android.app.AlertDialog;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.support.annotation.VisibleForTesting;
import android.util.Pair;
import android.widget.Toast;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.Utils.ErrorListener;
import java.util.List;
/**
* Utils is a helper class that contains constants for various
* Android resource IDs, debug logging flags, and static methods
@@ -141,4 +149,52 @@ public final class Utils {
com.android.settingslib.bluetooth.Utils.setErrorListener(mErrorListener);
}
};
static Pair<Integer, String> getBtClassDrawableWithDescription(Resources r,
CachedBluetoothDevice cachedDevice) {
BluetoothClass btClass = cachedDevice.getBtClass();
if (btClass != null) {
switch (btClass.getMajorDeviceClass()) {
case BluetoothClass.Device.Major.COMPUTER:
return new Pair<Integer, String>(R.drawable.ic_bt_laptop,
r.getString(R.string.bluetooth_talkback_computer));
case BluetoothClass.Device.Major.PHONE:
return new Pair<Integer, String>(R.drawable.ic_bt_cellphone,
r.getString(R.string.bluetooth_talkback_phone));
case BluetoothClass.Device.Major.PERIPHERAL:
return new Pair<Integer, String>(HidProfile.getHidClassDrawable(btClass),
r.getString(
R.string.bluetooth_talkback_input_peripheral));
case BluetoothClass.Device.Major.IMAGING:
return new Pair<Integer, String>(R.drawable.ic_bt_imaging,
r.getString(R.string.bluetooth_talkback_imaging));
default:
// unrecognized device class; continue
}
}
List<LocalBluetoothProfile> profiles = cachedDevice.getProfiles();
for (LocalBluetoothProfile profile : profiles) {
int resId = profile.getDrawableResource(btClass);
if (resId != 0) {
return new Pair<Integer, String>(resId, null);
}
}
if (btClass != null) {
if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
return new Pair<Integer, String>(R.drawable.ic_bt_headset_hfp,
r.getString(R.string.bluetooth_talkback_headset));
}
if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
return new Pair<Integer, String>(R.drawable.ic_bt_headphones_a2dp,
r.getString(R.string.bluetooth_talkback_headphone));
}
}
return new Pair<Integer, String>(R.drawable.ic_settings_bluetooth,
r.getString(R.string.bluetooth_talkback_bluetooth));
}
}

View File

@@ -57,6 +57,7 @@ import com.android.settings.applications.UsageAccessDetails;
import com.android.settings.applications.VrListenerSettings;
import com.android.settings.applications.WriteSettingsDetails;
import com.android.settings.applications.assist.ManageAssist;
import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.dashboard.SupportFragment;
@@ -247,6 +248,7 @@ public class SettingsGateway {
EnterprisePrivacySettings.class.getName(),
WebViewAppPicker.class.getName(),
LockscreenDashboardFragment.class.getName(),
BluetoothDeviceDetailsFragment.class.getName(),
};
public static final String[] SETTINGS_FOR_RESTRICTED = {