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