1 /* 2 * Copyright (C) 2014 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.systemui.qs.tiles; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.provider.Settings; 22 import android.text.TextUtils; 23 import android.view.View; 24 import android.view.ViewGroup; 25 26 import com.android.systemui.R; 27 import com.android.systemui.qs.QSDetailItems; 28 import com.android.systemui.qs.QSDetailItems.Item; 29 import com.android.systemui.qs.QSTile; 30 import com.android.systemui.statusbar.policy.BluetoothController; 31 import com.android.systemui.statusbar.policy.BluetoothController.PairedDevice; 32 33 import java.util.Set; 34 35 /** Quick settings tile: Bluetooth **/ 36 public class BluetoothTile extends QSTile<QSTile.BooleanState> { 37 private static final Intent BLUETOOTH_SETTINGS = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS); 38 39 private final BluetoothController mController; 40 private final BluetoothDetailAdapter mDetailAdapter; 41 42 public BluetoothTile(Host host) { 43 super(host); 44 mController = host.getBluetoothController(); 45 mDetailAdapter = new BluetoothDetailAdapter(); 46 } 47 48 @Override 49 public boolean supportsDualTargets() { 50 return true; 51 } 52 53 @Override 54 public DetailAdapter getDetailAdapter() { 55 return mDetailAdapter; 56 } 57 58 @Override 59 protected BooleanState newTileState() { 60 return new BooleanState(); 61 } 62 63 @Override 64 public void setListening(boolean listening) { 65 if (listening) { 66 mController.addStateChangedCallback(mCallback); 67 } else { 68 mController.removeStateChangedCallback(mCallback); 69 } 70 } 71 72 @Override 73 protected void handleClick() { 74 final boolean isEnabled = (Boolean)mState.value; 75 mController.setBluetoothEnabled(!isEnabled); 76 } 77 78 @Override 79 protected void handleSecondaryClick() { 80 if (!mState.value) { 81 mState.value = true; 82 mController.setBluetoothEnabled(true); 83 } 84 showDetail(true); 85 } 86 87 @Override 88 protected void handleUpdateState(BooleanState state, Object arg) { 89 final boolean supported = mController.isBluetoothSupported(); 90 final boolean enabled = mController.isBluetoothEnabled(); 91 final boolean connected = mController.isBluetoothConnected(); 92 final boolean connecting = mController.isBluetoothConnecting(); 93 state.visible = supported; 94 state.value = enabled; 95 state.autoMirrorDrawable = false; 96 if (enabled) { 97 state.label = null; 98 if (connected) { 99 state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connected); 100 state.contentDescription = mContext.getString( 101 R.string.accessibility_quick_settings_bluetooth_connected); 102 state.label = mController.getLastDeviceName(); 103 } else if (connecting) { 104 state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connecting); 105 state.contentDescription = mContext.getString( 106 R.string.accessibility_quick_settings_bluetooth_connecting); 107 state.label = mContext.getString(R.string.quick_settings_bluetooth_label); 108 } else { 109 state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on); 110 state.contentDescription = mContext.getString( 111 R.string.accessibility_quick_settings_bluetooth_on); 112 } 113 if (TextUtils.isEmpty(state.label)) { 114 state.label = mContext.getString(R.string.quick_settings_bluetooth_label); 115 } 116 } else { 117 state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_off); 118 state.label = mContext.getString(R.string.quick_settings_bluetooth_label); 119 state.contentDescription = mContext.getString( 120 R.string.accessibility_quick_settings_bluetooth_off); 121 } 122 123 String bluetoothName = state.label; 124 if (connected) { 125 bluetoothName = state.dualLabelContentDescription = mContext.getString( 126 R.string.accessibility_bluetooth_name, state.label); 127 } 128 state.dualLabelContentDescription = bluetoothName; 129 } 130 131 @Override 132 protected String composeChangeAnnouncement() { 133 if (mState.value) { 134 return mContext.getString(R.string.accessibility_quick_settings_bluetooth_changed_on); 135 } else { 136 return mContext.getString(R.string.accessibility_quick_settings_bluetooth_changed_off); 137 } 138 } 139 140 private final BluetoothController.Callback mCallback = new BluetoothController.Callback() { 141 @Override 142 public void onBluetoothStateChange(boolean enabled, boolean connecting) { 143 refreshState(); 144 } 145 @Override 146 public void onBluetoothPairedDevicesChanged() { 147 mUiHandler.post(new Runnable() { 148 @Override 149 public void run() { 150 mDetailAdapter.updateItems(); 151 } 152 }); 153 refreshState(); 154 } 155 }; 156 157 private final class BluetoothDetailAdapter implements DetailAdapter, QSDetailItems.Callback { 158 private QSDetailItems mItems; 159 160 @Override 161 public int getTitle() { 162 return R.string.quick_settings_bluetooth_label; 163 } 164 165 @Override 166 public Boolean getToggleState() { 167 return mState.value; 168 } 169 170 @Override 171 public Intent getSettingsIntent() { 172 return BLUETOOTH_SETTINGS; 173 } 174 175 @Override 176 public void setToggleState(boolean state) { 177 mController.setBluetoothEnabled(state); 178 showDetail(false); 179 } 180 181 @Override 182 public View createDetailView(Context context, View convertView, ViewGroup parent) { 183 mItems = QSDetailItems.convertOrInflate(context, convertView, parent); 184 mItems.setTagSuffix("Bluetooth"); 185 mItems.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty, 186 R.string.quick_settings_bluetooth_detail_empty_text); 187 mItems.setCallback(this); 188 mItems.setMinHeightInItems(0); 189 updateItems(); 190 setItemsVisible(mState.value); 191 return mItems; 192 } 193 194 public void setItemsVisible(boolean visible) { 195 if (mItems == null) return; 196 mItems.setItemsVisible(visible); 197 } 198 199 private void updateItems() { 200 if (mItems == null) return; 201 Item[] items = null; 202 final Set<PairedDevice> devices = mController.getPairedDevices(); 203 if (devices != null) { 204 items = new Item[devices.size()]; 205 int i = 0; 206 for (PairedDevice device : devices) { 207 final Item item = new Item(); 208 item.icon = R.drawable.ic_qs_bluetooth_on; 209 item.line1 = device.name; 210 if (device.state == PairedDevice.STATE_CONNECTED) { 211 item.icon = R.drawable.ic_qs_bluetooth_connected; 212 item.line2 = mContext.getString(R.string.quick_settings_connected); 213 item.canDisconnect = true; 214 } else if (device.state == PairedDevice.STATE_CONNECTING) { 215 item.icon = R.drawable.ic_qs_bluetooth_connecting; 216 item.line2 = mContext.getString(R.string.quick_settings_connecting); 217 } 218 item.tag = device; 219 items[i++] = item; 220 } 221 } 222 mItems.setItems(items); 223 } 224 225 @Override 226 public void onDetailItemClick(Item item) { 227 if (item == null || item.tag == null) return; 228 final PairedDevice device = (PairedDevice) item.tag; 229 if (device != null && device.state == PairedDevice.STATE_DISCONNECTED) { 230 mController.connect(device); 231 } 232 } 233 234 @Override 235 public void onDetailItemDisconnect(Item item) { 236 if (item == null || item.tag == null) return; 237 final PairedDevice device = (PairedDevice) item.tag; 238 if (device != null) { 239 mController.disconnect(device); 240 } 241 } 242 } 243 } 244