blob: 4ceace34befe6c87fc68625e0d817697f18df6d1 [file] [log] [blame]
John Spurlock3c4076a2015-02-24 12:12:25 -05001/*
2 * Copyright (C) 2015 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
Jason Monkbe3235a2017-04-05 09:29:53 -040019import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
20import static android.provider.Settings.Global.ZEN_MODE_OFF;
21
John Spurlock3c4076a2015-02-24 12:12:25 -050022import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.SharedPreferences;
John Spurlockcd863ad2015-04-07 14:01:28 -040027import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
Jason Monkbe3235a2017-04-05 09:29:53 -040028import android.content.pm.ApplicationInfo;
29import android.content.pm.PackageManager;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +000030import android.os.UserManager;
John Spurlock3c4076a2015-02-24 12:12:25 -050031import android.provider.Settings;
32import android.provider.Settings.Global;
Jason Monkbe3235a2017-04-05 09:29:53 -040033import android.service.notification.ZenModeConfig;
34import android.service.notification.ZenModeConfig.ZenRule;
Jason Monk32508852017-01-18 09:17:13 -050035import android.service.quicksettings.Tile;
Jason Monkbe3235a2017-04-05 09:29:53 -040036import android.util.Slog;
John Spurlock3c4076a2015-02-24 12:12:25 -050037import android.view.LayoutInflater;
38import android.view.View;
39import android.view.View.OnAttachStateChangeListener;
40import android.view.ViewGroup;
Julia Reynolds20aef8a2016-05-04 16:44:08 -040041import android.widget.Switch;
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +000042import android.widget.Toast;
Chris Wrenf6e9228b2016-01-26 18:04:35 -050043
Chris Wren457a21c2015-05-06 17:50:34 -040044import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010045import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jason Monk9c7844c2017-01-18 15:21:53 -050046import com.android.systemui.Dependency;
Andrew Flynn82862572015-04-01 14:22:37 -040047import com.android.systemui.Prefs;
John Spurlock3c4076a2015-02-24 12:12:25 -050048import com.android.systemui.R;
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +000049import com.android.systemui.SysUIToast;
Jason Monkec34da82017-02-24 15:57:05 -050050import com.android.systemui.plugins.ActivityStarter;
Jason Monke5b770e2017-03-03 21:49:29 -050051import com.android.systemui.plugins.qs.DetailAdapter;
Jason Monk702e2eb2017-03-03 16:53:44 -050052import com.android.systemui.plugins.qs.QSTile;
53import com.android.systemui.plugins.qs.QSTile.BooleanState;
Jason Monkbe3235a2017-04-05 09:29:53 -040054import com.android.systemui.qs.QSHost;
Jason Monk702e2eb2017-03-03 16:53:44 -050055import com.android.systemui.qs.tileimpl.QSTileImpl;
John Spurlock3c4076a2015-02-24 12:12:25 -050056import com.android.systemui.statusbar.policy.ZenModeController;
57import com.android.systemui.volume.ZenModePanel;
58
59/** Quick settings tile: Do not disturb **/
Jason Monk702e2eb2017-03-03 16:53:44 -050060public class DndTile extends QSTileImpl<BooleanState> {
John Spurlock036e7cb2015-06-15 13:49:57 -040061
John Spurlockd9c75db2015-04-28 11:19:13 -040062 private static final Intent ZEN_SETTINGS =
63 new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
64
65 private static final Intent ZEN_PRIORITY_SETTINGS =
66 new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);
John Spurlock3c4076a2015-02-24 12:12:25 -050067
68 private static final String ACTION_SET_VISIBLE = "com.android.systemui.dndtile.SET_VISIBLE";
69 private static final String EXTRA_VISIBLE = "visible";
John Spurlock3c4076a2015-02-24 12:12:25 -050070
John Spurlock036e7cb2015-06-15 13:49:57 -040071 private static final QSTile.Icon TOTAL_SILENCE =
72 ResourceIcon.get(R.drawable.ic_qs_dnd_on_total_silence);
73
John Spurlock3c4076a2015-02-24 12:12:25 -050074 private final ZenModeController mController;
75 private final DndDetailAdapter mDetailAdapter;
76
77 private boolean mListening;
John Spurlock3c4076a2015-02-24 12:12:25 -050078 private boolean mShowingDetail;
Yuta Yamada99fd2772016-10-06 17:40:19 +090079 private boolean mReceiverRegistered;
John Spurlock3c4076a2015-02-24 12:12:25 -050080
Jason Monk702e2eb2017-03-03 16:53:44 -050081 public DndTile(QSHost host) {
John Spurlock3c4076a2015-02-24 12:12:25 -050082 super(host);
Jason Monk9c7844c2017-01-18 15:21:53 -050083 mController = Dependency.get(ZenModeController.class);
John Spurlock3c4076a2015-02-24 12:12:25 -050084 mDetailAdapter = new DndDetailAdapter();
John Spurlock3c4076a2015-02-24 12:12:25 -050085 mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_SET_VISIBLE));
Yuta Yamada99fd2772016-10-06 17:40:19 +090086 mReceiverRegistered = true;
87 }
88
89 @Override
90 protected void handleDestroy() {
91 super.handleDestroy();
92 if (mReceiverRegistered) {
93 mContext.unregisterReceiver(mReceiver);
94 mReceiverRegistered = false;
95 }
John Spurlock3c4076a2015-02-24 12:12:25 -050096 }
97
98 public static void setVisible(Context context, boolean visible) {
Andrew Flynn82862572015-04-01 14:22:37 -040099 Prefs.putBoolean(context, Prefs.Key.DND_TILE_VISIBLE, visible);
John Spurlock3c4076a2015-02-24 12:12:25 -0500100 }
101
102 public static boolean isVisible(Context context) {
Andrew Flynn82862572015-04-01 14:22:37 -0400103 return Prefs.getBoolean(context, Prefs.Key.DND_TILE_VISIBLE, false /* defaultValue */);
John Spurlock3c4076a2015-02-24 12:12:25 -0500104 }
105
John Spurlockf88d8082015-03-25 18:09:51 -0400106 public static void setCombinedIcon(Context context, boolean combined) {
Andrew Flynn82862572015-04-01 14:22:37 -0400107 Prefs.putBoolean(context, Prefs.Key.DND_TILE_COMBINED_ICON, combined);
John Spurlockf88d8082015-03-25 18:09:51 -0400108 }
109
110 public static boolean isCombinedIcon(Context context) {
Andrew Flynn82862572015-04-01 14:22:37 -0400111 return Prefs.getBoolean(context, Prefs.Key.DND_TILE_COMBINED_ICON,
112 false /* defaultValue */);
John Spurlockf88d8082015-03-25 18:09:51 -0400113 }
114
John Spurlock3c4076a2015-02-24 12:12:25 -0500115 @Override
116 public DetailAdapter getDetailAdapter() {
117 return mDetailAdapter;
118 }
119
120 @Override
Jason Monk62b63a02016-02-02 15:15:31 -0500121 public BooleanState newTileState() {
John Spurlock3c4076a2015-02-24 12:12:25 -0500122 return new BooleanState();
123 }
124
125 @Override
Jason Monk76c67aa2016-02-19 14:49:42 -0500126 public Intent getLongClickIntent() {
127 return ZEN_SETTINGS;
128 }
129
130 @Override
Jason Monk32508852017-01-18 09:17:13 -0500131 protected void handleClick() {
132 if (mState.value) {
Jason Monkbe3235a2017-04-05 09:29:53 -0400133 mController.setZen(ZEN_MODE_OFF, null, TAG);
Jason Monk32508852017-01-18 09:17:13 -0500134 } else {
Beverly3e906c52018-01-22 10:49:43 -0500135 mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
Jason Monk32508852017-01-18 09:17:13 -0500136 }
137 }
138
139 @Override
140 protected void handleSecondaryClick() {
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000141 if (mController.isVolumeRestricted()) {
142 // Collapse the panels, so the user can see the toast.
143 mHost.collapsePanels();
144 SysUIToast.makeText(mContext, mContext.getString(
145 com.android.internal.R.string.error_message_change_not_allowed),
146 Toast.LENGTH_LONG).show();
147 return;
148 }
Jason Monk3c3b6502017-07-27 15:41:26 -0400149 if (!mState.value) {
150 // Because of the complexity of the zen panel, it needs to be shown after
151 // we turn on zen below.
152 mController.addCallback(new ZenModeController.Callback() {
153 @Override
154 public void onZenChanged(int zen) {
155 mController.removeCallback(this);
156 showDetail(true);
157 }
158 });
Beverly3e906c52018-01-22 10:49:43 -0500159 mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
Jason Monk3c3b6502017-07-27 15:41:26 -0400160 } else {
161 showDetail(true);
162 }
John Spurlock3c4076a2015-02-24 12:12:25 -0500163 }
164
165 @Override
Jason Monk39c98e62016-03-16 09:18:35 -0400166 public CharSequence getTileLabel() {
167 return mContext.getString(R.string.quick_settings_dnd_label);
168 }
169
170 @Override
John Spurlock3c4076a2015-02-24 12:12:25 -0500171 protected void handleUpdateState(BooleanState state, Object arg) {
172 final int zen = arg instanceof Integer ? (Integer) arg : mController.getZen();
Jason Monkbe3235a2017-04-05 09:29:53 -0400173 final boolean newValue = zen != ZEN_MODE_OFF;
John Spurlockb1688f62015-05-12 15:52:38 -0400174 final boolean valueChanged = state.value != newValue;
Jason Monk395617f2017-05-05 14:07:58 -0400175 if (state.slash == null) state.slash = new SlashState();
Jason Monk2db46192017-02-01 17:18:22 -0500176 state.dualTarget = true;
John Spurlockb1688f62015-05-12 15:52:38 -0400177 state.value = newValue;
Jason Monk32508852017-01-18 09:17:13 -0500178 state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
Jason Monk395617f2017-05-05 14:07:58 -0400179 state.slash.isSlashed = !state.value;
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000180 checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
John Spurlock3c4076a2015-02-24 12:12:25 -0500181 switch (zen) {
182 case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
183 state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
184 state.label = mContext.getString(R.string.quick_settings_dnd_priority_label);
185 state.contentDescription = mContext.getString(
186 R.string.accessibility_quick_settings_dnd_priority_on);
187 break;
188 case Global.ZEN_MODE_NO_INTERRUPTIONS:
John Spurlock036e7cb2015-06-15 13:49:57 -0400189 state.icon = TOTAL_SILENCE;
John Spurlock3c4076a2015-02-24 12:12:25 -0500190 state.label = mContext.getString(R.string.quick_settings_dnd_none_label);
191 state.contentDescription = mContext.getString(
192 R.string.accessibility_quick_settings_dnd_none_on);
193 break;
Jason Monkbe3235a2017-04-05 09:29:53 -0400194 case ZEN_MODE_ALARMS:
John Spurlock4f1163c2015-04-02 17:41:21 -0400195 state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
196 state.label = mContext.getString(R.string.quick_settings_dnd_alarms_label);
197 state.contentDescription = mContext.getString(
198 R.string.accessibility_quick_settings_dnd_alarms_on);
199 break;
John Spurlock3c4076a2015-02-24 12:12:25 -0500200 default:
Evan Lairdbcdd0462017-05-15 12:51:34 -0400201 state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
John Spurlock3c4076a2015-02-24 12:12:25 -0500202 state.label = mContext.getString(R.string.quick_settings_dnd_label);
Jason Monkbe3235a2017-04-05 09:29:53 -0400203 state.contentDescription = mContext.getString(
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400204 R.string.accessibility_quick_settings_dnd);
John Spurlock3c4076a2015-02-24 12:12:25 -0500205 break;
206 }
John Spurlockb1688f62015-05-12 15:52:38 -0400207 if (valueChanged) {
208 fireToggleStateChanged(state.value);
209 }
Jason Monk28df4ed2017-03-23 10:25:37 -0400210 state.dualLabelContentDescription = mContext.getResources().getString(
211 R.string.accessibility_quick_settings_open_settings, getTileLabel());
Jason Monk702e2eb2017-03-03 16:53:44 -0500212 state.expandedAccessibilityClassName = Switch.class.getName();
John Spurlock3c4076a2015-02-24 12:12:25 -0500213 }
214
215 @Override
Chris Wren457a21c2015-05-06 17:50:34 -0400216 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500217 return MetricsEvent.QS_DND;
Chris Wren457a21c2015-05-06 17:50:34 -0400218 }
219
220 @Override
John Spurlock3c4076a2015-02-24 12:12:25 -0500221 protected String composeChangeAnnouncement() {
222 if (mState.value) {
223 return mContext.getString(R.string.accessibility_quick_settings_dnd_changed_on);
224 } else {
225 return mContext.getString(R.string.accessibility_quick_settings_dnd_changed_off);
226 }
227 }
228
229 @Override
Jason Monk1c6116c2017-09-06 17:33:01 -0400230 public void handleSetListening(boolean listening) {
John Spurlock3c4076a2015-02-24 12:12:25 -0500231 if (mListening == listening) return;
232 mListening = listening;
233 if (mListening) {
234 mController.addCallback(mZenCallback);
Andrew Flynn82862572015-04-01 14:22:37 -0400235 Prefs.registerListener(mContext, mPrefListener);
John Spurlock3c4076a2015-02-24 12:12:25 -0500236 } else {
237 mController.removeCallback(mZenCallback);
Andrew Flynn82862572015-04-01 14:22:37 -0400238 Prefs.unregisterListener(mContext, mPrefListener);
John Spurlock3c4076a2015-02-24 12:12:25 -0500239 }
240 }
241
Jason Monkc3f42c12016-02-05 12:33:13 -0500242 @Override
243 public boolean isAvailable() {
244 return isVisible(mContext);
245 }
246
John Spurlockcd863ad2015-04-07 14:01:28 -0400247 private final OnSharedPreferenceChangeListener mPrefListener
248 = new OnSharedPreferenceChangeListener() {
249 @Override
Andrew Flynn82862572015-04-01 14:22:37 -0400250 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
251 @Prefs.Key String key) {
252 if (Prefs.Key.DND_TILE_COMBINED_ICON.equals(key) ||
253 Prefs.Key.DND_TILE_VISIBLE.equals(key)) {
John Spurlockcd863ad2015-04-07 14:01:28 -0400254 refreshState();
255 }
256 }
257 };
258
John Spurlock3c4076a2015-02-24 12:12:25 -0500259 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
260 public void onZenChanged(int zen) {
261 refreshState(zen);
Jason Monkbe3235a2017-04-05 09:29:53 -0400262 if (isShowingDetail()) {
263 mDetailAdapter.updatePanel();
264 }
265 }
266
267 @Override
268 public void onConfigChanged(ZenModeConfig config) {
269 if (isShowingDetail()) {
270 mDetailAdapter.updatePanel();
271 }
John Spurlock3c4076a2015-02-24 12:12:25 -0500272 }
273 };
274
John Spurlock3c4076a2015-02-24 12:12:25 -0500275 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
276 @Override
277 public void onReceive(Context context, Intent intent) {
John Spurlockcd863ad2015-04-07 14:01:28 -0400278 final boolean visible = intent.getBooleanExtra(EXTRA_VISIBLE, false);
279 setVisible(mContext, visible);
John Spurlock3c4076a2015-02-24 12:12:25 -0500280 refreshState();
281 }
282 };
283
284 private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener {
285
Jason Monkbe3235a2017-04-05 09:29:53 -0400286 private ZenModePanel mZenPanel;
287 private boolean mAuto;
288
John Spurlock3c4076a2015-02-24 12:12:25 -0500289 @Override
Jason Monkc06fbb12016-01-08 14:12:18 -0500290 public CharSequence getTitle() {
291 return mContext.getString(R.string.quick_settings_dnd_label);
John Spurlock3c4076a2015-02-24 12:12:25 -0500292 }
293
294 @Override
295 public Boolean getToggleState() {
296 return mState.value;
297 }
298
299 @Override
300 public Intent getSettingsIntent() {
301 return ZEN_SETTINGS;
302 }
303
304 @Override
305 public void setToggleState(boolean state) {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500306 MetricsLogger.action(mContext, MetricsEvent.QS_DND_TOGGLE, state);
John Spurlock3c4076a2015-02-24 12:12:25 -0500307 if (!state) {
Jason Monkbe3235a2017-04-05 09:29:53 -0400308 mController.setZen(ZEN_MODE_OFF, null, TAG);
309 mAuto = false;
310 } else {
Beverly3e906c52018-01-22 10:49:43 -0500311 mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
John Spurlock3c4076a2015-02-24 12:12:25 -0500312 }
313 }
314
315 @Override
Chris Wren457a21c2015-05-06 17:50:34 -0400316 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500317 return MetricsEvent.QS_DND_DETAILS;
Chris Wren457a21c2015-05-06 17:50:34 -0400318 }
319
320 @Override
John Spurlock3c4076a2015-02-24 12:12:25 -0500321 public View createDetailView(Context context, View convertView, ViewGroup parent) {
Jason Monkbe3235a2017-04-05 09:29:53 -0400322 mZenPanel = convertView != null ? (ZenModePanel) convertView
John Spurlock3c4076a2015-02-24 12:12:25 -0500323 : (ZenModePanel) LayoutInflater.from(context).inflate(
324 R.layout.zen_mode_panel, parent, false);
325 if (convertView == null) {
Jason Monkbe3235a2017-04-05 09:29:53 -0400326 mZenPanel.init(mController);
327 mZenPanel.addOnAttachStateChangeListener(this);
328 mZenPanel.setCallback(mZenModePanelCallback);
Evan Lairdbcdd0462017-05-15 12:51:34 -0400329 mZenPanel.setEmptyState(R.drawable.ic_qs_dnd_detail_empty, R.string.dnd_is_off);
John Spurlock3c4076a2015-02-24 12:12:25 -0500330 }
Jason Monkbe3235a2017-04-05 09:29:53 -0400331 updatePanel();
332 return mZenPanel;
333 }
334
335 private void updatePanel() {
336 if (mZenPanel == null) return;
337 mAuto = false;
338 if (mController.getZen() == ZEN_MODE_OFF) {
339 mZenPanel.setState(ZenModePanel.STATE_OFF);
340 } else {
341 ZenModeConfig config = mController.getConfig();
342 String summary = "";
343 if (config.manualRule != null && config.manualRule.enabler != null) {
344 summary = getOwnerCaption(config.manualRule.enabler);
345 }
346 for (ZenRule automaticRule : config.automaticRules.values()) {
347 if (automaticRule.isAutomaticActive()) {
348 if (summary.isEmpty()) {
349 summary = mContext.getString(R.string.qs_dnd_prompt_auto_rule,
350 automaticRule.name);
351 } else {
352 summary = mContext.getString(R.string.qs_dnd_prompt_auto_rule_app);
353 }
354 }
355 }
356 if (summary.isEmpty()) {
357 mZenPanel.setState(ZenModePanel.STATE_MODIFY);
358 } else {
359 mAuto = true;
360 mZenPanel.setState(ZenModePanel.STATE_AUTO_RULE);
361 mZenPanel.setAutoText(summary);
362 }
363 }
364 }
365
366 private String getOwnerCaption(String owner) {
367 final PackageManager pm = mContext.getPackageManager();
368 try {
369 final ApplicationInfo info = pm.getApplicationInfo(owner, 0);
370 if (info != null) {
371 final CharSequence seq = info.loadLabel(pm);
372 if (seq != null) {
373 final String str = seq.toString().trim();
374 return mContext.getString(R.string.qs_dnd_prompt_app, str);
375 }
376 }
377 } catch (Throwable e) {
378 Slog.w(TAG, "Error loading owner caption", e);
379 }
380 return "";
John Spurlock3c4076a2015-02-24 12:12:25 -0500381 }
382
383 @Override
384 public void onViewAttachedToWindow(View v) {
385 mShowingDetail = true;
386 }
387
388 @Override
389 public void onViewDetachedFromWindow(View v) {
390 mShowingDetail = false;
Jason Monkbe3235a2017-04-05 09:29:53 -0400391 mZenPanel = null;
John Spurlock3c4076a2015-02-24 12:12:25 -0500392 }
393 }
John Spurlockd9c75db2015-04-28 11:19:13 -0400394
395 private final ZenModePanel.Callback mZenModePanelCallback = new ZenModePanel.Callback() {
396 @Override
397 public void onPrioritySettings() {
Jason Monk9c7844c2017-01-18 15:21:53 -0500398 Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
399 ZEN_PRIORITY_SETTINGS, 0);
John Spurlockd9c75db2015-04-28 11:19:13 -0400400 }
401
402 @Override
403 public void onInteraction() {
404 // noop
405 }
406
407 @Override
408 public void onExpanded(boolean expanded) {
409 // noop
410 }
411 };
412
John Spurlock3c4076a2015-02-24 12:12:25 -0500413}