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:
29
res/xml/bluetooth_device_details_fragment.xml
Normal file
29
res/xml/bluetooth_device_details_fragment.xml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<PreferenceScreen
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:title="@string/bluetooth_device_advanced_title">
|
||||||
|
|
||||||
|
<com.android.settings.applications.LayoutPreference
|
||||||
|
android:key="action_buttons"
|
||||||
|
android:layout="@layout/app_action_buttons"
|
||||||
|
android:selectable="false"/>
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="bluetooth_profiles"/>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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();
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -17,7 +17,6 @@
|
|||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.bluetooth.BluetoothClass;
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
@@ -27,7 +26,6 @@ import android.support.v7.preference.Preference;
|
|||||||
import android.support.v7.preference.PreferenceViewHolder;
|
import android.support.v7.preference.PreferenceViewHolder;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.widget.ImageView;
|
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.overlay.FeatureFactory;
|
||||||
import com.android.settings.widget.GearPreference;
|
import com.android.settings.widget.GearPreference;
|
||||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
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;
|
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
|
||||||
|
|
||||||
@@ -63,18 +57,11 @@ public final class BluetoothDevicePreference extends GearPreference implements
|
|||||||
private String contentDescription = null;
|
private String contentDescription = null;
|
||||||
|
|
||||||
/* Talk-back descriptions for various BT icons */
|
/* Talk-back descriptions for various BT icons */
|
||||||
Resources r = getContext().getResources();
|
Resources mResources;
|
||||||
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);
|
|
||||||
|
|
||||||
public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) {
|
public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) {
|
||||||
super(context, null);
|
super(context, null);
|
||||||
|
mResources = getContext().getResources();
|
||||||
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||||
|
|
||||||
if (sDimAlpha == Integer.MIN_VALUE) {
|
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) {
|
if (pair.first != 0) {
|
||||||
setIcon(pair.first);
|
setIcon(pair.first);
|
||||||
contentDescription = pair.second;
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.Fragment;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
@@ -25,6 +26,7 @@ import android.content.Intent;
|
|||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.SystemProperties;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.support.annotation.VisibleForTesting;
|
import android.support.annotation.VisibleForTesting;
|
||||||
import android.support.v7.preference.Preference;
|
import android.support.v7.preference.Preference;
|
||||||
@@ -352,12 +354,28 @@ public class BluetoothSettings extends DeviceListPreferenceFragment implements I
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Bundle args = new Bundle();
|
final Bundle args = new Bundle();
|
||||||
args.putString(DeviceProfilesSettings.ARG_DEVICE_ADDRESS,
|
Context context = getActivity();
|
||||||
device.getDevice().getAddress());
|
boolean useDetailPage = FeatureFactory.getFactory(context).getBluetoothFeatureProvider(
|
||||||
final DeviceProfilesSettings profileSettings = new DeviceProfilesSettings();
|
context).isDeviceDetailPageEnabled();
|
||||||
profileSettings.setArguments(args);
|
if (!useDetailPage) {
|
||||||
profileSettings.show(getFragmentManager(),
|
// Old version - uses a dialog.
|
||||||
DeviceProfilesSettings.class.getSimpleName());
|
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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -17,20 +17,28 @@
|
|||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothProfile;
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.support.annotation.VisibleForTesting;
|
import android.support.annotation.VisibleForTesting;
|
||||||
|
import android.util.Pair;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
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;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
|
import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
|
||||||
import com.android.settingslib.bluetooth.Utils.ErrorListener;
|
import com.android.settingslib.bluetooth.Utils.ErrorListener;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utils is a helper class that contains constants for various
|
* Utils is a helper class that contains constants for various
|
||||||
* Android resource IDs, debug logging flags, and static methods
|
* Android resource IDs, debug logging flags, and static methods
|
||||||
@@ -141,4 +149,52 @@ public final class Utils {
|
|||||||
com.android.settingslib.bluetooth.Utils.setErrorListener(mErrorListener);
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -57,6 +57,7 @@ import com.android.settings.applications.UsageAccessDetails;
|
|||||||
import com.android.settings.applications.VrListenerSettings;
|
import com.android.settings.applications.VrListenerSettings;
|
||||||
import com.android.settings.applications.WriteSettingsDetails;
|
import com.android.settings.applications.WriteSettingsDetails;
|
||||||
import com.android.settings.applications.assist.ManageAssist;
|
import com.android.settings.applications.assist.ManageAssist;
|
||||||
|
import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
|
||||||
import com.android.settings.bluetooth.BluetoothSettings;
|
import com.android.settings.bluetooth.BluetoothSettings;
|
||||||
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
|
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
|
||||||
import com.android.settings.dashboard.SupportFragment;
|
import com.android.settings.dashboard.SupportFragment;
|
||||||
@@ -247,6 +248,7 @@ public class SettingsGateway {
|
|||||||
EnterprisePrivacySettings.class.getName(),
|
EnterprisePrivacySettings.class.getName(),
|
||||||
WebViewAppPicker.class.getName(),
|
WebViewAppPicker.class.getName(),
|
||||||
LockscreenDashboardFragment.class.getName(),
|
LockscreenDashboardFragment.class.getName(),
|
||||||
|
BluetoothDeviceDetailsFragment.class.getName(),
|
||||||
};
|
};
|
||||||
|
|
||||||
public static final String[] SETTINGS_FOR_RESTRICTED = {
|
public static final String[] SETTINGS_FOR_RESTRICTED = {
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
com.android.settings.bluetooth.DevicePickerFragment
|
com.android.settings.bluetooth.DevicePickerFragment
|
||||||
|
com.android.settings.bluetooth.BluetoothDeviceDetailsFragment
|
||||||
com.android.settings.bluetooth.BluetoothPairingDetail
|
com.android.settings.bluetooth.BluetoothPairingDetail
|
||||||
com.android.settings.notification.ZenModePrioritySettings
|
com.android.settings.notification.ZenModePrioritySettings
|
||||||
com.android.settings.accounts.AccountDetailDashboardFragment
|
com.android.settings.accounts.AccountDetailDashboardFragment
|
||||||
|
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.SettingsRobolectricTestRunner;
|
||||||
|
import com.android.settings.TestConfig;
|
||||||
|
import com.android.settings.applications.LayoutPreference;
|
||||||
|
import com.android.settings.testutils.shadow.SettingsShadowBluetoothDevice;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
|
||||||
|
shadows=SettingsShadowBluetoothDevice.class)
|
||||||
|
public class BluetoothDetailsButtonsControllerTest extends BluetoothDetailsControllerTestBase {
|
||||||
|
private BluetoothDetailsButtonsController mController;
|
||||||
|
private LayoutPreference mLayoutPreference;
|
||||||
|
private Button mLeftButton;
|
||||||
|
private Button mRightButton;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() {
|
||||||
|
super.setUp();
|
||||||
|
mController = new BluetoothDetailsButtonsController(mContext, mFragment, mCachedDevice,
|
||||||
|
mLifecycle);
|
||||||
|
mLeftButton = new Button(mContext);
|
||||||
|
mRightButton = new Button(mContext);
|
||||||
|
mLayoutPreference = new LayoutPreference(mContext, R.layout.app_action_buttons);
|
||||||
|
mLayoutPreference.setKey(mController.getPreferenceKey());
|
||||||
|
mScreen.addPreference(mLayoutPreference);
|
||||||
|
mLeftButton = (Button) mLayoutPreference.findViewById(R.id.left_button);
|
||||||
|
mRightButton = (Button) mLayoutPreference.findViewById(R.id.right_button);
|
||||||
|
setupDevice(mDeviceConfig);
|
||||||
|
when(mCachedDevice.isBusy()).thenReturn(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void connected() {
|
||||||
|
showScreen(mController);
|
||||||
|
assertThat(mLeftButton.getText()).isEqualTo(
|
||||||
|
mContext.getString(R.string.bluetooth_device_context_disconnect));
|
||||||
|
assertThat(mRightButton.getText()).isEqualTo(mContext.getString(R.string.forget));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void clickOnDisconnect() {
|
||||||
|
showScreen(mController);
|
||||||
|
mLeftButton.callOnClick();
|
||||||
|
verify(mCachedDevice).disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void clickOnConnect() {
|
||||||
|
when(mCachedDevice.isConnected()).thenReturn(false);
|
||||||
|
showScreen(mController);
|
||||||
|
|
||||||
|
assertThat(mLeftButton.getText()).isEqualTo(
|
||||||
|
mContext.getString(R.string.bluetooth_device_context_connect));
|
||||||
|
|
||||||
|
mLeftButton.callOnClick();
|
||||||
|
verify(mCachedDevice).connect(eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void becomeDisconnected() {
|
||||||
|
showScreen(mController);
|
||||||
|
// By default we start out with the device connected.
|
||||||
|
assertThat(mLeftButton.getText()).isEqualTo(
|
||||||
|
mContext.getString(R.string.bluetooth_device_context_disconnect));
|
||||||
|
|
||||||
|
// Now make the device appear to have changed to disconnected.
|
||||||
|
when(mCachedDevice.isConnected()).thenReturn(false);
|
||||||
|
mController.onDeviceAttributesChanged();
|
||||||
|
assertThat(mLeftButton.getText()).isEqualTo(
|
||||||
|
mContext.getString(R.string.bluetooth_device_context_connect));
|
||||||
|
|
||||||
|
// Click the button and make sure that connect (not disconnect) gets called.
|
||||||
|
mLeftButton.callOnClick();
|
||||||
|
verify(mCachedDevice).connect(eq(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void becomeConnected() {
|
||||||
|
// Start out with the device disconnected.
|
||||||
|
when(mCachedDevice.isConnected()).thenReturn(false);
|
||||||
|
showScreen(mController);
|
||||||
|
|
||||||
|
assertThat(mLeftButton.getText()).isEqualTo(
|
||||||
|
mContext.getString(R.string.bluetooth_device_context_connect));
|
||||||
|
|
||||||
|
// Now make the device appear to have changed to connected.
|
||||||
|
when(mCachedDevice.isConnected()).thenReturn(true);
|
||||||
|
mController.onDeviceAttributesChanged();
|
||||||
|
assertThat(mLeftButton.getText()).isEqualTo(
|
||||||
|
mContext.getString(R.string.bluetooth_device_context_disconnect));
|
||||||
|
|
||||||
|
// Click the button and make sure that disconnnect (not connect) gets called.
|
||||||
|
mLeftButton.callOnClick();
|
||||||
|
verify(mCachedDevice).disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void forget() {
|
||||||
|
showScreen(mController);
|
||||||
|
mRightButton.callOnClick();
|
||||||
|
verify(mCachedDevice).unpair();
|
||||||
|
verify(mActivity).finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void startsOutBusy() {
|
||||||
|
when(mCachedDevice.isBusy()).thenReturn(true);
|
||||||
|
showScreen(mController);
|
||||||
|
assertThat(mLeftButton.getText()).isEqualTo(
|
||||||
|
mContext.getString(R.string.bluetooth_device_context_disconnect));
|
||||||
|
assertThat(mRightButton.getText()).isEqualTo(mContext.getString(R.string.forget));
|
||||||
|
assertThat(mLeftButton.isEnabled()).isFalse();
|
||||||
|
|
||||||
|
// Now pretend the device became non-busy.
|
||||||
|
when(mCachedDevice.isBusy()).thenReturn(false);
|
||||||
|
mController.onDeviceAttributesChanged();
|
||||||
|
assertThat(mLeftButton.isEnabled()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void becomesBusy() {
|
||||||
|
showScreen(mController);
|
||||||
|
assertThat(mLeftButton.isEnabled()).isTrue();
|
||||||
|
|
||||||
|
when(mCachedDevice.isBusy()).thenReturn(true);
|
||||||
|
mController.onDeviceAttributesChanged();
|
||||||
|
assertThat(mLeftButton.isEnabled()).isFalse();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v14.preference.PreferenceFragment;
|
||||||
|
import android.support.v7.preference.PreferenceScreen;
|
||||||
|
|
||||||
|
import com.android.settings.SettingsRobolectricTestRunner;
|
||||||
|
import com.android.settings.TestConfig;
|
||||||
|
import com.android.settings.testutils.shadow.SettingsShadowBluetoothDevice;
|
||||||
|
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||||
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
|
||||||
|
shadows=SettingsShadowBluetoothDevice.class)
|
||||||
|
public class BluetoothDetailsControllerEventsTest extends BluetoothDetailsControllerTestBase {
|
||||||
|
|
||||||
|
static class TestController extends BluetoothDetailsController {
|
||||||
|
public TestController(Context context, PreferenceFragment fragment,
|
||||||
|
CachedBluetoothDevice device,
|
||||||
|
Lifecycle lifecycle) {
|
||||||
|
super(context, fragment, device, lifecycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPreferenceKey() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(PreferenceScreen screen) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void refresh() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pauseResumeEvents() {
|
||||||
|
|
||||||
|
TestController controller = spy(new TestController(mContext, mFragment, mCachedDevice,
|
||||||
|
mLifecycle));
|
||||||
|
verify(mLifecycle).addObserver(any(BluetoothDetailsController.class));
|
||||||
|
|
||||||
|
showScreen(controller);
|
||||||
|
verify(mCachedDevice, times(1)).registerCallback(controller);
|
||||||
|
verify(controller, times(1)).refresh();
|
||||||
|
|
||||||
|
controller.onPause();
|
||||||
|
verify(controller, times(1)).refresh();
|
||||||
|
verify(mCachedDevice).unregisterCallback(controller);
|
||||||
|
|
||||||
|
controller.onResume();
|
||||||
|
verify(controller, times(2)).refresh();
|
||||||
|
verify(mCachedDevice, times(2)).registerCallback(controller);
|
||||||
|
|
||||||
|
// The init function should only have been called once
|
||||||
|
verify(controller, times(1)).init(mScreen);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v7.preference.PreferenceManager;
|
||||||
|
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 org.junit.Before;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
|
public class BluetoothDetailsControllerTestBase {
|
||||||
|
protected Context mContext = RuntimeEnvironment.application;
|
||||||
|
protected Lifecycle mLifecycle;
|
||||||
|
protected DeviceConfig mDeviceConfig;
|
||||||
|
protected BluetoothDevice mDevice;
|
||||||
|
protected BluetoothManager mBluetoothManager;
|
||||||
|
protected BluetoothAdapter mBluetoothAdapter;
|
||||||
|
protected PreferenceScreen mScreen;
|
||||||
|
protected PreferenceManager mPreferenceManager;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
protected BluetoothDeviceDetailsFragment mFragment;
|
||||||
|
@Mock
|
||||||
|
protected CachedBluetoothDevice mCachedDevice;
|
||||||
|
@Mock
|
||||||
|
protected Activity mActivity;
|
||||||
|
@Mock
|
||||||
|
protected BluetoothClass mBluetoothDeviceClass;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mPreferenceManager = new PreferenceManager(mContext);
|
||||||
|
mScreen = mPreferenceManager.createPreferenceScreen(mContext);
|
||||||
|
mDeviceConfig = makeDefaultDeviceConfig();
|
||||||
|
when(mFragment.getActivity()).thenReturn(mActivity);
|
||||||
|
when(mActivity.getApplicationContext()).thenReturn(mContext);
|
||||||
|
when(mFragment.getContext()).thenReturn(mContext);
|
||||||
|
when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
|
||||||
|
when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
|
||||||
|
mLifecycle = spy(new Lifecycle());
|
||||||
|
mBluetoothManager = new BluetoothManager(mContext);
|
||||||
|
mBluetoothAdapter = mBluetoothManager.getAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class DeviceConfig {
|
||||||
|
private String name;
|
||||||
|
private String address;
|
||||||
|
private int majorDeviceClass;
|
||||||
|
private boolean connected;
|
||||||
|
private int connectionSummary;
|
||||||
|
|
||||||
|
public DeviceConfig setName(String newValue) {
|
||||||
|
this.name = newValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeviceConfig setAddress(String newValue) {
|
||||||
|
this.address = newValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeviceConfig setMajorDeviceClass(int newValue) {
|
||||||
|
this.majorDeviceClass = newValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeviceConfig setConnected(boolean newValue) {
|
||||||
|
this.connected = newValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public DeviceConfig setConnectionSummary(int newValue) {
|
||||||
|
this.connectionSummary = newValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMajorDeviceClass() {
|
||||||
|
return majorDeviceClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getConnectionSummary() {
|
||||||
|
return connectionSummary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static DeviceConfig makeDefaultDeviceConfig() {
|
||||||
|
return new DeviceConfig()
|
||||||
|
.setName("Mock Device")
|
||||||
|
.setAddress("B4:B0:34:B5:3B:1B")
|
||||||
|
.setMajorDeviceClass(BluetoothClass.Device.Major.AUDIO_VIDEO)
|
||||||
|
.setConnected(true)
|
||||||
|
.setConnectionSummary(R.string.bluetooth_connected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the device mock to return various state based on a test config.
|
||||||
|
* @param config
|
||||||
|
*/
|
||||||
|
protected void setupDevice(DeviceConfig config) {
|
||||||
|
when(mCachedDevice.getName()).thenReturn(config.getName());
|
||||||
|
when(mBluetoothDeviceClass.getMajorDeviceClass()).thenReturn(config.getMajorDeviceClass());
|
||||||
|
when(mCachedDevice.isConnected()).thenReturn(config.isConnected());
|
||||||
|
when(mCachedDevice.getConnectionSummary()).thenReturn(config.getConnectionSummary());
|
||||||
|
|
||||||
|
mDevice = mBluetoothAdapter.getRemoteDevice(mDeviceConfig.getAddress());
|
||||||
|
when(mCachedDevice.getDevice()).thenReturn(mDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method to call displayPreference and onResume.
|
||||||
|
*/
|
||||||
|
protected void showScreen(BluetoothDetailsController controller) {
|
||||||
|
controller.displayPreference(mScreen);
|
||||||
|
controller.onResume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Mockito.inOrder;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.support.v7.preference.Preference;
|
||||||
|
|
||||||
|
import com.android.settings.SettingsRobolectricTestRunner;
|
||||||
|
import com.android.settings.TestConfig;
|
||||||
|
import com.android.settings.testutils.FakeFeatureFactory;
|
||||||
|
import com.android.settings.testutils.shadow.SettingsShadowBluetoothDevice;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
|
||||||
|
import com.android.settings.widget.EntityHeaderController;
|
||||||
|
import com.android.settingslib.R;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Answers;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
|
||||||
|
shadows={SettingsShadowBluetoothDevice.class, ShadowEntityHeaderController.class})
|
||||||
|
public class BluetoothDetailsHeaderControllerTest extends BluetoothDetailsControllerTestBase {
|
||||||
|
private BluetoothDetailsHeaderController mController;
|
||||||
|
private Preference mPreference;
|
||||||
|
|
||||||
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
|
private EntityHeaderController mHeaderController;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() {
|
||||||
|
super.setUp();
|
||||||
|
FakeFeatureFactory.setupForTest(spy(mContext));
|
||||||
|
ShadowEntityHeaderController.setUseMock(mHeaderController);
|
||||||
|
mController = new BluetoothDetailsHeaderController(mContext, mFragment, mCachedDevice,
|
||||||
|
mLifecycle);
|
||||||
|
mPreference = new Preference(mContext);
|
||||||
|
mPreference.setKey(mController.getPreferenceKey());
|
||||||
|
mScreen.addPreference(mPreference);
|
||||||
|
setupDevice(mDeviceConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
ShadowEntityHeaderController.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void header() {
|
||||||
|
showScreen(mController);
|
||||||
|
|
||||||
|
verify(mHeaderController).setLabel(mDeviceConfig.getName());
|
||||||
|
verify(mHeaderController).setIcon(any(Drawable.class));
|
||||||
|
verify(mHeaderController).setIconContentDescription(any(String.class));
|
||||||
|
verify(mHeaderController).setSummary(any(String.class));
|
||||||
|
verify(mHeaderController).done(mActivity, mContext);
|
||||||
|
verify(mHeaderController).done(mActivity, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void connectionStatusChangesWhileScreenOpen() {
|
||||||
|
ArrayList<LocalBluetoothProfile> profiles = new ArrayList<>();
|
||||||
|
InOrder inOrder = inOrder(mHeaderController);
|
||||||
|
when(mCachedDevice.getConnectionSummary()).thenReturn(R.string.bluetooth_connected);
|
||||||
|
showScreen(mController);
|
||||||
|
inOrder.verify(mHeaderController).setSummary(mContext.getString(R.string.bluetooth_connected));
|
||||||
|
|
||||||
|
when(mCachedDevice.getConnectionSummary()).thenReturn(0);
|
||||||
|
mController.onDeviceAttributesChanged();
|
||||||
|
inOrder.verify(mHeaderController).setSummary((CharSequence) null);
|
||||||
|
|
||||||
|
when(mCachedDevice.getConnectionSummary()).thenReturn(R.string.bluetooth_connecting);
|
||||||
|
mController.onDeviceAttributesChanged();
|
||||||
|
inOrder.verify(mHeaderController).setSummary(
|
||||||
|
mContext.getString(R.string.bluetooth_connecting));
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import com.android.settings.SettingsRobolectricTestRunner;
|
||||||
|
import com.android.settings.TestConfig;
|
||||||
|
import com.android.settings.testutils.shadow.SettingsShadowBluetoothDevice;
|
||||||
|
import com.android.settingslib.widget.FooterPreference;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
|
||||||
|
shadows=SettingsShadowBluetoothDevice.class)
|
||||||
|
public class BluetoothDetailsMacAddressControllerTest extends BluetoothDetailsControllerTestBase {
|
||||||
|
private BluetoothDetailsMacAddressController mController;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() {
|
||||||
|
super.setUp();
|
||||||
|
mController = new BluetoothDetailsMacAddressController(mContext, mFragment, mCachedDevice,
|
||||||
|
mLifecycle);
|
||||||
|
setupDevice(mDeviceConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void macAddress() {
|
||||||
|
showScreen(mController);
|
||||||
|
FooterPreference footer = (FooterPreference) mScreen.findPreference(
|
||||||
|
mController.getPreferenceKey());
|
||||||
|
assertThat(footer.getTitle().toString()).endsWith(mDeviceConfig.getAddress());
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,450 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v14.preference.SwitchPreference;
|
||||||
|
import android.support.v7.preference.PreferenceCategory;
|
||||||
|
import android.support.v7.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.SettingsRobolectricTestRunner;
|
||||||
|
import com.android.settings.TestConfig;
|
||||||
|
import com.android.settings.testutils.shadow.SettingsShadowBluetoothDevice;
|
||||||
|
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.PbapServerProfile;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
|
||||||
|
shadows=SettingsShadowBluetoothDevice.class)
|
||||||
|
public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsControllerTestBase {
|
||||||
|
private BluetoothDetailsProfilesController mController;
|
||||||
|
private List<LocalBluetoothProfile> mConnectableProfiles;
|
||||||
|
private PreferenceCategory mProfiles;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LocalBluetoothManager mLocalManager;
|
||||||
|
@Mock
|
||||||
|
private LocalBluetoothProfileManager mProfileManager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
mProfiles = spy(new PreferenceCategory(mContext));
|
||||||
|
when(mProfiles.getPreferenceManager()).thenReturn(mPreferenceManager);
|
||||||
|
|
||||||
|
mConnectableProfiles = new ArrayList<>();
|
||||||
|
when(mLocalManager.getProfileManager()).thenReturn(mProfileManager);
|
||||||
|
when(mCachedDevice.getConnectableProfiles()).thenAnswer(invocation ->
|
||||||
|
new ArrayList<>(mConnectableProfiles)
|
||||||
|
);
|
||||||
|
|
||||||
|
setupDevice(mDeviceConfig);
|
||||||
|
mController = new BluetoothDetailsProfilesController(mContext, mFragment, mLocalManager,
|
||||||
|
mCachedDevice, mLifecycle);
|
||||||
|
mProfiles.setKey(mController.getPreferenceKey());
|
||||||
|
mScreen.addPreference(mProfiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class FakeBluetoothProfile implements LocalBluetoothProfile {
|
||||||
|
protected HashSet<BluetoothDevice> mConnectedDevices;
|
||||||
|
protected HashMap<BluetoothDevice, Boolean> mPreferred;
|
||||||
|
protected Context mContext;
|
||||||
|
protected int mNameResourceId;
|
||||||
|
|
||||||
|
public FakeBluetoothProfile(Context context, int nameResourceId) {
|
||||||
|
mConnectedDevices = new HashSet<>();
|
||||||
|
mPreferred = new HashMap<>();
|
||||||
|
mContext = context;
|
||||||
|
mNameResourceId = nameResourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return mContext.getString(mNameResourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnectable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAutoConnectable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean connect(BluetoothDevice device) {
|
||||||
|
mConnectedDevices.add(device);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean disconnect(BluetoothDevice device) {
|
||||||
|
mConnectedDevices.remove(device);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getConnectionStatus(BluetoothDevice device) {
|
||||||
|
if (mConnectedDevices.contains(device)) {
|
||||||
|
return BluetoothProfile.STATE_CONNECTED;
|
||||||
|
} else {
|
||||||
|
return BluetoothProfile.STATE_DISCONNECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPreferred(BluetoothDevice device) {
|
||||||
|
return mPreferred.getOrDefault(device, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferred(BluetoothDevice device) {
|
||||||
|
return isPreferred(device) ?
|
||||||
|
BluetoothProfile.PRIORITY_ON : BluetoothProfile.PRIORITY_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPreferred(BluetoothDevice device, boolean preferred) {
|
||||||
|
mPreferred.put(device, preferred);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isProfileReady() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrdinal() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNameResource(BluetoothDevice device) {
|
||||||
|
return mNameResourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSummaryResourceForDevice(BluetoothDevice device) {
|
||||||
|
return Utils.getConnectionStateSummary(getConnectionStatus(device));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDrawableResource(BluetoothClass btClass) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and adds a mock LocalBluetoothProfile to the list of connectable profiles for the
|
||||||
|
* device.
|
||||||
|
@param profileNameResId the resource id for the name used by this profile
|
||||||
|
@param deviceIsPreferred whether this profile should start out as enabled for the device
|
||||||
|
*/
|
||||||
|
private LocalBluetoothProfile addFakeProfile(int profileNameResId,
|
||||||
|
boolean deviceIsPreferred) {
|
||||||
|
LocalBluetoothProfile profile = new FakeBluetoothProfile(mContext, profileNameResId);
|
||||||
|
profile.setPreferred(mDevice, deviceIsPreferred);
|
||||||
|
mConnectableProfiles.add(profile);
|
||||||
|
when(mProfileManager.getProfileByName(eq(profile.toString()))).thenReturn(profile);
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the list of SwitchPreference objects added to the screen - there should be one per
|
||||||
|
* Bluetooth profile.
|
||||||
|
*/
|
||||||
|
private List<SwitchPreference> getProfileSwitches(boolean expectOnlyMConnectable) {
|
||||||
|
if (expectOnlyMConnectable) {
|
||||||
|
assertThat(mConnectableProfiles).isNotEmpty();
|
||||||
|
assertThat(mProfiles.getPreferenceCount()).isEqualTo(mConnectableProfiles.size());
|
||||||
|
}
|
||||||
|
ArrayList<SwitchPreference> result = new ArrayList<>();
|
||||||
|
for (int i = 0; i < mProfiles.getPreferenceCount(); i++) {
|
||||||
|
result.add((SwitchPreference)mProfiles.getPreference(i));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyProfileSwitchTitles(List<SwitchPreference> switches) {
|
||||||
|
for (int i = 0; i < switches.size(); i++) {
|
||||||
|
String expectedTitle = mContext.getString(
|
||||||
|
mConnectableProfiles.get(i).getNameResource(mDevice));
|
||||||
|
assertThat(switches.get(i).getTitle()).isEqualTo(expectedTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void oneProfile() {
|
||||||
|
addFakeProfile(R.string.bluetooth_profile_a2dp, true);
|
||||||
|
showScreen(mController);
|
||||||
|
verifyProfileSwitchTitles(getProfileSwitches(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipleProfiles() {
|
||||||
|
addFakeProfile(R.string.bluetooth_profile_a2dp, true);
|
||||||
|
addFakeProfile(R.string.bluetooth_profile_headset, false);
|
||||||
|
showScreen(mController);
|
||||||
|
List<SwitchPreference> switches = getProfileSwitches(true);
|
||||||
|
verifyProfileSwitchTitles(switches);
|
||||||
|
assertThat(switches.get(0).isChecked()).isTrue();
|
||||||
|
assertThat(switches.get(1).isChecked()).isFalse();
|
||||||
|
|
||||||
|
// Both switches should be enabled.
|
||||||
|
assertThat(switches.get(0).isEnabled()).isTrue();
|
||||||
|
assertThat(switches.get(1).isEnabled()).isTrue();
|
||||||
|
|
||||||
|
// Make device busy.
|
||||||
|
when(mCachedDevice.isBusy()).thenReturn(true);
|
||||||
|
mController.onDeviceAttributesChanged();
|
||||||
|
|
||||||
|
// There should have been no new switches added.
|
||||||
|
assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
|
||||||
|
|
||||||
|
// Make sure both switches got disabled.
|
||||||
|
assertThat(switches.get(0).isEnabled()).isFalse();
|
||||||
|
assertThat(switches.get(1).isEnabled()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void disableThenReenableOneProfile() {
|
||||||
|
addFakeProfile(R.string.bluetooth_profile_a2dp, true);
|
||||||
|
addFakeProfile(R.string.bluetooth_profile_headset, true);
|
||||||
|
showScreen(mController);
|
||||||
|
List<SwitchPreference> switches = getProfileSwitches(true);
|
||||||
|
SwitchPreference pref = switches.get(0);
|
||||||
|
|
||||||
|
// Clicking the pref should cause the profile to become not-preferred.
|
||||||
|
assertThat(pref.isChecked()).isTrue();
|
||||||
|
pref.performClick();
|
||||||
|
assertThat(pref.isChecked()).isFalse();
|
||||||
|
assertThat(mConnectableProfiles.get(0).isPreferred(mDevice)).isFalse();
|
||||||
|
|
||||||
|
// Make sure no new preferences were added.
|
||||||
|
assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
|
||||||
|
|
||||||
|
// Clicking the pref again should make the profile once again preferred.
|
||||||
|
pref.performClick();
|
||||||
|
assertThat(pref.isChecked()).isTrue();
|
||||||
|
assertThat(mConnectableProfiles.get(0).isPreferred(mDevice)).isTrue();
|
||||||
|
|
||||||
|
// Make sure we still haven't gotten any new preferences added.
|
||||||
|
assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void disconnectedDeviceOneProfile() {
|
||||||
|
setupDevice(makeDefaultDeviceConfig().setConnected(false).setConnectionSummary(0));
|
||||||
|
addFakeProfile(R.string.bluetooth_profile_a2dp, true);
|
||||||
|
showScreen(mController);
|
||||||
|
verifyProfileSwitchTitles(getProfileSwitches(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pbapProfileStartsEnabled() {
|
||||||
|
setupDevice(makeDefaultDeviceConfig());
|
||||||
|
when(mCachedDevice.getPhonebookPermissionChoice()).thenReturn(
|
||||||
|
CachedBluetoothDevice.ACCESS_ALLOWED);
|
||||||
|
PbapServerProfile psp = mock(PbapServerProfile.class);
|
||||||
|
when(psp.getNameResource(mDevice)).thenReturn(R.string.bluetooth_profile_pbap);
|
||||||
|
when(psp.toString()).thenReturn(PbapServerProfile.NAME);
|
||||||
|
when(mProfileManager.getPbapProfile()).thenReturn(psp);
|
||||||
|
|
||||||
|
showScreen(mController);
|
||||||
|
List<SwitchPreference> switches = getProfileSwitches(false);
|
||||||
|
assertThat(switches.size()).isEqualTo(1);
|
||||||
|
SwitchPreference pref = switches.get(0);
|
||||||
|
assertThat(pref.getTitle()).isEqualTo(mContext.getString(R.string.bluetooth_profile_pbap));
|
||||||
|
assertThat(pref.isChecked()).isTrue();
|
||||||
|
|
||||||
|
pref.performClick();
|
||||||
|
assertThat(mProfiles.getPreferenceCount()).isEqualTo(1);
|
||||||
|
verify(mCachedDevice).setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_REJECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pbapProfileStartsDisabled() {
|
||||||
|
setupDevice(makeDefaultDeviceConfig());
|
||||||
|
when(mCachedDevice.getPhonebookPermissionChoice()).thenReturn(
|
||||||
|
CachedBluetoothDevice.ACCESS_REJECTED);
|
||||||
|
PbapServerProfile psp = mock(PbapServerProfile.class);
|
||||||
|
when(psp.getNameResource(mDevice)).thenReturn(R.string.bluetooth_profile_pbap);
|
||||||
|
when(psp.toString()).thenReturn(PbapServerProfile.NAME);
|
||||||
|
when(mProfileManager.getPbapProfile()).thenReturn(psp);
|
||||||
|
|
||||||
|
showScreen(mController);
|
||||||
|
List<SwitchPreference> switches = getProfileSwitches(false);
|
||||||
|
assertThat(switches.size()).isEqualTo(1);
|
||||||
|
SwitchPreference pref = switches.get(0);
|
||||||
|
assertThat(pref.getTitle()).isEqualTo(mContext.getString(R.string.bluetooth_profile_pbap));
|
||||||
|
assertThat(pref.isChecked()).isFalse();
|
||||||
|
|
||||||
|
pref.performClick();
|
||||||
|
assertThat(mProfiles.getPreferenceCount()).isEqualTo(1);
|
||||||
|
verify(mCachedDevice).setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mapProfile() {
|
||||||
|
setupDevice(makeDefaultDeviceConfig());
|
||||||
|
MapProfile mapProfile = mock(MapProfile.class);
|
||||||
|
when(mapProfile.getNameResource(mDevice)).thenReturn(R.string.bluetooth_profile_map);
|
||||||
|
when(mProfileManager.getMapProfile()).thenReturn(mapProfile);
|
||||||
|
when(mProfileManager.getProfileByName(eq(mapProfile.toString()))).thenReturn(mapProfile);
|
||||||
|
when(mCachedDevice.getMessagePermissionChoice()).thenReturn(
|
||||||
|
CachedBluetoothDevice.ACCESS_REJECTED);
|
||||||
|
showScreen(mController);
|
||||||
|
List<SwitchPreference> switches = getProfileSwitches(false);
|
||||||
|
assertThat(switches.size()).isEqualTo(1);
|
||||||
|
SwitchPreference pref = switches.get(0);
|
||||||
|
assertThat(pref.getTitle()).isEqualTo(mContext.getString(R.string.bluetooth_profile_map));
|
||||||
|
assertThat(pref.isChecked()).isFalse();
|
||||||
|
|
||||||
|
pref.performClick();
|
||||||
|
assertThat(mProfiles.getPreferenceCount()).isEqualTo(1);
|
||||||
|
verify(mCachedDevice).setMessagePermissionChoice(BluetoothDevice.ACCESS_ALLOWED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private A2dpProfile addMockA2dpProfile(boolean preferred, boolean supportsHighQualityAudio,
|
||||||
|
boolean highQualityAudioEnabled) {
|
||||||
|
A2dpProfile profile = mock(A2dpProfile.class);
|
||||||
|
when(mProfileManager.getProfileByName(eq(profile.toString()))).thenReturn(profile);
|
||||||
|
when(profile.getNameResource(mDevice)).thenReturn(R.string.bluetooth_profile_a2dp);
|
||||||
|
when(profile.getHighQualityAudioOptionLabel(mDevice)).thenReturn(mContext.getString(
|
||||||
|
R.string.bluetooth_profile_a2dp_high_quality_unknown_codec));
|
||||||
|
when(profile.supportsHighQualityAudio(mDevice)).thenReturn(supportsHighQualityAudio);
|
||||||
|
when(profile.isHighQualityAudioEnabled(mDevice)).thenReturn(highQualityAudioEnabled);
|
||||||
|
when(profile.isPreferred(mDevice)).thenReturn(preferred);
|
||||||
|
mConnectableProfiles.add(profile);
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SwitchPreference getHighQualityAudioPref() {
|
||||||
|
return (SwitchPreference) mScreen.findPreference(
|
||||||
|
BluetoothDetailsProfilesController.HIGH_QUALITY_AUDIO_PREF_TAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void highQualityAudio_prefIsPresentWhenSupported() {
|
||||||
|
setupDevice(makeDefaultDeviceConfig());
|
||||||
|
addMockA2dpProfile(true, true, true);
|
||||||
|
showScreen(mController);
|
||||||
|
SwitchPreference pref = getHighQualityAudioPref();
|
||||||
|
assertThat(pref.getKey()).isEqualTo(
|
||||||
|
BluetoothDetailsProfilesController.HIGH_QUALITY_AUDIO_PREF_TAG);
|
||||||
|
|
||||||
|
// Make sure the preference works when clicked on.
|
||||||
|
pref.performClick();
|
||||||
|
A2dpProfile profile = (A2dpProfile) mConnectableProfiles.get(0);
|
||||||
|
verify(profile).setHighQualityAudioEnabled(mDevice, false);
|
||||||
|
pref.performClick();
|
||||||
|
verify(profile).setHighQualityAudioEnabled(mDevice, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void highQualityAudio_prefIsAbsentWhenNotSupported() {
|
||||||
|
setupDevice(makeDefaultDeviceConfig());
|
||||||
|
addMockA2dpProfile(true, false, false);
|
||||||
|
showScreen(mController);
|
||||||
|
assertThat(mProfiles.getPreferenceCount()).isEqualTo(1);
|
||||||
|
SwitchPreference pref = (SwitchPreference) mProfiles.getPreference(0);
|
||||||
|
assertThat(pref.getKey()).isNotEqualTo(
|
||||||
|
BluetoothDetailsProfilesController.HIGH_QUALITY_AUDIO_PREF_TAG);
|
||||||
|
assertThat(pref.getTitle()).isEqualTo(mContext.getString(R.string.bluetooth_profile_a2dp));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void highQualityAudio_busyDeviceDisablesSwitch() {
|
||||||
|
setupDevice(makeDefaultDeviceConfig());
|
||||||
|
addMockA2dpProfile(true, true, true);
|
||||||
|
when(mCachedDevice.isBusy()).thenReturn(true);
|
||||||
|
showScreen(mController);
|
||||||
|
SwitchPreference pref = getHighQualityAudioPref();
|
||||||
|
assertThat(pref.isEnabled()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void highQualityAudio_mediaAudioDisabledAndReEnabled() {
|
||||||
|
setupDevice(makeDefaultDeviceConfig());
|
||||||
|
A2dpProfile audioProfile = addMockA2dpProfile(true, true, true);
|
||||||
|
showScreen(mController);
|
||||||
|
assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
|
||||||
|
|
||||||
|
// Disabling media audio should cause the high quality audio switch to disappear, but not
|
||||||
|
// the regular audio one.
|
||||||
|
SwitchPreference audioPref = (SwitchPreference) mScreen.findPreference(
|
||||||
|
audioProfile.toString());
|
||||||
|
audioPref.performClick();
|
||||||
|
verify(audioProfile).setPreferred(mDevice, false);
|
||||||
|
when(audioProfile.isPreferred(mDevice)).thenReturn(false);
|
||||||
|
mController.onDeviceAttributesChanged();
|
||||||
|
assertThat(audioPref.isVisible()).isTrue();
|
||||||
|
SwitchPreference highQualityAudioPref = getHighQualityAudioPref();
|
||||||
|
assertThat(highQualityAudioPref.isVisible()).isFalse();
|
||||||
|
|
||||||
|
// And re-enabling media audio should make high quality switch to reappear.
|
||||||
|
audioPref.performClick();
|
||||||
|
verify(audioProfile).setPreferred(mDevice, true);
|
||||||
|
when(audioProfile.isPreferred(mDevice)).thenReturn(true);
|
||||||
|
mController.onDeviceAttributesChanged();
|
||||||
|
highQualityAudioPref = getHighQualityAudioPref();
|
||||||
|
assertThat(highQualityAudioPref.isVisible()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void highQualityAudio_mediaAudioStartsDisabled() {
|
||||||
|
setupDevice(makeDefaultDeviceConfig());
|
||||||
|
A2dpProfile audioProfile = addMockA2dpProfile(false, true, true);
|
||||||
|
showScreen(mController);
|
||||||
|
SwitchPreference audioPref = (SwitchPreference) mScreen.findPreference(
|
||||||
|
audioProfile.toString());
|
||||||
|
SwitchPreference highQualityAudioPref = getHighQualityAudioPref();
|
||||||
|
assertThat(audioPref).isNotNull();
|
||||||
|
assertThat(audioPref.isChecked()).isFalse();
|
||||||
|
assertThat(highQualityAudioPref).isNotNull();
|
||||||
|
assertThat(highQualityAudioPref.isVisible()).isFalse();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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.testutils.shadow;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import org.robolectric.shadows.ShadowBluetoothDevice;
|
||||||
|
import org.robolectric.annotation.Implementation;
|
||||||
|
import org.robolectric.annotation.Implements;
|
||||||
|
|
||||||
|
@Implements(BluetoothDevice.class)
|
||||||
|
public class SettingsShadowBluetoothDevice {
|
||||||
|
private String mAddress;
|
||||||
|
|
||||||
|
public void __constructor__(String address) {
|
||||||
|
mAddress = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public String getAddress() {
|
||||||
|
return mAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public int hashCode() {
|
||||||
|
return mAddress.hashCode();
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user