• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice)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 
getCachedDevice()77     CachedBluetoothDevice getCachedDevice() {
78         return mCachedDevice;
79     }
80 
setOnSettingsClickListener(OnClickListener listener)81     public void setOnSettingsClickListener(OnClickListener listener) {
82         mOnSettingsClickListener = listener;
83     }
84 
85     @Override
onPrepareForRemoval()86     protected void onPrepareForRemoval() {
87         super.onPrepareForRemoval();
88         mCachedDevice.unregisterCallback(this);
89         if (mDisconnectDialog != null) {
90             mDisconnectDialog.dismiss();
91             mDisconnectDialog = null;
92         }
93     }
94 
onDeviceAttributesChanged()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
onBindView(View view)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 
onClick(View v)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
equals(Object o)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
hashCode()158     public int hashCode() {
159         return mCachedDevice.hashCode();
160     }
161 
162     @Override
compareTo(Preference another)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 
onClicked()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.
askDisconnect()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 
pair()205     private void pair() {
206         if (!mCachedDevice.startPairing()) {
207             Utils.showError(getContext(), mCachedDevice.getName(),
208                     R.string.bluetooth_pairing_error_message);
209         }
210     }
211 
getConnectionSummary()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 
getBtClassDrawable()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