DndTile.java revision 99fd277a4737e4369246fc3d210c14c83d1fbce7
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
17package com.android.systemui.qs.tiles;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.content.SharedPreferences;
24import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
25import android.os.UserManager;
26import android.provider.Settings;
27import android.provider.Settings.Global;
28import android.view.LayoutInflater;
29import android.view.View;
30import android.view.View.OnAttachStateChangeListener;
31import android.view.ViewGroup;
32import android.widget.Switch;
33import android.widget.Toast;
34
35import com.android.internal.logging.MetricsLogger;
36import com.android.internal.logging.MetricsProto.MetricsEvent;
37import com.android.systemui.Prefs;
38import com.android.systemui.R;
39import com.android.systemui.SysUIToast;
40import com.android.systemui.qs.QSTile;
41import com.android.systemui.statusbar.policy.ZenModeController;
42import com.android.systemui.volume.ZenModePanel;
43
44/** Quick settings tile: Do not disturb **/
45public class DndTile extends QSTile<QSTile.BooleanState> {
46
47    private static final Intent ZEN_SETTINGS =
48            new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
49
50    private static final Intent ZEN_PRIORITY_SETTINGS =
51            new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);
52
53    private static final String ACTION_SET_VISIBLE = "com.android.systemui.dndtile.SET_VISIBLE";
54    private static final String EXTRA_VISIBLE = "visible";
55
56    private static final QSTile.Icon TOTAL_SILENCE =
57            ResourceIcon.get(R.drawable.ic_qs_dnd_on_total_silence);
58
59    private final AnimationIcon mDisable =
60            new AnimationIcon(R.drawable.ic_dnd_disable_animation,
61                    R.drawable.ic_qs_dnd_off);
62    private final AnimationIcon mDisableTotalSilence =
63            new AnimationIcon(R.drawable.ic_dnd_total_silence_disable_animation,
64                    R.drawable.ic_qs_dnd_off);
65
66    private final ZenModeController mController;
67    private final DndDetailAdapter mDetailAdapter;
68
69    private boolean mListening;
70    private boolean mShowingDetail;
71    private boolean mReceiverRegistered;
72
73    public DndTile(Host host) {
74        super(host);
75        mController = host.getZenModeController();
76        mDetailAdapter = new DndDetailAdapter();
77        mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_SET_VISIBLE));
78        mReceiverRegistered = true;
79    }
80
81    @Override
82    protected void handleDestroy() {
83        super.handleDestroy();
84        if (mReceiverRegistered) {
85            mContext.unregisterReceiver(mReceiver);
86            mReceiverRegistered = false;
87        }
88    }
89
90    public static void setVisible(Context context, boolean visible) {
91        Prefs.putBoolean(context, Prefs.Key.DND_TILE_VISIBLE, visible);
92    }
93
94    public static boolean isVisible(Context context) {
95        return Prefs.getBoolean(context, Prefs.Key.DND_TILE_VISIBLE, false /* defaultValue */);
96    }
97
98    public static void setCombinedIcon(Context context, boolean combined) {
99        Prefs.putBoolean(context, Prefs.Key.DND_TILE_COMBINED_ICON, combined);
100    }
101
102    public static boolean isCombinedIcon(Context context) {
103        return Prefs.getBoolean(context, Prefs.Key.DND_TILE_COMBINED_ICON,
104                false /* defaultValue */);
105    }
106
107    @Override
108    public DetailAdapter getDetailAdapter() {
109        return mDetailAdapter;
110    }
111
112    @Override
113    public BooleanState newTileState() {
114        return new BooleanState();
115    }
116
117    @Override
118    public Intent getLongClickIntent() {
119        return ZEN_SETTINGS;
120    }
121
122    @Override
123    public void handleClick() {
124        if (mController.isVolumeRestricted()) {
125            // Collapse the panels, so the user can see the toast.
126            mHost.collapsePanels();
127            SysUIToast.makeText(mContext, mContext.getString(
128                    com.android.internal.R.string.error_message_change_not_allowed),
129                    Toast.LENGTH_LONG).show();
130            return;
131        }
132        MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
133        if (mState.value) {
134            mController.setZen(Global.ZEN_MODE_OFF, null, TAG);
135        } else {
136            int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, Global.ZEN_MODE_ALARMS);
137            mController.setZen(zen, null, TAG);
138            showDetail(true);
139        }
140    }
141
142    @Override
143    public CharSequence getTileLabel() {
144        return mContext.getString(R.string.quick_settings_dnd_label);
145    }
146
147    @Override
148    protected void handleUpdateState(BooleanState state, Object arg) {
149        final int zen = arg instanceof Integer ? (Integer) arg : mController.getZen();
150        final boolean newValue = zen != Global.ZEN_MODE_OFF;
151        final boolean valueChanged = state.value != newValue;
152        state.value = newValue;
153        checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
154        switch (zen) {
155            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
156                state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
157                state.label = mContext.getString(R.string.quick_settings_dnd_priority_label);
158                state.contentDescription = mContext.getString(
159                        R.string.accessibility_quick_settings_dnd_priority_on);
160                break;
161            case Global.ZEN_MODE_NO_INTERRUPTIONS:
162                state.icon = TOTAL_SILENCE;
163                state.label = mContext.getString(R.string.quick_settings_dnd_none_label);
164                state.contentDescription = mContext.getString(
165                        R.string.accessibility_quick_settings_dnd_none_on);
166                break;
167            case Global.ZEN_MODE_ALARMS:
168                state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
169                state.label = mContext.getString(R.string.quick_settings_dnd_alarms_label);
170                state.contentDescription = mContext.getString(
171                        R.string.accessibility_quick_settings_dnd_alarms_on);
172                break;
173            default:
174                state.icon = TOTAL_SILENCE.equals(state.icon) ? mDisableTotalSilence : mDisable;
175                state.label = mContext.getString(R.string.quick_settings_dnd_label);
176                state.contentDescription =  mContext.getString(
177                        R.string.accessibility_quick_settings_dnd);
178                break;
179        }
180        if (mShowingDetail && !state.value) {
181            showDetail(false);
182        }
183        if (valueChanged) {
184            fireToggleStateChanged(state.value);
185        }
186        state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
187                = Switch.class.getName();
188    }
189
190    @Override
191    public int getMetricsCategory() {
192        return MetricsEvent.QS_DND;
193    }
194
195    @Override
196    protected String composeChangeAnnouncement() {
197        if (mState.value) {
198            return mContext.getString(R.string.accessibility_quick_settings_dnd_changed_on);
199        } else {
200            return mContext.getString(R.string.accessibility_quick_settings_dnd_changed_off);
201        }
202    }
203
204    @Override
205    public void setListening(boolean listening) {
206        if (mListening == listening) return;
207        mListening = listening;
208        if (mListening) {
209            mController.addCallback(mZenCallback);
210            Prefs.registerListener(mContext, mPrefListener);
211        } else {
212            mController.removeCallback(mZenCallback);
213            Prefs.unregisterListener(mContext, mPrefListener);
214        }
215    }
216
217    @Override
218    public boolean isAvailable() {
219        return isVisible(mContext);
220    }
221
222    private final OnSharedPreferenceChangeListener mPrefListener
223            = new OnSharedPreferenceChangeListener() {
224        @Override
225        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
226                @Prefs.Key String key) {
227            if (Prefs.Key.DND_TILE_COMBINED_ICON.equals(key) ||
228                    Prefs.Key.DND_TILE_VISIBLE.equals(key)) {
229                refreshState();
230            }
231        }
232    };
233
234    private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
235        public void onZenChanged(int zen) {
236            refreshState(zen);
237        }
238    };
239
240    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
241        @Override
242        public void onReceive(Context context, Intent intent) {
243            final boolean visible = intent.getBooleanExtra(EXTRA_VISIBLE, false);
244            setVisible(mContext, visible);
245            refreshState();
246        }
247    };
248
249    private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener {
250
251        @Override
252        public CharSequence getTitle() {
253            return mContext.getString(R.string.quick_settings_dnd_label);
254        }
255
256        @Override
257        public Boolean getToggleState() {
258            return mState.value;
259        }
260
261        @Override
262        public Intent getSettingsIntent() {
263            return ZEN_SETTINGS;
264        }
265
266        @Override
267        public void setToggleState(boolean state) {
268            MetricsLogger.action(mContext, MetricsEvent.QS_DND_TOGGLE, state);
269            if (!state) {
270                mController.setZen(Global.ZEN_MODE_OFF, null, TAG);
271                showDetail(false);
272            }
273        }
274
275        @Override
276        public int getMetricsCategory() {
277            return MetricsEvent.QS_DND_DETAILS;
278        }
279
280        @Override
281        public View createDetailView(Context context, View convertView, ViewGroup parent) {
282            final ZenModePanel zmp = convertView != null ? (ZenModePanel) convertView
283                    : (ZenModePanel) LayoutInflater.from(context).inflate(
284                            R.layout.zen_mode_panel, parent, false);
285            if (convertView == null) {
286                zmp.init(mController);
287                zmp.addOnAttachStateChangeListener(this);
288                zmp.setCallback(mZenModePanelCallback);
289            }
290            return zmp;
291        }
292
293        @Override
294        public void onViewAttachedToWindow(View v) {
295            mShowingDetail = true;
296        }
297
298        @Override
299        public void onViewDetachedFromWindow(View v) {
300            mShowingDetail = false;
301        }
302    }
303
304    private final ZenModePanel.Callback mZenModePanelCallback = new ZenModePanel.Callback() {
305        @Override
306        public void onPrioritySettings() {
307            mHost.startActivityDismissingKeyguard(ZEN_PRIORITY_SETTINGS);
308        }
309
310        @Override
311        public void onInteraction() {
312            // noop
313        }
314
315        @Override
316        public void onExpanded(boolean expanded) {
317            // noop
318        }
319    };
320
321}
322