blob: d61e2f2bdac056497e1686a4893f912fe87c8ed8 [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;
Jason Monk32508852017-01-18 09:17:13 -050022import android.service.quicksettings.Tile;
John Spurlock1e6eb172014-07-13 11:59:50 -040023import android.util.Log;
John Spurlockaf8d6c42014-05-07 17:49:08 -040024import android.view.View;
John Spurlock1e6eb172014-07-13 11:59:50 -040025import android.view.View.OnAttachStateChangeListener;
26import android.view.ViewGroup;
Julia Reynolds20aef8a2016-05-04 16:44:08 -040027import android.widget.Button;
Chris Wrenf6e9228b2016-01-26 18:04:35 -050028
Chris Wren457a21c2015-05-06 17:50:34 -040029import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010030import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
John Spurlockaf8d6c42014-05-07 17:49:08 -040031import com.android.systemui.R;
Jason Monk0ceef212016-11-02 14:05:23 -040032import com.android.systemui.plugins.qs.QS.DetailAdapter;
John Spurlock1e6eb172014-07-13 11:59:50 -040033import com.android.systemui.qs.QSDetailItems;
34import com.android.systemui.qs.QSDetailItems.Item;
John Spurlockaf8d6c42014-05-07 17:49:08 -040035import com.android.systemui.qs.QSTile;
36import com.android.systemui.statusbar.policy.CastController;
John Spurlock1e6eb172014-07-13 11:59:50 -040037import com.android.systemui.statusbar.policy.CastController.CastDevice;
John Spurlock657c62c2014-07-22 12:18:09 -040038import com.android.systemui.statusbar.policy.KeyguardMonitor;
John Spurlock1e6eb172014-07-13 11:59:50 -040039
40import java.util.LinkedHashMap;
41import java.util.Set;
John Spurlockaf8d6c42014-05-07 17:49:08 -040042
43/** Quick settings tile: Cast **/
44public class CastTile extends QSTile<QSTile.BooleanState> {
Jeff Brown18492622014-08-19 17:51:47 -070045 private static final Intent CAST_SETTINGS =
46 new Intent(Settings.ACTION_CAST_SETTINGS);
John Spurlockaf8d6c42014-05-07 17:49:08 -040047
48 private final CastController mController;
John Spurlock1e6eb172014-07-13 11:59:50 -040049 private final CastDetailAdapter mDetailAdapter;
John Spurlock657c62c2014-07-22 12:18:09 -040050 private final KeyguardMonitor mKeyguard;
51 private final Callback mCallback = new Callback();
John Spurlockaf8d6c42014-05-07 17:49:08 -040052
John Spurlockaf8d6c42014-05-07 17:49:08 -040053 public CastTile(Host host) {
54 super(host);
55 mController = host.getCastController();
John Spurlock1e6eb172014-07-13 11:59:50 -040056 mDetailAdapter = new CastDetailAdapter();
John Spurlock657c62c2014-07-22 12:18:09 -040057 mKeyguard = host.getKeyguardMonitor();
John Spurlock1e6eb172014-07-13 11:59:50 -040058 }
59
60 @Override
61 public DetailAdapter getDetailAdapter() {
62 return mDetailAdapter;
John Spurlockaf8d6c42014-05-07 17:49:08 -040063 }
64
65 @Override
Jason Monk62b63a02016-02-02 15:15:31 -050066 public BooleanState newTileState() {
John Spurlockaf8d6c42014-05-07 17:49:08 -040067 return new BooleanState();
68 }
69
70 @Override
John Spurlockccb6b9a2014-05-17 15:54:40 -040071 public void setListening(boolean listening) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040072 if (mController == null) return;
John Spurlock1e6eb172014-07-13 11:59:50 -040073 if (DEBUG) Log.d(TAG, "setListening " + listening);
John Spurlockccb6b9a2014-05-17 15:54:40 -040074 if (listening) {
75 mController.addCallback(mCallback);
John Spurlock657c62c2014-07-22 12:18:09 -040076 mKeyguard.addCallback(mCallback);
John Spurlockccb6b9a2014-05-17 15:54:40 -040077 } else {
John Spurlock1e6eb172014-07-13 11:59:50 -040078 mController.setDiscovering(false);
John Spurlockccb6b9a2014-05-17 15:54:40 -040079 mController.removeCallback(mCallback);
John Spurlock657c62c2014-07-22 12:18:09 -040080 mKeyguard.removeCallback(mCallback);
John Spurlockccb6b9a2014-05-17 15:54:40 -040081 }
John Spurlockaf8d6c42014-05-07 17:49:08 -040082 }
83
84 @Override
85 protected void handleUserSwitch(int newUserId) {
86 super.handleUserSwitch(newUserId);
87 if (mController == null) return;
88 mController.setCurrentUserId(newUserId);
89 }
90
91 @Override
Jason Monk76c67aa2016-02-19 14:49:42 -050092 public Intent getLongClickIntent() {
93 return new Intent(Settings.ACTION_CAST_SETTINGS);
94 }
95
96 @Override
Jason Monk32508852017-01-18 09:17:13 -050097 protected void handleSecondaryClick() {
98 handleClick();
99 }
100
101 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400102 protected void handleClick() {
Jason Monkba2318e2015-12-08 09:04:23 -0500103 if (mKeyguard.isSecure() && !mKeyguard.canSkipBouncer()) {
104 mHost.startRunnableDismissingKeyguard(new Runnable() {
105 @Override
106 public void run() {
107 MetricsLogger.action(mContext, getMetricsCategory());
108 showDetail(true);
109 mHost.openPanels();
110 }
111 });
112 return;
113 }
Chris Wren9e7283f2015-05-08 17:23:47 -0400114 MetricsLogger.action(mContext, getMetricsCategory());
John Spurlock1e6eb172014-07-13 11:59:50 -0400115 showDetail(true);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400116 }
117
118 @Override
Jason Monk39c98e62016-03-16 09:18:35 -0400119 public CharSequence getTileLabel() {
120 return mContext.getString(R.string.quick_settings_cast_title);
121 }
122
123 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400124 protected void handleUpdateState(BooleanState state, Object arg) {
John Spurlock1e6eb172014-07-13 11:59:50 -0400125 state.label = mContext.getString(R.string.quick_settings_cast_title);
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400126 state.contentDescription = state.label;
John Spurlock1e6eb172014-07-13 11:59:50 -0400127 state.value = false;
Selim Cinek06d3bca2014-08-26 17:29:20 +0200128 state.autoMirrorDrawable = false;
John Spurlock1e6eb172014-07-13 11:59:50 -0400129 final Set<CastDevice> devices = mController.getCastDevices();
130 boolean connecting = false;
131 for (CastDevice device : devices) {
132 if (device.state == CastDevice.STATE_CONNECTED) {
133 state.value = true;
134 state.label = getDeviceName(device);
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400135 state.contentDescription = state.contentDescription + "," +
136 mContext.getString(R.string.accessibility_cast_name, state.label);
John Spurlock1e6eb172014-07-13 11:59:50 -0400137 } else if (device.state == CastDevice.STATE_CONNECTING) {
138 connecting = true;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400139 }
140 }
John Spurlock1e6eb172014-07-13 11:59:50 -0400141 if (!state.value && connecting) {
142 state.label = mContext.getString(R.string.quick_settings_connecting);
143 }
Jason Monk32508852017-01-18 09:17:13 -0500144 state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
John Spurlock2d695812014-10-30 13:25:21 -0400145 state.icon = ResourceIcon.get(state.value ? R.drawable.ic_qs_cast_on
146 : R.drawable.ic_qs_cast_off);
John Spurlock1e6eb172014-07-13 11:59:50 -0400147 mDetailAdapter.updateItems(devices);
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400148 state.minimalAccessibilityClassName = state.expandedAccessibilityClassName =
149 Button.class.getName();
150 state.contentDescription = state.contentDescription + ","
151 + mContext.getString(R.string.accessibility_quick_settings_open_details);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400152 }
153
Selim Cinek4fda7b22014-08-18 22:07:25 +0200154 @Override
Chris Wren457a21c2015-05-06 17:50:34 -0400155 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500156 return MetricsEvent.QS_CAST;
Chris Wren457a21c2015-05-06 17:50:34 -0400157 }
158
159 @Override
Selim Cinek4fda7b22014-08-18 22:07:25 +0200160 protected String composeChangeAnnouncement() {
161 if (!mState.value) {
162 // We only announce when it's turned off to avoid vocal overflow.
163 return mContext.getString(R.string.accessibility_casting_turned_off);
164 }
165 return null;
166 }
167
John Spurlock1e6eb172014-07-13 11:59:50 -0400168 private String getDeviceName(CastDevice device) {
169 return device.name != null ? device.name
170 : mContext.getString(R.string.quick_settings_cast_device_default_name);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400171 }
172
John Spurlock657c62c2014-07-22 12:18:09 -0400173 private final class Callback implements CastController.Callback, KeyguardMonitor.Callback {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400174 @Override
John Spurlock1e6eb172014-07-13 11:59:50 -0400175 public void onCastDevicesChanged() {
176 refreshState();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400177 }
John Spurlock657c62c2014-07-22 12:18:09 -0400178
179 @Override
180 public void onKeyguardChanged() {
181 refreshState();
182 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400183 };
John Spurlock76c43b92014-05-13 21:10:51 -0400184
John Spurlock1e6eb172014-07-13 11:59:50 -0400185 private final class CastDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
186 private final LinkedHashMap<String, CastDevice> mVisibleOrder = new LinkedHashMap<>();
187
188 private QSDetailItems mItems;
189
John Spurlock76c43b92014-05-13 21:10:51 -0400190 @Override
Jason Monkc06fbb12016-01-08 14:12:18 -0500191 public CharSequence getTitle() {
192 return mContext.getString(R.string.quick_settings_cast_title);
John Spurlock76c43b92014-05-13 21:10:51 -0400193 }
John Spurlock1e6eb172014-07-13 11:59:50 -0400194
195 @Override
196 public Boolean getToggleState() {
197 return null;
198 }
199
200 @Override
201 public Intent getSettingsIntent() {
Jeff Brown18492622014-08-19 17:51:47 -0700202 return CAST_SETTINGS;
John Spurlock1e6eb172014-07-13 11:59:50 -0400203 }
204
205 @Override
206 public void setToggleState(boolean state) {
207 // noop
208 }
209
210 @Override
Chris Wren457a21c2015-05-06 17:50:34 -0400211 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500212 return MetricsEvent.QS_CAST_DETAILS;
Chris Wren457a21c2015-05-06 17:50:34 -0400213 }
214
215 @Override
John Spurlock1e6eb172014-07-13 11:59:50 -0400216 public View createDetailView(Context context, View convertView, ViewGroup parent) {
217 mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
218 mItems.setTagSuffix("Cast");
219 if (convertView == null) {
220 if (DEBUG) Log.d(TAG, "addOnAttachStateChangeListener");
221 mItems.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
222 @Override
223 public void onViewAttachedToWindow(View v) {
224 if (DEBUG) Log.d(TAG, "onViewAttachedToWindow");
225 }
226
227 @Override
228 public void onViewDetachedFromWindow(View v) {
229 if (DEBUG) Log.d(TAG, "onViewDetachedFromWindow");
230 mVisibleOrder.clear();
231 }
232 });
233 }
234 mItems.setEmptyState(R.drawable.ic_qs_cast_detail_empty,
235 R.string.quick_settings_cast_detail_empty_text);
236 mItems.setCallback(this);
237 updateItems(mController.getCastDevices());
238 mController.setDiscovering(true);
239 return mItems;
240 }
241
242 private void updateItems(Set<CastDevice> devices) {
243 if (mItems == null) return;
244 Item[] items = null;
245 if (devices != null && !devices.isEmpty()) {
246 // if we are connected, simply show that device
247 for (CastDevice device : devices) {
248 if (device.state == CastDevice.STATE_CONNECTED) {
249 final Item item = new Item();
250 item.icon = R.drawable.ic_qs_cast_on;
251 item.line1 = getDeviceName(device);
252 item.line2 = mContext.getString(R.string.quick_settings_connected);
253 item.tag = device;
254 item.canDisconnect = true;
255 items = new Item[] { item };
256 break;
257 }
258 }
259 // otherwise list all available devices, and don't move them around
260 if (items == null) {
261 for (CastDevice device : devices) {
262 mVisibleOrder.put(device.id, device);
263 }
264 items = new Item[devices.size()];
265 int i = 0;
266 for (String id : mVisibleOrder.keySet()) {
267 final CastDevice device = mVisibleOrder.get(id);
268 if (!devices.contains(device)) continue;
269 final Item item = new Item();
270 item.icon = R.drawable.ic_qs_cast_off;
271 item.line1 = getDeviceName(device);
272 if (device.state == CastDevice.STATE_CONNECTING) {
273 item.line2 = mContext.getString(R.string.quick_settings_connecting);
274 }
275 item.tag = device;
276 items[i++] = item;
277 }
278 }
279 }
280 mItems.setItems(items);
281 }
282
283 @Override
284 public void onDetailItemClick(Item item) {
285 if (item == null || item.tag == null) return;
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500286 MetricsLogger.action(mContext, MetricsEvent.QS_CAST_SELECT);
John Spurlock1e6eb172014-07-13 11:59:50 -0400287 final CastDevice device = (CastDevice) item.tag;
288 mController.startCasting(device);
289 }
290
291 @Override
292 public void onDetailItemDisconnect(Item item) {
293 if (item == null || item.tag == null) return;
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500294 MetricsLogger.action(mContext, MetricsEvent.QS_CAST_DISCONNECT);
John Spurlock78b8c8f2014-08-25 17:52:06 -0400295 final CastDevice device = (CastDevice) item.tag;
296 mController.stopCasting(device);
John Spurlock1e6eb172014-07-13 11:59:50 -0400297 }
298 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400299}