ZenModePanel.java revision fbb3d6ff392e9cd13fea49f85f12efd0a304d448
1/*
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.volume;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.content.Context;
22import android.content.Intent;
23import android.content.SharedPreferences;
24import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
25import android.net.Uri;
26import android.os.Handler;
27import android.os.Looper;
28import android.os.Message;
29import android.provider.Settings;
30import android.provider.Settings.Global;
31import android.service.notification.Condition;
32import android.service.notification.ZenModeConfig;
33import android.util.AttributeSet;
34import android.util.Log;
35import android.view.LayoutInflater;
36import android.view.View;
37import android.view.animation.AnimationUtils;
38import android.view.animation.Interpolator;
39import android.widget.CompoundButton;
40import android.widget.CompoundButton.OnCheckedChangeListener;
41import android.widget.ImageView;
42import android.widget.LinearLayout;
43import android.widget.RadioButton;
44import android.widget.TextView;
45
46import com.android.systemui.R;
47import com.android.systemui.statusbar.policy.ZenModeController;
48
49import java.util.Arrays;
50import java.util.Objects;
51
52public class ZenModePanel extends LinearLayout {
53    private static final String TAG = "ZenModePanel";
54    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
55
56    private static final int SECONDS_MS = 1000;
57    private static final int MINUTES_MS = 60 * SECONDS_MS;
58
59    private static final int[] MINUTE_BUCKETS = DEBUG
60            ? new int[] { 0, 1, 2, 5, 15, 30, 45, 60, 120, 180, 240, 480 }
61            : new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
62    private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
63    private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
64    private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
65    private static final int FOREVER_CONDITION_INDEX = 0;
66    private static final int TIME_CONDITION_INDEX = 1;
67    private static final int FIRST_CONDITION_INDEX = 2;
68    private static final float SILENT_HINT_PULSE_SCALE = 1.1f;
69    private static final int ZERO_VALUE_MS = 20 * SECONDS_MS;
70
71    public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
72
73    private final Context mContext;
74    private final LayoutInflater mInflater;
75    private final H mHandler = new H();
76    private final Favorites mFavorites;
77    private final Interpolator mFastOutSlowInInterpolator;
78
79    private char mLogTag = '?';
80    private String mTag;
81
82    private SegmentedButtons mZenButtons;
83    private View mZenSubhead;
84    private TextView mZenSubheadCollapsed;
85    private TextView mZenSubheadExpanded;
86    private View mMoreSettings;
87    private LinearLayout mZenConditions;
88
89    private Callback mCallback;
90    private ZenModeController mController;
91    private boolean mRequestingConditions;
92    private Uri mExitConditionId;
93    private int mBucketIndex = -1;
94    private boolean mExpanded;
95    private int mSessionZen;
96    private Uri mSessionExitConditionId;
97    private String mExitConditionText;
98
99    public ZenModePanel(Context context, AttributeSet attrs) {
100        super(context, attrs);
101        mContext = context;
102        mFavorites = new Favorites();
103        mInflater = LayoutInflater.from(mContext.getApplicationContext());
104        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
105                android.R.interpolator.fast_out_slow_in);
106        updateTag();
107        if (DEBUG) Log.d(mTag, "new ZenModePanel");
108    }
109
110    private void updateTag() {
111        mTag = TAG + "/" + mLogTag + "/" + Integer.toHexString(System.identityHashCode(this));
112    }
113
114    @Override
115    protected void onFinishInflate() {
116        super.onFinishInflate();
117
118        mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons);
119        mZenButtons.addButton(R.string.interruption_level_none, Global.ZEN_MODE_NO_INTERRUPTIONS);
120        mZenButtons.addButton(R.string.interruption_level_priority,
121                Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
122        mZenButtons.addButton(R.string.interruption_level_all, Global.ZEN_MODE_OFF);
123        mZenButtons.setCallback(mZenButtonsCallback);
124
125        mZenSubhead = findViewById(R.id.zen_subhead);
126
127        mZenSubheadCollapsed = (TextView) findViewById(R.id.zen_subhead_collapsed);
128        mZenSubheadCollapsed.setOnClickListener(new View.OnClickListener() {
129            @Override
130            public void onClick(View v) {
131                setExpanded(true);
132                fireInteraction();
133            }
134        });
135
136        mZenSubheadExpanded = (TextView) findViewById(R.id.zen_subhead_expanded);
137
138        mMoreSettings = findViewById(R.id.zen_more_settings);
139        mMoreSettings.setOnClickListener(new View.OnClickListener() {
140            @Override
141            public void onClick(View v) {
142                fireMoreSettings();
143                fireInteraction();
144            }
145        });
146
147        mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
148    }
149
150    @Override
151    protected void onAttachedToWindow() {
152        super.onAttachedToWindow();
153        if (DEBUG) Log.d(mTag, "onAttachedToWindow");
154        mSessionZen = getSelectedZen(-1);
155        mSessionExitConditionId = mExitConditionId;
156        refreshExitConditionText();
157        updateWidgets();
158    }
159
160    @Override
161    protected void onDetachedFromWindow() {
162        super.onDetachedFromWindow();
163        if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
164        mSessionZen = -1;
165        mSessionExitConditionId = null;
166        setExpanded(false);
167    }
168
169    private void setExpanded(boolean expanded) {
170        if (expanded == mExpanded) return;
171        mExpanded = expanded;
172        updateWidgets();
173        setRequestingConditions(mExpanded);
174        fireExpanded();
175    }
176
177    /** Start or stop requesting relevant zen mode exit conditions */
178    private void setRequestingConditions(boolean requesting) {
179        if (mRequestingConditions == requesting) return;
180        if (DEBUG) Log.d(mTag, "setRequestingConditions " + requesting);
181        mRequestingConditions = requesting;
182        if (mController != null) {
183            mController.requestConditions(mRequestingConditions);
184        }
185        if (mRequestingConditions) {
186            Condition timeCondition = parseExistingTimeCondition(mExitConditionId);
187            if (timeCondition != null) {
188                mBucketIndex = -1;
189            } else {
190                mBucketIndex = DEFAULT_BUCKET_INDEX;
191                timeCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
192            }
193            if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex);
194            handleUpdateConditions(new Condition[0]);  // ensures forever exists
195            bind(timeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
196            checkForDefault();
197        } else {
198            mZenConditions.removeAllViews();
199        }
200    }
201
202    public void init(ZenModeController controller, char logTag) {
203        mController = controller;
204        mLogTag = logTag;
205        updateTag();
206        setExitConditionId(mController.getExitConditionId());
207        refreshExitConditionText();
208        mSessionZen = getSelectedZen(-1);
209        handleUpdateZen(mController.getZen());
210        if (DEBUG) Log.d(mTag, "init mExitConditionId=" + mExitConditionId);
211        mZenConditions.removeAllViews();
212        mController.addCallback(mZenCallback);
213    }
214
215    private void setExitConditionId(Uri exitConditionId) {
216        if (Objects.equals(mExitConditionId, exitConditionId)) return;
217        mExitConditionId = exitConditionId;
218        refreshExitConditionText();
219    }
220
221    private void refreshExitConditionText() {
222        final String forever = mContext.getString(R.string.zen_mode_forever);
223        if (mExitConditionId == null) {
224            mExitConditionText = forever;
225        } else if (ZenModeConfig.isValidCountdownConditionId(mExitConditionId)) {
226            final Condition condition = parseExistingTimeCondition(mExitConditionId);
227            mExitConditionText = condition != null ? condition.summary : forever;
228        } else {
229            mExitConditionText = "(until condition ends)";  // TODO persist current description
230        }
231    }
232
233    public void setCallback(Callback callback) {
234        mCallback = callback;
235    }
236
237    public void showSilentHint() {
238        if (DEBUG) Log.d(mTag, "showSilentHint");
239        if (mZenButtons == null || mZenButtons.getChildCount() == 0) return;
240        final View noneButton = mZenButtons.getChildAt(0);
241        if (noneButton.getScaleX() != 1) return;  // already running
242        noneButton.animate().cancel();
243        noneButton.animate().scaleX(SILENT_HINT_PULSE_SCALE).scaleY(SILENT_HINT_PULSE_SCALE)
244                .setInterpolator(mFastOutSlowInInterpolator)
245                .setListener(new AnimatorListenerAdapter() {
246                    @Override
247                    public void onAnimationEnd(Animator animation) {
248                        noneButton.animate().scaleX(1).scaleY(1).setListener(null);
249                    }
250                });
251    }
252
253    private void handleUpdateZen(int zen) {
254        if (mSessionZen != -1 && mSessionZen != zen) {
255            setExpanded(zen != Global.ZEN_MODE_OFF);
256            mSessionZen = zen;
257        }
258        mZenButtons.setSelectedValue(zen);
259        updateWidgets();
260    }
261
262    private int getSelectedZen(int defValue) {
263        final Object zen = mZenButtons.getSelectedValue();
264        return zen != null ? (Integer) zen : defValue;
265    }
266
267    private void updateWidgets() {
268        final int zen = getSelectedZen(Global.ZEN_MODE_OFF);
269        final boolean zenOff = zen == Global.ZEN_MODE_OFF;
270        final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
271        final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
272        final boolean foreverSelected = mExitConditionId == null;
273
274        mZenSubhead.setVisibility(!zenOff && (mExpanded || !foreverSelected) ? VISIBLE : GONE);
275        mZenSubheadExpanded.setVisibility(mExpanded ? VISIBLE : GONE);
276        mZenSubheadCollapsed.setVisibility(!mExpanded ? VISIBLE : GONE);
277        mMoreSettings.setVisibility(zenImportant && mExpanded ? VISIBLE : GONE);
278        mZenConditions.setVisibility(!zenOff && mExpanded ? VISIBLE : GONE);
279
280        if (zenNone) {
281            mZenSubheadExpanded.setText(R.string.zen_no_interruptions_with_warning);
282            mZenSubheadCollapsed.setText(mExitConditionText);
283        } else if (zenImportant) {
284            mZenSubheadExpanded.setText(R.string.zen_important_interruptions);
285            mZenSubheadCollapsed.setText(mExitConditionText);
286        }
287    }
288
289    private Condition parseExistingTimeCondition(Uri conditionId) {
290        final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
291        if (time == 0) return null;
292        final long span = time - System.currentTimeMillis();
293        if (span <= 0 || span > MAX_BUCKET_MINUTES * MINUTES_MS) return null;
294        return timeCondition(time, Math.round(span / (float)MINUTES_MS));
295    }
296
297    private Condition newTimeCondition(int minutesFromNow) {
298        final long now = System.currentTimeMillis();
299        final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
300        return timeCondition(now + millis, minutesFromNow);
301    }
302
303    private Condition timeCondition(long time, int minutes) {
304        final int num = minutes < 60 ? minutes : Math.round(minutes / 60f);
305        final int resId = minutes < 60
306                ? R.plurals.zen_mode_duration_minutes
307                : R.plurals.zen_mode_duration_hours;
308        final String caption = mContext.getResources().getQuantityString(resId, num, num);
309        final Uri id = ZenModeConfig.toCountdownConditionId(time);
310        return new Condition(id, caption, "", "", 0, Condition.STATE_TRUE,
311                Condition.FLAG_RELEVANT_NOW);
312    }
313
314    private void handleUpdateConditions(Condition[] conditions) {
315        final int newCount = conditions == null ? 0 : conditions.length;
316        if (DEBUG) Log.d(mTag, "handleUpdateConditions newCount=" + newCount);
317        for (int i = mZenConditions.getChildCount(); i >= newCount + FIRST_CONDITION_INDEX; i--) {
318            mZenConditions.removeViewAt(i);
319        }
320        bind(null, mZenConditions.getChildAt(FOREVER_CONDITION_INDEX));
321        for (int i = 0; i < newCount; i++) {
322            bind(conditions[i], mZenConditions.getChildAt(FIRST_CONDITION_INDEX + i));
323        }
324    }
325
326    private ConditionTag getConditionTagAt(int index) {
327        return (ConditionTag) mZenConditions.getChildAt(index).getTag();
328    }
329
330    private void checkForDefault() {
331        // are we left without anything selected?  if so, set a default
332        for (int i = 0; i < mZenConditions.getChildCount(); i++) {
333            if (getConditionTagAt(i).rb.isChecked()) {
334                if (DEBUG) Log.d(mTag, "Not selecting a default, checked="
335                        + getConditionTagAt(i).conditionId);
336                return;
337            }
338        }
339        if (DEBUG) Log.d(mTag, "Selecting a default");
340        final int favoriteIndex = mFavorites.getMinuteIndex();
341        if (favoriteIndex == -1) {
342            getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
343        } else {
344            final Condition c = newTimeCondition(MINUTE_BUCKETS[favoriteIndex]);
345            mBucketIndex = favoriteIndex;
346            bind(c, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
347            getConditionTagAt(TIME_CONDITION_INDEX).rb.setChecked(true);
348        }
349    }
350
351    private void handleExitConditionChanged(Uri exitCondition) {
352        setExitConditionId(exitCondition);
353        if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitConditionId);
354        final int N = mZenConditions.getChildCount();
355        for (int i = 0; i < N; i++) {
356            final ConditionTag tag = getConditionTagAt(i);
357            tag.rb.setChecked(Objects.equals(tag.conditionId, exitCondition));
358        }
359    }
360
361    private void bind(final Condition condition, View convertView) {
362        final boolean enabled = condition == null || condition.state == Condition.STATE_TRUE;
363        final View row;
364        if (convertView == null) {
365            row = mInflater.inflate(R.layout.zen_mode_condition, this, false);
366            if (DEBUG) Log.d(mTag, "Adding new condition view for: " + condition);
367            mZenConditions.addView(row);
368        } else {
369            row = convertView;
370        }
371        final ConditionTag tag =
372                row.getTag() != null ? (ConditionTag) row.getTag() : new ConditionTag();
373        row.setTag(tag);
374        if (tag.rb == null) {
375            tag.rb = (RadioButton) row.findViewById(android.R.id.checkbox);
376        }
377        tag.conditionId = condition != null ? condition.id : null;
378        tag.rb.setEnabled(enabled);
379        if (mSessionExitConditionId != null && mSessionExitConditionId.equals(tag.conditionId)) {
380            tag.rb.setChecked(true);
381        }
382        tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
383            @Override
384            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
385                if (mExpanded && isChecked) {
386                    if (DEBUG) Log.d(mTag, "onCheckedChanged " + tag.conditionId);
387                    final int N = mZenConditions.getChildCount();
388                    for (int i = 0; i < N; i++) {
389                        ConditionTag childTag = getConditionTagAt(i);
390                        if (childTag == tag) continue;
391                        childTag.rb.setChecked(false);
392                    }
393                    select(tag.conditionId);
394                    fireInteraction();
395                }
396            }
397        });
398        final TextView title = (TextView) row.findViewById(android.R.id.title);
399        if (condition == null) {
400            title.setText(R.string.zen_mode_forever);
401        } else {
402            title.setText(condition.summary);
403        }
404        title.setEnabled(enabled);
405        title.setAlpha(enabled ? 1 : .4f);
406        final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
407        button1.setOnClickListener(new OnClickListener() {
408            @Override
409            public void onClick(View v) {
410                onClickTimeButton(row, tag, false /*down*/);
411            }
412        });
413
414        final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2);
415        button2.setOnClickListener(new OnClickListener() {
416            @Override
417            public void onClick(View v) {
418                onClickTimeButton(row, tag, true /*up*/);
419            }
420        });
421        title.setOnClickListener(new OnClickListener() {
422            @Override
423            public void onClick(View v) {
424                tag.rb.setChecked(true);
425                fireInteraction();
426            }
427        });
428
429        final long time = ZenModeConfig.tryParseCountdownConditionId(tag.conditionId);
430        if (time > 0) {
431            if (mBucketIndex > -1) {
432                button1.setEnabled(mBucketIndex > 0);
433                button2.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1);
434            } else {
435                final long span = time - System.currentTimeMillis();
436                button1.setEnabled(span > MIN_BUCKET_MINUTES * MINUTES_MS);
437                final Condition maxCondition = newTimeCondition(MAX_BUCKET_MINUTES);
438                button2.setEnabled(!Objects.equals(condition.summary, maxCondition.summary));
439            }
440
441            button1.setAlpha(button1.isEnabled() ? 1f : .5f);
442            button2.setAlpha(button2.isEnabled() ? 1f : .5f);
443        } else {
444            button1.setVisibility(View.GONE);
445            button2.setVisibility(View.GONE);
446        }
447    }
448
449    private void onClickTimeButton(View row, ConditionTag tag, boolean up) {
450        Condition newCondition = null;
451        final int N = MINUTE_BUCKETS.length;
452        if (mBucketIndex == -1) {
453            // not on a known index, search for the next or prev bucket by time
454            final long time = ZenModeConfig.tryParseCountdownConditionId(tag.conditionId);
455            final long now = System.currentTimeMillis();
456            for (int i = 0; i < N; i++) {
457                int j = up ? i : N - 1 - i;
458                final int bucketMinutes = MINUTE_BUCKETS[j];
459                final long bucketTime = now + bucketMinutes * MINUTES_MS;
460                if (up && bucketTime > time || !up && bucketTime < time) {
461                    mBucketIndex = j;
462                    newCondition = timeCondition(bucketTime, bucketMinutes);
463                    break;
464                }
465            }
466            if (newCondition == null) {
467                mBucketIndex = DEFAULT_BUCKET_INDEX;
468                newCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
469            }
470        } else {
471            // on a known index, simply increment or decrement
472            mBucketIndex = Math.max(0, Math.min(N - 1, mBucketIndex + (up ? 1 : -1)));
473            newCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
474        }
475        bind(newCondition, row);
476        tag.rb.setChecked(true);
477        select(newCondition.id);
478        fireInteraction();
479    }
480
481    private void select(Uri conditionId) {
482        if (DEBUG) Log.d(mTag, "select " + conditionId);
483        if (mController != null) {
484            mController.setExitConditionId(conditionId);
485        }
486        setExitConditionId(conditionId);
487        if (conditionId == null) {
488            mFavorites.setMinuteIndex(-1);
489        } else if (ZenModeConfig.isValidCountdownConditionId(conditionId) && mBucketIndex != -1) {
490            mFavorites.setMinuteIndex(mBucketIndex);
491        }
492        mSessionExitConditionId = conditionId;
493    }
494
495    private void fireMoreSettings() {
496        if (mCallback != null) {
497            mCallback.onMoreSettings();
498        }
499    }
500
501    private void fireInteraction() {
502        if (mCallback != null) {
503            mCallback.onInteraction();
504        }
505    }
506
507    private void fireExpanded() {
508        if (mCallback != null) {
509            mCallback.onExpanded(mExpanded);
510        }
511    }
512
513    private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
514        @Override
515        public void onZenChanged(int zen) {
516            mHandler.obtainMessage(H.UPDATE_ZEN, zen, 0).sendToTarget();
517        }
518        @Override
519        public void onConditionsChanged(Condition[] conditions) {
520            mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
521        }
522
523        @Override
524        public void onExitConditionChanged(Uri exitConditionId) {
525            mHandler.obtainMessage(H.EXIT_CONDITION_CHANGED, exitConditionId).sendToTarget();
526        }
527    };
528
529    private final class H extends Handler {
530        private static final int UPDATE_CONDITIONS = 1;
531        private static final int EXIT_CONDITION_CHANGED = 2;
532        private static final int UPDATE_ZEN = 3;
533
534        private H() {
535            super(Looper.getMainLooper());
536        }
537
538        @Override
539        public void handleMessage(Message msg) {
540            if (msg.what == UPDATE_CONDITIONS) {
541                handleUpdateConditions((Condition[])msg.obj);
542                checkForDefault();
543            } else if (msg.what == EXIT_CONDITION_CHANGED) {
544                handleExitConditionChanged((Uri)msg.obj);
545            } else if (msg.what == UPDATE_ZEN) {
546                handleUpdateZen(msg.arg1);
547            }
548        }
549    }
550
551    public interface Callback {
552        void onMoreSettings();
553        void onInteraction();
554        void onExpanded(boolean expanded);
555    }
556
557    // used as the view tag on condition rows
558    private static class ConditionTag {
559        RadioButton rb;
560        Uri conditionId;
561    }
562
563    private final class Favorites implements OnSharedPreferenceChangeListener {
564        private static final String KEY_MINUTE_INDEX = "minuteIndex";
565
566        private int mMinuteIndex;
567
568        private Favorites() {
569            prefs().registerOnSharedPreferenceChangeListener(this);
570            updateMinuteIndex();
571        }
572
573        public int getMinuteIndex() {
574            return mMinuteIndex;
575        }
576
577        public void setMinuteIndex(int minuteIndex) {
578            minuteIndex = clamp(minuteIndex);
579            if (minuteIndex == mMinuteIndex) return;
580            mMinuteIndex = clamp(minuteIndex);
581            if (DEBUG) Log.d(mTag, "Setting favorite minute index: " + mMinuteIndex);
582            prefs().edit().putInt(KEY_MINUTE_INDEX, mMinuteIndex).apply();
583        }
584
585        @Override
586        public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
587            updateMinuteIndex();
588        }
589
590        private SharedPreferences prefs() {
591            return mContext.getSharedPreferences(ZenModePanel.class.getSimpleName(), 0);
592        }
593
594        private void updateMinuteIndex() {
595            mMinuteIndex = clamp(prefs().getInt(KEY_MINUTE_INDEX, DEFAULT_BUCKET_INDEX));
596            if (DEBUG) Log.d(mTag, "Favorite minute index: " + mMinuteIndex);
597        }
598
599        private int clamp(int index) {
600            return Math.max(-1, Math.min(MINUTE_BUCKETS.length - 1, index));
601        }
602    }
603
604    private final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
605        @Override
606        public void onSelected(Object value) {
607            if (value != null && mZenButtons.isShown()) {
608                if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + value);
609                mController.setZen((Integer) value);
610            }
611        }
612    };
613}
614