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