Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings.bluetooth;
     18 
     19 import android.app.AlertDialog;
     20 import android.bluetooth.BluetoothClass;
     21 import android.bluetooth.BluetoothDevice;
     22 import android.bluetooth.BluetoothProfile;
     23 import android.content.Context;
     24 import android.content.DialogInterface;
     25 import android.graphics.drawable.Drawable;
     26 import android.preference.Preference;
     27 import android.text.Html;
     28 import android.text.TextUtils;
     29 import android.util.Log;
     30 import android.util.TypedValue;
     31 import android.view.LayoutInflater;
     32 import android.view.View;
     33 import android.view.View.OnClickListener;
     34 import android.view.ViewGroup;
     35 import android.widget.ImageView;
     36 
     37 import com.android.settings.R;
     38 
     39 import java.util.List;
     40 
     41 /**
     42  * BluetoothDevicePreference is the preference type used to display each remote
     43  * Bluetooth device in the Bluetooth Settings screen.
     44  */
     45 public final class BluetoothDevicePreference extends Preference implements
     46         CachedBluetoothDevice.Callback, OnClickListener {
     47     private static final String TAG = "BluetoothDevicePreference";
     48 
     49     private static int sDimAlpha = Integer.MIN_VALUE;
     50 
     51     private final CachedBluetoothDevice mCachedDevice;
     52 
     53     private OnClickListener mOnSettingsClickListener;
     54 
     55     private AlertDialog mDisconnectDialog;
     56 
     57     public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) {
     58         super(context);
     59 
     60         if (sDimAlpha == Integer.MIN_VALUE) {
     61             TypedValue outValue = new TypedValue();
     62             context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
     63             sDimAlpha = (int) (outValue.getFloat() * 255);
     64         }
     65 
     66         mCachedDevice = cachedDevice;
     67 
     68         if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
     69             setWidgetLayoutResource(R.layout.preference_bluetooth);
     70         }
     71 
     72         mCachedDevice.registerCallback(this);
     73 
     74         onDeviceAttributesChanged();
     75     }
     76 
     77     CachedBluetoothDevice getCachedDevice() {
     78         return mCachedDevice;
     79     }
     80 
     81     public void setOnSettingsClickListener(OnClickListener listener) {
     82         mOnSettingsClickListener = listener;
     83     }
     84 
     85     @Override
     86     protected void onPrepareForRemoval() {
     87         super.onPrepareForRemoval();
     88         mCachedDevice.unregisterCallback(this);
     89         if (mDisconnectDialog != null) {
     90             mDisconnectDialog.dismiss();
     91             mDisconnectDialog = null;
     92         }
     93     }
     94 
     95     public void onDeviceAttributesChanged() {
     96         /*
     97          * The preference framework takes care of making sure the value has
     98          * changed before proceeding. It will also call notifyChanged() if
     99          * any preference info has changed from the previous value.
    100          */
    101         setTitle(mCachedDevice.getName());
    102 
    103         int summaryResId = getConnectionSummary();
    104         if (summaryResId != 0) {
    105             setSummary(summaryResId);
    106         } else {
    107             setSummary(null);   // empty summary for unpaired devices
    108         }
    109 
    110         int iconResId = getBtClassDrawable();
    111         if (iconResId != 0) {
    112             setIcon(iconResId);
    113         }
    114 
    115         // Used to gray out the item
    116         setEnabled(!mCachedDevice.isBusy());
    117 
    118         // This could affect ordering, so notify that
    119         notifyHierarchyChanged();
    120     }
    121 
    122     @Override
    123     protected void onBindView(View view) {
    124         // Disable this view if the bluetooth enable/disable preference view is off
    125         if (null != findPreferenceInHierarchy("bt_checkbox")) {
    126             setDependency("bt_checkbox");
    127         }
    128 
    129         if (mCachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
    130             ImageView deviceDetails = (ImageView) view.findViewById(R.id.deviceDetails);
    131             if (deviceDetails != null) {
    132                 deviceDetails.setOnClickListener(this);
    133                 deviceDetails.setTag(mCachedDevice);
    134                 deviceDetails.setAlpha(isEnabled() ? 255 : sDimAlpha);
    135             }
    136         }
    137 
    138         super.onBindView(view);
    139     }
    140 
    141     public void onClick(View v) {
    142         // Should never be null by construction
    143         if (mOnSettingsClickListener != null) {
    144             mOnSettingsClickListener.onClick(v);
    145         }
    146     }
    147 
    148     @Override
    149     public boolean equals(Object o) {
    150         if ((o == null) || !(o instanceof BluetoothDevicePreference)) {
    151             return false;
    152         }
    153         return mCachedDevice.equals(
    154                 ((BluetoothDevicePreference) o).mCachedDevice);
    155     }
    156 
    157     @Override
    158     public int hashCode() {
    159         return mCachedDevice.hashCode();
    160     }
    161 
    162     @Override
    163     public int compareTo(Preference another) {
    164         if (!(another instanceof BluetoothDevicePreference)) {
    165             // Rely on default sort
    166             return super.compareTo(another);
    167         }
    168 
    169         return mCachedDevice
    170                 .compareTo(((BluetoothDevicePreference) another).mCachedDevice);
    171     }
    172 
    173     void onClicked() {
    174         int bondState = mCachedDevice.getBondState();
    175 
    176         if (mCachedDevice.isConnected()) {
    177             askDisconnect();
    178         } else if (bondState == BluetoothDevice.BOND_BONDED) {
    179             mCachedDevice.connect(true);
    180         } else if (bondState == BluetoothDevice.BOND_NONE) {
    181             pair();
    182         }
    183     }
    184 
    185     // Show disconnect confirmation dialog for a device.
    186     private void askDisconnect() {
    187         Context context = getContext();
    188         String name = mCachedDevice.getName();
    189         if (TextUtils.isEmpty(name)) {
    190             name = context.getString(R.string.bluetooth_device);
    191         }
    192         String message = context.getString(R.string.bluetooth_disconnect_all_profiles, name);
    193         String title = context.getString(R.string.bluetooth_disconnect_title);
    194 
    195         DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() {
    196             public void onClick(DialogInterface dialog, int which) {
    197                 mCachedDevice.disconnect();
    198             }
    199         };
    200 
    201         mDisconnectDialog = Utils.showDisconnectDialog(context,
    202                 mDisconnectDialog, disconnectListener, title, Html.fromHtml(message));
    203     }
    204 
    205     private void pair() {
    206         if (!mCachedDevice.startPairing()) {
    207             Utils.showError(getContext(), mCachedDevice.getName(),
    208                     R.string.bluetooth_pairing_error_message);
    209         }
    210     }
    211 
    212     private int getConnectionSummary() {
    213         final CachedBluetoothDevice cachedDevice = mCachedDevice;
    214 
    215         boolean profileConnected = false;       // at least one profile is connected
    216         boolean a2dpNotConnected = false;       // A2DP is preferred but not connected
    217         boolean headsetNotConnected = false;    // Headset is preferred but not connected
    218 
    219         for (LocalBluetoothProfile profile : cachedDevice.getProfiles()) {
    220             int connectionStatus = cachedDevice.getProfileConnectionState(profile);
    221 
    222             switch (connectionStatus) {
    223                 case BluetoothProfile.STATE_CONNECTING:
    224                 case BluetoothProfile.STATE_DISCONNECTING:
    225                     return Utils.getConnectionStateSummary(connectionStatus);
    226 
    227                 case BluetoothProfile.STATE_CONNECTED:
    228                     profileConnected = true;
    229                     break;
    230 
    231                 case BluetoothProfile.STATE_DISCONNECTED:
    232                     if (profile.isProfileReady() && profile.isPreferred(cachedDevice.getDevice())) {
    233                         if (profile instanceof A2dpProfile) {
    234                             a2dpNotConnected = true;
    235                         } else if (profile instanceof HeadsetProfile) {
    236                             headsetNotConnected = true;
    237                         }
    238                     }
    239                     break;
    240             }
    241         }
    242 
    243         if (profileConnected) {
    244             if (a2dpNotConnected && headsetNotConnected) {
    245                 return R.string.bluetooth_connected_no_headset_no_a2dp;
    246             } else if (a2dpNotConnected) {
    247                 return R.string.bluetooth_connected_no_a2dp;
    248             } else if (headsetNotConnected) {
    249                 return R.string.bluetooth_connected_no_headset;
    250             } else {
    251                 return R.string.bluetooth_connected;
    252             }
    253         }
    254 
    255         switch (cachedDevice.getBondState()) {
    256             case BluetoothDevice.BOND_BONDING:
    257                 return R.string.bluetooth_pairing;
    258 
    259             case BluetoothDevice.BOND_BONDED:
    260             case BluetoothDevice.BOND_NONE:
    261             default:
    262                 return 0;
    263         }
    264     }
    265 
    266     private int getBtClassDrawable() {
    267         BluetoothClass btClass = mCachedDevice.getBtClass();
    268         if (btClass != null) {
    269             switch (btClass.getMajorDeviceClass()) {
    270                 case BluetoothClass.Device.Major.COMPUTER:
    271                     return R.drawable.ic_bt_laptop;
    272 
    273                 case BluetoothClass.Device.Major.PHONE:
    274                     return R.drawable.ic_bt_cellphone;
    275 
    276                 case BluetoothClass.Device.Major.PERIPHERAL:
    277                     return HidProfile.getHidClassDrawable(btClass);
    278 
    279                 case BluetoothClass.Device.Major.IMAGING:
    280                     return R.drawable.ic_bt_imaging;
    281 
    282                 default:
    283                     // unrecognized device class; continue
    284             }
    285         } else {
    286             Log.w(TAG, "mBtClass is null");
    287         }
    288 
    289         List<LocalBluetoothProfile> profiles = mCachedDevice.getProfiles();
    290         for (LocalBluetoothProfile profile : profiles) {
    291             int resId = profile.getDrawableResource(btClass);
    292             if (resId != 0) {
    293                 return resId;
    294             }
    295         }
    296         if (btClass != null) {
    297             if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
    298                 return R.drawable.ic_bt_headphones_a2dp;
    299 
    300             }
    301             if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
    302                 return R.drawable.ic_bt_headset_hfp;
    303             }
    304         }
    305         return 0;
    306     }
    307 }
    308