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.content.Context;
     20 import android.content.Intent;
     21 import android.provider.Settings;
     22 import android.util.Log;
     23 import android.view.View;
     24 import android.view.View.OnAttachStateChangeListener;
     25 import android.view.ViewGroup;
     26 
     27 import com.android.systemui.R;
     28 import com.android.systemui.qs.QSDetailItems;
     29 import com.android.systemui.qs.QSDetailItems.Item;
     30 import com.android.systemui.qs.QSTile;
     31 import com.android.systemui.statusbar.policy.CastController;
     32 import com.android.systemui.statusbar.policy.CastController.CastDevice;
     33 import com.android.systemui.statusbar.policy.KeyguardMonitor;
     34 
     35 import java.util.LinkedHashMap;
     36 import java.util.Set;
     37 
     38 /** Quick settings tile: Cast **/
     39 public class CastTile extends QSTile<QSTile.BooleanState> {
     40     private static final Intent CAST_SETTINGS =
     41             new Intent(Settings.ACTION_CAST_SETTINGS);
     42 
     43     private final CastController mController;
     44     private final CastDetailAdapter mDetailAdapter;
     45     private final KeyguardMonitor mKeyguard;
     46     private final Callback mCallback = new Callback();
     47 
     48     public CastTile(Host host) {
     49         super(host);
     50         mController = host.getCastController();
     51         mDetailAdapter = new CastDetailAdapter();
     52         mKeyguard = host.getKeyguardMonitor();
     53     }
     54 
     55     @Override
     56     public DetailAdapter getDetailAdapter() {
     57         return mDetailAdapter;
     58     }
     59 
     60     @Override
     61     protected BooleanState newTileState() {
     62         return new BooleanState();
     63     }
     64 
     65     @Override
     66     public void setListening(boolean listening) {
     67         if (mController == null) return;
     68         if (DEBUG) Log.d(TAG, "setListening " + listening);
     69         if (listening) {
     70             mController.addCallback(mCallback);
     71             mKeyguard.addCallback(mCallback);
     72         } else {
     73             mController.setDiscovering(false);
     74             mController.removeCallback(mCallback);
     75             mKeyguard.removeCallback(mCallback);
     76         }
     77     }
     78 
     79     @Override
     80     protected void handleUserSwitch(int newUserId) {
     81         super.handleUserSwitch(newUserId);
     82         if (mController == null) return;
     83         mController.setCurrentUserId(newUserId);
     84     }
     85 
     86     @Override
     87     protected void handleClick() {
     88         showDetail(true);
     89     }
     90 
     91     @Override
     92     protected void handleUpdateState(BooleanState state, Object arg) {
     93         state.visible = !(mKeyguard.isSecure() && mKeyguard.isShowing());
     94         state.label = mContext.getString(R.string.quick_settings_cast_title);
     95         state.value = false;
     96         state.autoMirrorDrawable = false;
     97         final Set<CastDevice> devices = mController.getCastDevices();
     98         boolean connecting = false;
     99         for (CastDevice device : devices) {
    100             if (device.state == CastDevice.STATE_CONNECTED) {
    101                 state.value = true;
    102                 state.label = getDeviceName(device);
    103             } else if (device.state == CastDevice.STATE_CONNECTING) {
    104                 connecting = true;
    105             }
    106         }
    107         if (!state.value && connecting) {
    108             state.label = mContext.getString(R.string.quick_settings_connecting);
    109         }
    110         state.iconId = state.value ? R.drawable.ic_qs_cast_on : R.drawable.ic_qs_cast_off;
    111         mDetailAdapter.updateItems(devices);
    112     }
    113 
    114     @Override
    115     protected String composeChangeAnnouncement() {
    116         if (!mState.value) {
    117             // We only announce when it's turned off to avoid vocal overflow.
    118             return mContext.getString(R.string.accessibility_casting_turned_off);
    119         }
    120         return null;
    121     }
    122 
    123     private String getDeviceName(CastDevice device) {
    124         return device.name != null ? device.name
    125                 : mContext.getString(R.string.quick_settings_cast_device_default_name);
    126     }
    127 
    128     private final class Callback implements CastController.Callback, KeyguardMonitor.Callback {
    129         @Override
    130         public void onCastDevicesChanged() {
    131             refreshState();
    132         }
    133 
    134         @Override
    135         public void onKeyguardChanged() {
    136             refreshState();
    137         }
    138     };
    139 
    140     private final class CastDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
    141         private final LinkedHashMap<String, CastDevice> mVisibleOrder = new LinkedHashMap<>();
    142 
    143         private QSDetailItems mItems;
    144 
    145         @Override
    146         public int getTitle() {
    147             return R.string.quick_settings_cast_title;
    148         }
    149 
    150         @Override
    151         public Boolean getToggleState() {
    152             return null;
    153         }
    154 
    155         @Override
    156         public Intent getSettingsIntent() {
    157             return CAST_SETTINGS;
    158         }
    159 
    160         @Override
    161         public void setToggleState(boolean state) {
    162             // noop
    163         }
    164 
    165         @Override
    166         public View createDetailView(Context context, View convertView, ViewGroup parent) {
    167             mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
    168             mItems.setTagSuffix("Cast");
    169             if (convertView == null) {
    170                 if (DEBUG) Log.d(TAG, "addOnAttachStateChangeListener");
    171                 mItems.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
    172                     @Override
    173                     public void onViewAttachedToWindow(View v) {
    174                         if (DEBUG) Log.d(TAG, "onViewAttachedToWindow");
    175                     }
    176 
    177                     @Override
    178                     public void onViewDetachedFromWindow(View v) {
    179                         if (DEBUG) Log.d(TAG, "onViewDetachedFromWindow");
    180                         mVisibleOrder.clear();
    181                     }
    182                 });
    183             }
    184             mItems.setEmptyState(R.drawable.ic_qs_cast_detail_empty,
    185                     R.string.quick_settings_cast_detail_empty_text);
    186             mItems.setCallback(this);
    187             updateItems(mController.getCastDevices());
    188             mController.setDiscovering(true);
    189             return mItems;
    190         }
    191 
    192         private void updateItems(Set<CastDevice> devices) {
    193             if (mItems == null) return;
    194             Item[] items = null;
    195             if (devices != null && !devices.isEmpty()) {
    196                 // if we are connected, simply show that device
    197                 for (CastDevice device : devices) {
    198                     if (device.state == CastDevice.STATE_CONNECTED) {
    199                         final Item item = new Item();
    200                         item.icon = R.drawable.ic_qs_cast_on;
    201                         item.line1 = getDeviceName(device);
    202                         item.line2 = mContext.getString(R.string.quick_settings_connected);
    203                         item.tag = device;
    204                         item.canDisconnect = true;
    205                         items = new Item[] { item };
    206                         break;
    207                     }
    208                 }
    209                 // otherwise list all available devices, and don't move them around
    210                 if (items == null) {
    211                     for (CastDevice device : devices) {
    212                         mVisibleOrder.put(device.id, device);
    213                     }
    214                     items = new Item[devices.size()];
    215                     int i = 0;
    216                     for (String id : mVisibleOrder.keySet()) {
    217                         final CastDevice device = mVisibleOrder.get(id);
    218                         if (!devices.contains(device)) continue;
    219                         final Item item = new Item();
    220                         item.icon = R.drawable.ic_qs_cast_off;
    221                         item.line1 = getDeviceName(device);
    222                         if (device.state == CastDevice.STATE_CONNECTING) {
    223                             item.line2 = mContext.getString(R.string.quick_settings_connecting);
    224                         }
    225                         item.tag = device;
    226                         items[i++] = item;
    227                     }
    228                 }
    229             }
    230             mItems.setItems(items);
    231         }
    232 
    233         @Override
    234         public void onDetailItemClick(Item item) {
    235             if (item == null || item.tag == null) return;
    236             final CastDevice device = (CastDevice) item.tag;
    237             mController.startCasting(device);
    238         }
    239 
    240         @Override
    241         public void onDetailItemDisconnect(Item item) {
    242             if (item == null || item.tag == null) return;
    243             final CastDevice device = (CastDevice) item.tag;
    244             mController.stopCasting(device);
    245         }
    246     }
    247 }
    248