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