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