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.icon = ResourceIcon.get(state.value ? R.drawable.ic_qs_cast_on
    111                 : R.drawable.ic_qs_cast_off);
    112         mDetailAdapter.updateItems(devices);
    113     }
    114 
    115     @Override
    116     protected String composeChangeAnnouncement() {
    117         if (!mState.value) {
    118             // We only announce when it's turned off to avoid vocal overflow.
    119             return mContext.getString(R.string.accessibility_casting_turned_off);
    120         }
    121         return null;
    122     }
    123 
    124     private String getDeviceName(CastDevice device) {
    125         return device.name != null ? device.name
    126                 : mContext.getString(R.string.quick_settings_cast_device_default_name);
    127     }
    128 
    129     private final class Callback implements CastController.Callback, KeyguardMonitor.Callback {
    130         @Override
    131         public void onCastDevicesChanged() {
    132             refreshState();
    133         }
    134 
    135         @Override
    136         public void onKeyguardChanged() {
    137             refreshState();
    138         }
    139     };
    140 
    141     private final class CastDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
    142         private final LinkedHashMap<String, CastDevice> mVisibleOrder = new LinkedHashMap<>();
    143 
    144         private QSDetailItems mItems;
    145 
    146         @Override
    147         public int getTitle() {
    148             return R.string.quick_settings_cast_title;
    149         }
    150 
    151         @Override
    152         public Boolean getToggleState() {
    153             return null;
    154         }
    155 
    156         @Override
    157         public Intent getSettingsIntent() {
    158             return CAST_SETTINGS;
    159         }
    160 
    161         @Override
    162         public void setToggleState(boolean state) {
    163             // noop
    164         }
    165 
    166         @Override
    167         public View createDetailView(Context context, View convertView, ViewGroup parent) {
    168             mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
    169             mItems.setTagSuffix("Cast");
    170             if (convertView == null) {
    171                 if (DEBUG) Log.d(TAG, "addOnAttachStateChangeListener");
    172                 mItems.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
    173                     @Override
    174                     public void onViewAttachedToWindow(View v) {
    175                         if (DEBUG) Log.d(TAG, "onViewAttachedToWindow");
    176                     }
    177 
    178                     @Override
    179                     public void onViewDetachedFromWindow(View v) {
    180                         if (DEBUG) Log.d(TAG, "onViewDetachedFromWindow");
    181                         mVisibleOrder.clear();
    182                     }
    183                 });
    184             }
    185             mItems.setEmptyState(R.drawable.ic_qs_cast_detail_empty,
    186                     R.string.quick_settings_cast_detail_empty_text);
    187             mItems.setCallback(this);
    188             updateItems(mController.getCastDevices());
    189             mController.setDiscovering(true);
    190             return mItems;
    191         }
    192 
    193         private void updateItems(Set<CastDevice> devices) {
    194             if (mItems == null) return;
    195             Item[] items = null;
    196             if (devices != null && !devices.isEmpty()) {
    197                 // if we are connected, simply show that device
    198                 for (CastDevice device : devices) {
    199                     if (device.state == CastDevice.STATE_CONNECTED) {
    200                         final Item item = new Item();
    201                         item.icon = R.drawable.ic_qs_cast_on;
    202                         item.line1 = getDeviceName(device);
    203                         item.line2 = mContext.getString(R.string.quick_settings_connected);
    204                         item.tag = device;
    205                         item.canDisconnect = true;
    206                         items = new Item[] { item };
    207                         break;
    208                     }
    209                 }
    210                 // otherwise list all available devices, and don't move them around
    211                 if (items == null) {
    212                     for (CastDevice device : devices) {
    213                         mVisibleOrder.put(device.id, device);
    214                     }
    215                     items = new Item[devices.size()];
    216                     int i = 0;
    217                     for (String id : mVisibleOrder.keySet()) {
    218                         final CastDevice device = mVisibleOrder.get(id);
    219                         if (!devices.contains(device)) continue;
    220                         final Item item = new Item();
    221                         item.icon = R.drawable.ic_qs_cast_off;
    222                         item.line1 = getDeviceName(device);
    223                         if (device.state == CastDevice.STATE_CONNECTING) {
    224                             item.line2 = mContext.getString(R.string.quick_settings_connecting);
    225                         }
    226                         item.tag = device;
    227                         items[i++] = item;
    228                     }
    229                 }
    230             }
    231             mItems.setItems(items);
    232         }
    233 
    234         @Override
    235         public void onDetailItemClick(Item item) {
    236             if (item == null || item.tag == null) return;
    237             final CastDevice device = (CastDevice) item.tag;
    238             mController.startCasting(device);
    239         }
    240 
    241         @Override
    242         public void onDetailItemDisconnect(Item item) {
    243             if (item == null || item.tag == null) return;
    244             final CastDevice device = (CastDevice) item.tag;
    245             mController.stopCasting(device);
    246         }
    247     }
    248 }
    249