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 17package com.android.settings.bluetooth; 18 19import android.app.AlertDialog; 20import android.bluetooth.BluetoothDevice; 21import android.content.Context; 22import android.content.DialogInterface; 23import android.content.res.Resources; 24import android.graphics.drawable.Drawable; 25import android.os.UserManager; 26import android.support.v7.preference.Preference; 27import android.support.v7.preference.PreferenceViewHolder; 28import android.text.Html; 29import android.text.TextUtils; 30import android.util.Pair; 31import android.util.TypedValue; 32import android.widget.ImageView; 33 34import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 35import com.android.settings.R; 36import com.android.settings.overlay.FeatureFactory; 37import com.android.settings.widget.GearPreference; 38import com.android.settingslib.bluetooth.CachedBluetoothDevice; 39import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 40 41import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH; 42 43/** 44 * BluetoothDevicePreference is the preference type used to display each remote 45 * Bluetooth device in the Bluetooth Settings screen. 46 */ 47public final class BluetoothDevicePreference extends GearPreference implements 48 CachedBluetoothDevice.Callback { 49 private static final String TAG = "BluetoothDevicePref"; 50 51 private static int sDimAlpha = Integer.MIN_VALUE; 52 53 private final CachedBluetoothDevice mCachedDevice; 54 private final UserManager mUserManager; 55 private final boolean mShowDevicesWithoutNames; 56 57 private AlertDialog mDisconnectDialog; 58 private String contentDescription = null; 59 private boolean mHideSecondTarget = false; 60 /* Talk-back descriptions for various BT icons */ 61 Resources mResources; 62 63 public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice, 64 boolean showDeviceWithoutNames) { 65 super(context, null); 66 mResources = getContext().getResources(); 67 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 68 mShowDevicesWithoutNames = showDeviceWithoutNames; 69 70 if (sDimAlpha == Integer.MIN_VALUE) { 71 TypedValue outValue = new TypedValue(); 72 context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true); 73 sDimAlpha = (int) (outValue.getFloat() * 255); 74 } 75 76 mCachedDevice = cachedDevice; 77 mCachedDevice.registerCallback(this); 78 79 onDeviceAttributesChanged(); 80 } 81 82 void rebind() { 83 notifyChanged(); 84 } 85 86 @Override 87 protected boolean shouldHideSecondTarget() { 88 return mCachedDevice == null 89 || mCachedDevice.getBondState() != BluetoothDevice.BOND_BONDED 90 || mUserManager.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH) 91 || mHideSecondTarget; 92 } 93 94 @Override 95 protected int getSecondTargetResId() { 96 return R.layout.preference_widget_gear; 97 } 98 99 CachedBluetoothDevice getCachedDevice() { 100 return mCachedDevice; 101 } 102 103 @Override 104 protected void onPrepareForRemoval() { 105 super.onPrepareForRemoval(); 106 mCachedDevice.unregisterCallback(this); 107 if (mDisconnectDialog != null) { 108 mDisconnectDialog.dismiss(); 109 mDisconnectDialog = null; 110 } 111 } 112 113 public CachedBluetoothDevice getBluetoothDevice() { 114 return mCachedDevice; 115 } 116 117 public void hideSecondTarget(boolean hideSecondTarget) { 118 mHideSecondTarget = hideSecondTarget; 119 } 120 121 public void onDeviceAttributesChanged() { 122 /* 123 * The preference framework takes care of making sure the value has 124 * changed before proceeding. It will also call notifyChanged() if 125 * any preference info has changed from the previous value. 126 */ 127 setTitle(mCachedDevice.getName()); 128 // Null check is done at the framework 129 setSummary(mCachedDevice.getConnectionSummary()); 130 131 final Pair<Drawable, String> pair = com.android.settingslib.bluetooth.Utils 132 .getBtClassDrawableWithDescription(getContext(), mCachedDevice); 133 if (pair.first != null) { 134 setIcon(pair.first); 135 contentDescription = pair.second; 136 } 137 138 // Used to gray out the item 139 setEnabled(!mCachedDevice.isBusy()); 140 141 // Device is only visible in the UI if it has a valid name besides MAC address or when user 142 // allows showing devices without user-friendly name in developer settings 143 setVisible(mShowDevicesWithoutNames || mCachedDevice.hasHumanReadableName()); 144 145 // This could affect ordering, so notify that 146 notifyHierarchyChanged(); 147 } 148 149 @Override 150 public void onBindViewHolder(PreferenceViewHolder view) { 151 // Disable this view if the bluetooth enable/disable preference view is off 152 if (null != findPreferenceInHierarchy("bt_checkbox")) { 153 setDependency("bt_checkbox"); 154 } 155 156 if (mCachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) { 157 ImageView deviceDetails = (ImageView) view.findViewById(R.id.settings_button); 158 159 if (deviceDetails != null) { 160 deviceDetails.setOnClickListener(this); 161 } 162 } 163 final ImageView imageView = (ImageView) view.findViewById(android.R.id.icon); 164 if (imageView != null) { 165 imageView.setContentDescription(contentDescription); 166 } 167 super.onBindViewHolder(view); 168 } 169 170 @Override 171 public boolean equals(Object o) { 172 if ((o == null) || !(o instanceof BluetoothDevicePreference)) { 173 return false; 174 } 175 return mCachedDevice.equals( 176 ((BluetoothDevicePreference) o).mCachedDevice); 177 } 178 179 @Override 180 public int hashCode() { 181 return mCachedDevice.hashCode(); 182 } 183 184 @Override 185 public int compareTo(Preference another) { 186 if (!(another instanceof BluetoothDevicePreference)) { 187 // Rely on default sort 188 return super.compareTo(another); 189 } 190 191 return mCachedDevice 192 .compareTo(((BluetoothDevicePreference) another).mCachedDevice); 193 } 194 195 void onClicked() { 196 Context context = getContext(); 197 int bondState = mCachedDevice.getBondState(); 198 199 final MetricsFeatureProvider metricsFeatureProvider = 200 FeatureFactory.getFactory(context).getMetricsFeatureProvider(); 201 202 if (mCachedDevice.isConnected()) { 203 metricsFeatureProvider.action(context, 204 MetricsEvent.ACTION_SETTINGS_BLUETOOTH_DISCONNECT); 205 askDisconnect(); 206 } else if (bondState == BluetoothDevice.BOND_BONDED) { 207 metricsFeatureProvider.action(context, 208 MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT); 209 mCachedDevice.connect(true); 210 } else if (bondState == BluetoothDevice.BOND_NONE) { 211 metricsFeatureProvider.action(context, 212 MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR); 213 if (!mCachedDevice.hasHumanReadableName()) { 214 metricsFeatureProvider.action(context, 215 MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES); 216 } 217 pair(); 218 } 219 } 220 221 // Show disconnect confirmation dialog for a device. 222 private void askDisconnect() { 223 Context context = getContext(); 224 String name = mCachedDevice.getName(); 225 if (TextUtils.isEmpty(name)) { 226 name = context.getString(R.string.bluetooth_device); 227 } 228 String message = context.getString(R.string.bluetooth_disconnect_all_profiles, name); 229 String title = context.getString(R.string.bluetooth_disconnect_title); 230 231 DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() { 232 public void onClick(DialogInterface dialog, int which) { 233 mCachedDevice.disconnect(); 234 } 235 }; 236 237 mDisconnectDialog = Utils.showDisconnectDialog(context, 238 mDisconnectDialog, disconnectListener, title, Html.fromHtml(message)); 239 } 240 241 private void pair() { 242 if (!mCachedDevice.startPairing()) { 243 Utils.showError(getContext(), mCachedDevice.getName(), 244 R.string.bluetooth_pairing_error_message); 245 } 246 } 247 248} 249