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