• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.systemui.qs.tiles;
18 
19 import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
20 import static android.provider.Settings.Global.ZEN_MODE_OFF;
21 
22 import android.app.AlertDialog;
23 import android.app.Dialog;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.SharedPreferences;
27 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
28 import android.net.Uri;
29 import android.os.Handler;
30 import android.os.Looper;
31 import android.os.UserManager;
32 import android.provider.Settings;
33 import android.provider.Settings.Global;
34 import android.service.notification.ZenModeConfig;
35 import android.service.quicksettings.Tile;
36 import android.text.TextUtils;
37 import android.util.Log;
38 import android.widget.Switch;
39 
40 import androidx.annotation.Nullable;
41 
42 import com.android.internal.jank.InteractionJankMonitor;
43 import com.android.internal.logging.MetricsLogger;
44 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
45 import com.android.settingslib.notification.modes.EnableDndDialogFactory;
46 import com.android.systemui.Prefs;
47 import com.android.systemui.animation.DialogCuj;
48 import com.android.systemui.animation.DialogTransitionAnimator;
49 import com.android.systemui.animation.Expandable;
50 import com.android.systemui.dagger.qualifiers.Background;
51 import com.android.systemui.dagger.qualifiers.Main;
52 import com.android.systemui.modes.shared.ModesUi;
53 import com.android.systemui.plugins.ActivityStarter;
54 import com.android.systemui.plugins.FalsingManager;
55 import com.android.systemui.plugins.qs.QSTile.BooleanState;
56 import com.android.systemui.plugins.statusbar.StatusBarStateController;
57 import com.android.systemui.qs.QSHost;
58 import com.android.systemui.qs.QsEventLogger;
59 import com.android.systemui.qs.UserSettingObserver;
60 import com.android.systemui.qs.logging.QSLogger;
61 import com.android.systemui.qs.tileimpl.QSTileImpl;
62 import com.android.systemui.qs.tiles.dialog.QSEnableDndDialogMetricsLogger;
63 import com.android.systemui.res.R;
64 import com.android.systemui.statusbar.phone.SystemUIDialog;
65 import com.android.systemui.statusbar.policy.ZenModeController;
66 import com.android.systemui.util.settings.SecureSettings;
67 
68 import javax.inject.Inject;
69 
70 /** Quick settings tile: Do not disturb **/
71 public class DndTile extends QSTileImpl<BooleanState> {
72 
73     public static final String TILE_SPEC = "dnd";
74 
75     private static final Intent ZEN_SETTINGS =
76             new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
77 
78     private static final Intent ZEN_PRIORITY_SETTINGS =
79             new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);
80 
81     private static final String INTERACTION_JANK_TAG = "start_zen_mode";
82 
83     private final ZenModeController mController;
84     private final SharedPreferences mSharedPreferences;
85     private final UserSettingObserver mSettingZenDuration;
86     private final DialogTransitionAnimator mDialogTransitionAnimator;
87     private final QSEnableDndDialogMetricsLogger mQSDndDurationDialogLogger;
88 
89     private boolean mListening;
90 
91     @Inject
DndTile( QSHost host, QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, MetricsLogger metricsLogger, StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, ZenModeController zenModeController, @Main SharedPreferences sharedPreferences, SecureSettings secureSettings, DialogTransitionAnimator dialogTransitionAnimator )92     public DndTile(
93             QSHost host,
94             QsEventLogger uiEventLogger,
95             @Background Looper backgroundLooper,
96             @Main Handler mainHandler,
97             FalsingManager falsingManager,
98             MetricsLogger metricsLogger,
99             StatusBarStateController statusBarStateController,
100             ActivityStarter activityStarter,
101             QSLogger qsLogger,
102             ZenModeController zenModeController,
103             @Main SharedPreferences sharedPreferences,
104             SecureSettings secureSettings,
105             DialogTransitionAnimator dialogTransitionAnimator
106     ) {
107         super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
108                 statusBarStateController, activityStarter, qsLogger);
109 
110         // If the flag is on, this shouldn't run at all since the modes tile replaces the DND tile.
111         ModesUi.assertInLegacyMode();
112 
113         mController = zenModeController;
114         mSharedPreferences = sharedPreferences;
115         mController.observe(getLifecycle(), mZenCallback);
116         mDialogTransitionAnimator = dialogTransitionAnimator;
117         mSettingZenDuration = new UserSettingObserver(secureSettings, mUiHandler,
118                 Settings.Secure.ZEN_DURATION, getHost().getUserId()) {
119             @Override
120             protected void handleValueChanged(int value, boolean observedChange) {
121                 refreshState();
122             }
123         };
124         mQSDndDurationDialogLogger = new QSEnableDndDialogMetricsLogger(mContext);
125     }
126 
setVisible(Context context, boolean visible)127     public static void setVisible(Context context, boolean visible) {
128         Prefs.putBoolean(context, Prefs.Key.DND_TILE_VISIBLE, visible);
129     }
130 
isVisible(SharedPreferences prefs)131     public static boolean isVisible(SharedPreferences prefs) {
132         return prefs.getBoolean(Prefs.Key.DND_TILE_VISIBLE, false /* defaultValue */);
133     }
134 
setCombinedIcon(Context context, boolean combined)135     public static void setCombinedIcon(Context context, boolean combined) {
136         Prefs.putBoolean(context, Prefs.Key.DND_TILE_COMBINED_ICON, combined);
137     }
138 
isCombinedIcon(SharedPreferences sharedPreferences)139     public static boolean isCombinedIcon(SharedPreferences sharedPreferences) {
140         return sharedPreferences.getBoolean(Prefs.Key.DND_TILE_COMBINED_ICON,
141                 false /* defaultValue */);
142     }
143 
144     @Override
newTileState()145     public BooleanState newTileState() {
146         return new BooleanState();
147     }
148 
149     @Override
getLongClickIntent()150     public Intent getLongClickIntent() {
151         return ZEN_SETTINGS;
152     }
153 
154     @Override
handleClick(@ullable Expandable expandable)155     protected void handleClick(@Nullable Expandable expandable) {
156         // Zen is currently on
157         if (mState.value) {
158             mController.setZen(ZEN_MODE_OFF, null, TAG);
159         } else {
160             enableZenMode(expandable);
161         }
162     }
163 
164     @Override
handleUserSwitch(int newUserId)165     protected void handleUserSwitch(int newUserId) {
166         super.handleUserSwitch(newUserId);
167         mSettingZenDuration.setUserId(newUserId);
168     }
169 
enableZenMode(@ullable Expandable expandable)170     private void enableZenMode(@Nullable Expandable expandable) {
171         int zenDuration = mSettingZenDuration.getValue();
172         switch (zenDuration) {
173             case Settings.Secure.ZEN_DURATION_PROMPT:
174                 mUiHandler.post(() -> {
175                     Dialog dialog = makeZenModeDialog();
176                     if (expandable != null) {
177                         DialogTransitionAnimator.Controller controller =
178                                 expandable.dialogTransitionController(new DialogCuj(
179                                         InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
180                                         INTERACTION_JANK_TAG));
181                         if (controller != null) {
182                             mDialogTransitionAnimator.show(dialog,
183                                     controller, /* animateBackgroundBoundsChange= */ false);
184                         } else {
185                             dialog.show();
186                         }
187                     } else {
188                         dialog.show();
189                     }
190                 });
191                 break;
192             case Settings.Secure.ZEN_DURATION_FOREVER:
193                 mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
194                 break;
195             default:
196                 Uri conditionId = ZenModeConfig.toTimeCondition(mContext, zenDuration,
197                         mHost.getUserId(), true).id;
198                 mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
199                         conditionId, TAG);
200         }
201     }
202 
makeZenModeDialog()203     private Dialog makeZenModeDialog() {
204         AlertDialog dialog = new EnableDndDialogFactory(mContext, R.style.Theme_SystemUI_Dialog,
205                 true /* cancelIsNeutral */,
206                 mQSDndDurationDialogLogger).createDialog();
207         SystemUIDialog.applyFlags(dialog);
208         SystemUIDialog.setShowForAllUsers(dialog, true);
209         SystemUIDialog.registerDismissListener(dialog);
210         SystemUIDialog.setDialogSize(dialog);
211         return dialog;
212     }
213 
214     @Override
handleSecondaryClick(@ullable Expandable expandable)215     protected void handleSecondaryClick(@Nullable Expandable expandable) {
216         handleLongClick(expandable);
217     }
218 
219     @Override
getTileLabel()220     public CharSequence getTileLabel() {
221         return mContext.getString(R.string.quick_settings_dnd_label);
222     }
223 
224     @Override
handleUpdateState(BooleanState state, Object arg)225     protected void handleUpdateState(BooleanState state, Object arg) {
226         if (mController == null) return;
227         final int zen = arg instanceof Integer ? (Integer) arg : mController.getZen();
228         final boolean newValue = zen != ZEN_MODE_OFF;
229         state.dualTarget = true;
230         state.value = newValue;
231         state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
232         state.icon = maybeLoadResourceIcon(state.value
233                 ? R.drawable.qs_dnd_icon_on
234                 : R.drawable.qs_dnd_icon_off);
235         state.label = getTileLabel();
236         state.secondaryLabel = TextUtils.emptyIfNull(ZenModeConfig.getDescription(mContext,
237                 zen != Global.ZEN_MODE_OFF, mController.getConfig(), false));
238         checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
239         // Keeping the secondaryLabel in contentDescription instead of stateDescription is easier
240         // to understand.
241         switch (zen) {
242             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
243                 state.contentDescription =
244                         mContext.getString(R.string.accessibility_quick_settings_dnd) + ", "
245                                 + state.secondaryLabel;
246                 break;
247             case Global.ZEN_MODE_NO_INTERRUPTIONS:
248                 state.contentDescription =
249                         mContext.getString(R.string.accessibility_quick_settings_dnd) + ", " +
250                                 mContext.getString(
251                                         R.string.accessibility_quick_settings_dnd_none_on)
252                                 + ", " + state.secondaryLabel;
253                 break;
254             case ZEN_MODE_ALARMS:
255                 state.contentDescription =
256                         mContext.getString(R.string.accessibility_quick_settings_dnd) + ", " +
257                                 mContext.getString(
258                                         R.string.accessibility_quick_settings_dnd_alarms_on)
259                                 + ", " + state.secondaryLabel;
260                 break;
261             default:
262                 state.contentDescription = mContext.getString(
263                         R.string.accessibility_quick_settings_dnd);
264                 break;
265         }
266         state.dualLabelContentDescription = mContext.getResources().getString(
267                 R.string.accessibility_quick_settings_open_settings, getTileLabel());
268         state.expandedAccessibilityClassName = Switch.class.getName();
269         state.forceExpandIcon =
270                 mSettingZenDuration.getValue() == Settings.Secure.ZEN_DURATION_PROMPT;
271     }
272 
273     @Override
getMetricsCategory()274     public int getMetricsCategory() {
275         return MetricsEvent.QS_DND;
276     }
277 
278     @Override
handleSetListening(boolean listening)279     public void handleSetListening(boolean listening) {
280         super.handleSetListening(listening);
281         if (mListening == listening) return;
282         mListening = listening;
283         if (mListening) {
284             Prefs.registerListener(mContext, mPrefListener);
285         } else {
286             Prefs.unregisterListener(mContext, mPrefListener);
287         }
288         mSettingZenDuration.setListening(listening);
289     }
290 
291     @Override
handleDestroy()292     protected void handleDestroy() {
293         super.handleDestroy();
294         mSettingZenDuration.setListening(false);
295     }
296 
297     @Override
isAvailable()298     public boolean isAvailable() {
299         return isVisible(mSharedPreferences);
300     }
301 
302     private final OnSharedPreferenceChangeListener mPrefListener
303             = new OnSharedPreferenceChangeListener() {
304         @Override
305         public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
306                 @Prefs.Key String key) {
307             if (Prefs.Key.DND_TILE_COMBINED_ICON.equals(key) ||
308                     Prefs.Key.DND_TILE_VISIBLE.equals(key)) {
309                 refreshState();
310             }
311         }
312     };
313 
314     private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
315         public void onZenChanged(int zen) {
316             Log.d(TAG, "Zen changed to " + zen + ". Requesting refresh of tile.");
317             refreshState(zen);
318         }
319     };
320 
321 }
322