blob: fa84e33e875041e49172aa7d52652aecdac7d17c [file] [log] [blame]
John Spurlockaf8d6c42014-05-07 17:49:08 -04001/*
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
17package com.android.systemui.qs.tiles;
18
John Spurlock1e6eb172014-07-13 11:59:50 -040019import android.content.Context;
John Spurlockaf8d6c42014-05-07 17:49:08 -040020import android.content.Intent;
John Spurlockaf8d6c42014-05-07 17:49:08 -040021import android.provider.Settings;
John Spurlock1e6eb172014-07-13 11:59:50 -040022import android.util.Log;
John Spurlockaf8d6c42014-05-07 17:49:08 -040023import android.view.View;
John Spurlock1e6eb172014-07-13 11:59:50 -040024import android.view.View.OnAttachStateChangeListener;
25import android.view.ViewGroup;
Julia Reynolds20aef8a2016-05-04 16:44:08 -040026import android.widget.Button;
Chris Wrenf6e9228b2016-01-26 18:04:35 -050027
Chris Wren457a21c2015-05-06 17:50:34 -040028import com.android.internal.logging.MetricsLogger;
Tamas Berghammercbd3f0c2016-06-22 15:21:38 +010029import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
John Spurlockaf8d6c42014-05-07 17:49:08 -040030import com.android.systemui.R;
John Spurlock1e6eb172014-07-13 11:59:50 -040031import com.android.systemui.qs.QSDetailItems;
32import com.android.systemui.qs.QSDetailItems.Item;
John Spurlockaf8d6c42014-05-07 17:49:08 -040033import com.android.systemui.qs.QSTile;
34import com.android.systemui.statusbar.policy.CastController;
John Spurlock1e6eb172014-07-13 11:59:50 -040035import com.android.systemui.statusbar.policy.CastController.CastDevice;
John Spurlock657c62c2014-07-22 12:18:09 -040036import com.android.systemui.statusbar.policy.KeyguardMonitor;
John Spurlock1e6eb172014-07-13 11:59:50 -040037
38import java.util.LinkedHashMap;
39import java.util.Set;
John Spurlockaf8d6c42014-05-07 17:49:08 -040040
41/** Quick settings tile: Cast **/
42public class CastTile extends QSTile<QSTile.BooleanState> {
Jeff Brown18492622014-08-19 17:51:47 -070043 private static final Intent CAST_SETTINGS =
44 new Intent(Settings.ACTION_CAST_SETTINGS);
John Spurlockaf8d6c42014-05-07 17:49:08 -040045
46 private final CastController mController;
John Spurlock1e6eb172014-07-13 11:59:50 -040047 private final CastDetailAdapter mDetailAdapter;
John Spurlock657c62c2014-07-22 12:18:09 -040048 private final KeyguardMonitor mKeyguard;
49 private final Callback mCallback = new Callback();
John Spurlockaf8d6c42014-05-07 17:49:08 -040050
John Spurlockaf8d6c42014-05-07 17:49:08 -040051 public CastTile(Host host) {
52 super(host);
53 mController = host.getCastController();
John Spurlock1e6eb172014-07-13 11:59:50 -040054 mDetailAdapter = new CastDetailAdapter();
John Spurlock657c62c2014-07-22 12:18:09 -040055 mKeyguard = host.getKeyguardMonitor();
John Spurlock1e6eb172014-07-13 11:59:50 -040056 }
57
58 @Override
59 public DetailAdapter getDetailAdapter() {
60 return mDetailAdapter;
John Spurlockaf8d6c42014-05-07 17:49:08 -040061 }
62
63 @Override
Jason Monk62b63a02016-02-02 15:15:31 -050064 public BooleanState newTileState() {
John Spurlockaf8d6c42014-05-07 17:49:08 -040065 return new BooleanState();
66 }
67
68 @Override
John Spurlockccb6b9a2014-05-17 15:54:40 -040069 public void setListening(boolean listening) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040070 if (mController == null) return;
John Spurlock1e6eb172014-07-13 11:59:50 -040071 if (DEBUG) Log.d(TAG, "setListening " + listening);
John Spurlockccb6b9a2014-05-17 15:54:40 -040072 if (listening) {
73 mController.addCallback(mCallback);
John Spurlock657c62c2014-07-22 12:18:09 -040074 mKeyguard.addCallback(mCallback);
John Spurlockccb6b9a2014-05-17 15:54:40 -040075 } else {
John Spurlock1e6eb172014-07-13 11:59:50 -040076 mController.setDiscovering(false);
John Spurlockccb6b9a2014-05-17 15:54:40 -040077 mController.removeCallback(mCallback);
John Spurlock657c62c2014-07-22 12:18:09 -040078 mKeyguard.removeCallback(mCallback);
John Spurlockccb6b9a2014-05-17 15:54:40 -040079 }
John Spurlockaf8d6c42014-05-07 17:49:08 -040080 }
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
Jason Monk76c67aa2016-02-19 14:49:42 -050090 public Intent getLongClickIntent() {
91 return new Intent(Settings.ACTION_CAST_SETTINGS);
92 }
93
94 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -040095 protected void handleClick() {
Jason Monkba2318e2015-12-08 09:04:23 -050096 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 }
Chris Wren9e7283f2015-05-08 17:23:47 -0400107 MetricsLogger.action(mContext, getMetricsCategory());
John Spurlock1e6eb172014-07-13 11:59:50 -0400108 showDetail(true);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400109 }
110
111 @Override
Jason Monk39c98e62016-03-16 09:18:35 -0400112 public CharSequence getTileLabel() {
113 return mContext.getString(R.string.quick_settings_cast_title);
114 }
115
116 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400117 protected void handleUpdateState(BooleanState state, Object arg) {
John Spurlock1e6eb172014-07-13 11:59:50 -0400118 state.label = mContext.getString(R.string.quick_settings_cast_title);
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400119 state.contentDescription = state.label;
John Spurlock1e6eb172014-07-13 11:59:50 -0400120 state.value = false;
Selim Cinek06d3bca2014-08-26 17:29:20 +0200121 state.autoMirrorDrawable = false;
John Spurlock1e6eb172014-07-13 11:59:50 -0400122 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);
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400128 state.contentDescription = state.contentDescription + "," +
129 mContext.getString(R.string.accessibility_cast_name, state.label);
John Spurlock1e6eb172014-07-13 11:59:50 -0400130 } else if (device.state == CastDevice.STATE_CONNECTING) {
131 connecting = true;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400132 }
133 }
John Spurlock1e6eb172014-07-13 11:59:50 -0400134 if (!state.value && connecting) {
135 state.label = mContext.getString(R.string.quick_settings_connecting);
136 }
John Spurlock2d695812014-10-30 13:25:21 -0400137 state.icon = ResourceIcon.get(state.value ? R.drawable.ic_qs_cast_on
138 : R.drawable.ic_qs_cast_off);
John Spurlock1e6eb172014-07-13 11:59:50 -0400139 mDetailAdapter.updateItems(devices);
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400140 state.minimalAccessibilityClassName = state.expandedAccessibilityClassName =
141 Button.class.getName();
142 state.contentDescription = state.contentDescription + ","
143 + mContext.getString(R.string.accessibility_quick_settings_open_details);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400144 }
145
Selim Cinek4fda7b22014-08-18 22:07:25 +0200146 @Override
Chris Wren457a21c2015-05-06 17:50:34 -0400147 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500148 return MetricsEvent.QS_CAST;
Chris Wren457a21c2015-05-06 17:50:34 -0400149 }
150
151 @Override
Selim Cinek4fda7b22014-08-18 22:07:25 +0200152 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
John Spurlock1e6eb172014-07-13 11:59:50 -0400160 private String getDeviceName(CastDevice device) {
161 return device.name != null ? device.name
162 : mContext.getString(R.string.quick_settings_cast_device_default_name);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400163 }
164
John Spurlock657c62c2014-07-22 12:18:09 -0400165 private final class Callback implements CastController.Callback, KeyguardMonitor.Callback {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400166 @Override
John Spurlock1e6eb172014-07-13 11:59:50 -0400167 public void onCastDevicesChanged() {
168 refreshState();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400169 }
John Spurlock657c62c2014-07-22 12:18:09 -0400170
171 @Override
172 public void onKeyguardChanged() {
173 refreshState();
174 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400175 };
John Spurlock76c43b92014-05-13 21:10:51 -0400176
John Spurlock1e6eb172014-07-13 11:59:50 -0400177 private final class CastDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
178 private final LinkedHashMap<String, CastDevice> mVisibleOrder = new LinkedHashMap<>();
179
180 private QSDetailItems mItems;
181
John Spurlock76c43b92014-05-13 21:10:51 -0400182 @Override
Jason Monkc06fbb12016-01-08 14:12:18 -0500183 public CharSequence getTitle() {
184 return mContext.getString(R.string.quick_settings_cast_title);
John Spurlock76c43b92014-05-13 21:10:51 -0400185 }
John Spurlock1e6eb172014-07-13 11:59:50 -0400186
187 @Override
188 public Boolean getToggleState() {
189 return null;
190 }
191
192 @Override
193 public Intent getSettingsIntent() {
Jeff Brown18492622014-08-19 17:51:47 -0700194 return CAST_SETTINGS;
John Spurlock1e6eb172014-07-13 11:59:50 -0400195 }
196
197 @Override
198 public void setToggleState(boolean state) {
199 // noop
200 }
201
202 @Override
Chris Wren457a21c2015-05-06 17:50:34 -0400203 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500204 return MetricsEvent.QS_CAST_DETAILS;
Chris Wren457a21c2015-05-06 17:50:34 -0400205 }
206
207 @Override
John Spurlock1e6eb172014-07-13 11:59:50 -0400208 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;
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500278 MetricsLogger.action(mContext, MetricsEvent.QS_CAST_SELECT);
John Spurlock1e6eb172014-07-13 11:59:50 -0400279 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;
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500286 MetricsLogger.action(mContext, MetricsEvent.QS_CAST_DISCONNECT);
John Spurlock78b8c8f2014-08-25 17:52:06 -0400287 final CastDevice device = (CastDevice) item.tag;
288 mController.stopCasting(device);
John Spurlock1e6eb172014-07-13 11:59:50 -0400289 }
290 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400291}