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