| /* |
| * Copyright (C) 2008 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.app.AlertDialog; |
| import android.bluetooth.BluetoothClass; |
| import android.bluetooth.BluetoothDevice; |
| import android.bluetooth.BluetoothProfile; |
| import android.content.Context; |
| import android.content.DialogInterface; |
| import android.graphics.drawable.Drawable; |
| import android.preference.Preference; |
| import android.text.Html; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.util.TypedValue; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.View.OnClickListener; |
| import android.view.ViewGroup; |
| import android.widget.ImageView; |
| |
| import com.android.settings.R; |
| |
| import java.util.List; |
| |
| /** |
| * BluetoothDevicePreference is the preference type used to display each remote |
| * Bluetooth device in the Bluetooth Settings screen. |
| */ |
| public final class BluetoothDevicePreference extends Preference implements |
| CachedBluetoothDevice.Callback, OnClickListener { |
| private static final String TAG = "BluetoothDevicePreference"; |
| |
| private static int sDimAlpha = Integer.MIN_VALUE; |
| |
| private final CachedBluetoothDevice mCachedDevice; |
| |
| private OnClickListener mOnSettingsClickListener; |
| |
| private AlertDialog mDisconnectDialog; |
| |
| public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) { |
| super(context); |
| |
| if (sDimAlpha == Integer.MIN_VALUE) { |
| TypedValue outValue = new TypedValue(); |
| context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true); |
| sDimAlpha = (int) (outValue.getFloat() * 255); |
| } |
| |
| mCachedDevice = cachedDevice; |
| |
| if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) { |
| setWidgetLayoutResource(R.layout.preference_bluetooth); |
| } |
| |
| mCachedDevice.registerCallback(this); |
| |
| onDeviceAttributesChanged(); |
| } |
| |
| CachedBluetoothDevice getCachedDevice() { |
| return mCachedDevice; |
| } |
| |
| public void setOnSettingsClickListener(OnClickListener listener) { |
| mOnSettingsClickListener = listener; |
| } |
| |
| @Override |
| protected void onPrepareForRemoval() { |
| super.onPrepareForRemoval(); |
| mCachedDevice.unregisterCallback(this); |
| if (mDisconnectDialog != null) { |
| mDisconnectDialog.dismiss(); |
| mDisconnectDialog = null; |
| } |
| } |
| |
| public void onDeviceAttributesChanged() { |
| /* |
| * The preference framework takes care of making sure the value has |
| * changed before proceeding. It will also call notifyChanged() if |
| * any preference info has changed from the previous value. |
| */ |
| setTitle(mCachedDevice.getName()); |
| |
| int summaryResId = getConnectionSummary(); |
| if (summaryResId != 0) { |
| setSummary(summaryResId); |
| } else { |
| setSummary(null); // empty summary for unpaired devices |
| } |
| |
| int iconResId = getBtClassDrawable(); |
| if (iconResId != 0) { |
| setIcon(iconResId); |
| } |
| |
| // Used to gray out the item |
| setEnabled(!mCachedDevice.isBusy()); |
| |
| // This could affect ordering, so notify that |
| notifyHierarchyChanged(); |
| } |
| |
| @Override |
| protected void onBindView(View view) { |
| // Disable this view if the bluetooth enable/disable preference view is off |
| if (null != findPreferenceInHierarchy("bt_checkbox")) { |
| setDependency("bt_checkbox"); |
| } |
| |
| if (mCachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) { |
| ImageView deviceDetails = (ImageView) view.findViewById(R.id.deviceDetails); |
| if (deviceDetails != null) { |
| deviceDetails.setOnClickListener(this); |
| deviceDetails.setTag(mCachedDevice); |
| deviceDetails.setAlpha(isEnabled() ? 255 : sDimAlpha); |
| } |
| } |
| |
| super.onBindView(view); |
| } |
| |
| public void onClick(View v) { |
| // Should never be null by construction |
| if (mOnSettingsClickListener != null) { |
| mOnSettingsClickListener.onClick(v); |
| } |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if ((o == null) || !(o instanceof BluetoothDevicePreference)) { |
| return false; |
| } |
| return mCachedDevice.equals( |
| ((BluetoothDevicePreference) o).mCachedDevice); |
| } |
| |
| @Override |
| public int hashCode() { |
| return mCachedDevice.hashCode(); |
| } |
| |
| @Override |
| public int compareTo(Preference another) { |
| if (!(another instanceof BluetoothDevicePreference)) { |
| // Rely on default sort |
| return super.compareTo(another); |
| } |
| |
| return mCachedDevice |
| .compareTo(((BluetoothDevicePreference) another).mCachedDevice); |
| } |
| |
| void onClicked() { |
| int bondState = mCachedDevice.getBondState(); |
| |
| if (mCachedDevice.isConnected()) { |
| askDisconnect(); |
| } else if (bondState == BluetoothDevice.BOND_BONDED) { |
| mCachedDevice.connect(true); |
| } else if (bondState == BluetoothDevice.BOND_NONE) { |
| pair(); |
| } |
| } |
| |
| // Show disconnect confirmation dialog for a device. |
| private void askDisconnect() { |
| Context context = getContext(); |
| String name = mCachedDevice.getName(); |
| if (TextUtils.isEmpty(name)) { |
| name = context.getString(R.string.bluetooth_device); |
| } |
| String message = context.getString(R.string.bluetooth_disconnect_all_profiles, name); |
| String title = context.getString(R.string.bluetooth_disconnect_title); |
| |
| DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() { |
| public void onClick(DialogInterface dialog, int which) { |
| mCachedDevice.disconnect(); |
| } |
| }; |
| |
| mDisconnectDialog = Utils.showDisconnectDialog(context, |
| mDisconnectDialog, disconnectListener, title, Html.fromHtml(message)); |
| } |
| |
| private void pair() { |
| if (!mCachedDevice.startPairing()) { |
| Utils.showError(getContext(), mCachedDevice.getName(), |
| R.string.bluetooth_pairing_error_message); |
| } |
| } |
| |
| private int getConnectionSummary() { |
| final CachedBluetoothDevice cachedDevice = mCachedDevice; |
| |
| boolean profileConnected = false; // at least one profile is connected |
| boolean a2dpNotConnected = false; // A2DP is preferred but not connected |
| boolean headsetNotConnected = false; // Headset is preferred but not connected |
| |
| for (LocalBluetoothProfile profile : cachedDevice.getProfiles()) { |
| int connectionStatus = cachedDevice.getProfileConnectionState(profile); |
| |
| switch (connectionStatus) { |
| case BluetoothProfile.STATE_CONNECTING: |
| case BluetoothProfile.STATE_DISCONNECTING: |
| return Utils.getConnectionStateSummary(connectionStatus); |
| |
| case BluetoothProfile.STATE_CONNECTED: |
| profileConnected = true; |
| break; |
| |
| case BluetoothProfile.STATE_DISCONNECTED: |
| if (profile.isProfileReady() && profile.isPreferred(cachedDevice.getDevice())) { |
| if (profile instanceof A2dpProfile) { |
| a2dpNotConnected = true; |
| } else if (profile instanceof HeadsetProfile) { |
| headsetNotConnected = true; |
| } |
| } |
| break; |
| } |
| } |
| |
| if (profileConnected) { |
| if (a2dpNotConnected && headsetNotConnected) { |
| return R.string.bluetooth_connected_no_headset_no_a2dp; |
| } else if (a2dpNotConnected) { |
| return R.string.bluetooth_connected_no_a2dp; |
| } else if (headsetNotConnected) { |
| return R.string.bluetooth_connected_no_headset; |
| } else { |
| return R.string.bluetooth_connected; |
| } |
| } |
| |
| switch (cachedDevice.getBondState()) { |
| case BluetoothDevice.BOND_BONDING: |
| return R.string.bluetooth_pairing; |
| |
| case BluetoothDevice.BOND_BONDED: |
| case BluetoothDevice.BOND_NONE: |
| default: |
| return 0; |
| } |
| } |
| |
| private int getBtClassDrawable() { |
| BluetoothClass btClass = mCachedDevice.getBtClass(); |
| if (btClass != null) { |
| switch (btClass.getMajorDeviceClass()) { |
| case BluetoothClass.Device.Major.COMPUTER: |
| return R.drawable.ic_bt_laptop; |
| |
| case BluetoothClass.Device.Major.PHONE: |
| return R.drawable.ic_bt_cellphone; |
| |
| case BluetoothClass.Device.Major.PERIPHERAL: |
| return HidProfile.getHidClassDrawable(btClass); |
| |
| case BluetoothClass.Device.Major.IMAGING: |
| return R.drawable.ic_bt_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 resId; |
| } |
| } |
| if (btClass != null) { |
| if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) { |
| return R.drawable.ic_bt_headphones_a2dp; |
| |
| } |
| if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) { |
| return R.drawable.ic_bt_headset_hfp; |
| } |
| } |
| return 0; |
| } |
| } |