StatusBarHeaderView.java revision bc976e3f4c79a8565ec625564c92d316825f26a7
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.statusbar.phone;
18
19import android.content.Context;
20import android.content.Intent;
21import android.graphics.Outline;
22import android.graphics.Rect;
23import android.graphics.drawable.Drawable;
24import android.util.AttributeSet;
25import android.view.View;
26import android.view.ViewGroup;
27import android.view.ViewOutlineProvider;
28import android.widget.ImageView;
29import android.widget.LinearLayout;
30import android.widget.RelativeLayout;
31import android.widget.Switch;
32import android.widget.TextView;
33
34import com.android.systemui.R;
35import com.android.systemui.qs.QSPanel;
36import com.android.systemui.qs.QSTile;
37import com.android.systemui.statusbar.policy.BatteryController;
38import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
39import com.android.systemui.statusbar.policy.UserInfoController;
40
41/**
42 * The view to manage the header area in the expanded status bar.
43 */
44public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener,
45        BatteryController.BatteryStateChangeCallback {
46
47    private boolean mExpanded;
48    private boolean mListening;
49    private boolean mOverscrolled;
50    private boolean mKeyguardShowing;
51    private boolean mCharging;
52
53    private ViewGroup mSystemIconsContainer;
54    private View mSystemIconsSuperContainer;
55    private View mDateTime;
56    private View mTime;
57    private View mAmPm;
58    private View mKeyguardCarrierText;
59    private MultiUserSwitch mMultiUserSwitch;
60    private ImageView mMultiUserAvatar;
61    private View mDateCollapsed;
62    private View mDateExpanded;
63    private View mStatusIcons;
64    private View mSignalCluster;
65    private View mSettingsButton;
66    private View mQsDetailHeader;
67    private TextView mQsDetailHeaderTitle;
68    private Switch mQsDetailHeaderSwitch;
69    private View mEmergencyCallsOnly;
70    private TextView mBatteryLevel;
71
72    private boolean mShowEmergencyCallsOnly;
73    private boolean mKeyguardUserSwitcherShowing;
74
75    private int mCollapsedHeight;
76    private int mExpandedHeight;
77    private int mKeyguardHeight;
78
79    private int mKeyguardWidth = ViewGroup.LayoutParams.MATCH_PARENT;
80    private int mNormalWidth;
81    private int mPadding;
82    private int mMultiUserExpandedMargin;
83    private int mMultiUserCollapsedMargin;
84    private int mMultiUserKeyguardMargin;
85    private int mSystemIconsSwitcherHiddenExpandedMargin;
86    private int mClockMarginBottomExpanded;
87    private int mMultiUserSwitchWidthCollapsed;
88    private int mMultiUserSwitchWidthExpanded;
89    private int mBatteryPaddingEnd;
90
91    /**
92     * In collapsed QS, the clock and avatar are scaled down a bit post-layout to allow for a nice
93     * transition. These values determine that factor.
94     */
95    private float mClockCollapsedScaleFactor;
96    private float mAvatarCollapsedScaleFactor;
97
98    private ActivityStarter mActivityStarter;
99    private BatteryController mBatteryController;
100    private QSPanel mQSPanel;
101    private KeyguardUserSwitcher mKeyguardUserSwitcher;
102
103    private final Rect mClipBounds = new Rect();
104    private final StatusIconClipper mStatusIconClipper = new StatusIconClipper();
105
106    public StatusBarHeaderView(Context context, AttributeSet attrs) {
107        super(context, attrs);
108    }
109
110    @Override
111    protected void onFinishInflate() {
112        super.onFinishInflate();
113        mSystemIconsSuperContainer = findViewById(R.id.system_icons_super_container);
114        mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container);
115        mSystemIconsSuperContainer.setOnClickListener(this);
116        mDateTime = findViewById(R.id.datetime);
117        mTime = findViewById(R.id.time_view);
118        mAmPm = findViewById(R.id.am_pm_view);
119        mKeyguardCarrierText = findViewById(R.id.keyguard_carrier_text);
120        mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
121        mMultiUserAvatar = (ImageView) findViewById(R.id.multi_user_avatar);
122        mDateCollapsed = findViewById(R.id.date_collapsed);
123        mDateExpanded = findViewById(R.id.date_expanded);
124        mSettingsButton = findViewById(R.id.settings_button);
125        mSettingsButton.setOnClickListener(this);
126        mQsDetailHeader = findViewById(R.id.qs_detail_header);
127        mQsDetailHeader.setAlpha(0);
128        mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
129        mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
130        mEmergencyCallsOnly = findViewById(R.id.header_emergency_calls_only);
131        mBatteryLevel = (TextView) findViewById(R.id.battery_level);
132        loadDimens();
133        updateVisibilities();
134        updateClockScale();
135        updateAvatarScale();
136        addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
137            @Override
138            public void onLayoutChange(View v, int left, int top, int right,
139                    int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
140                if ((right - left) != (oldRight - oldLeft)) {
141                    // width changed, update clipping
142                    setClipping(getHeight());
143                }
144                boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
145                mTime.setPivotX(rtl ? mTime.getWidth() : 0);
146                mTime.setPivotY(mTime.getBaseline());
147                mAmPm.setPivotX(rtl ? mAmPm.getWidth() : 0);
148                mAmPm.setPivotY(mAmPm.getBaseline());
149                updateAmPmTranslation();
150            }
151        });
152        setOutlineProvider(new ViewOutlineProvider() {
153            @Override
154            public void getOutline(View view, Outline outline) {
155                outline.setRect(mClipBounds);
156            }
157        });
158    }
159
160    private void loadDimens() {
161        mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height);
162        mExpandedHeight = getResources().getDimensionPixelSize(
163                R.dimen.status_bar_header_height_expanded);
164        mKeyguardHeight = getResources().getDimensionPixelSize(
165                R.dimen.status_bar_header_height_keyguard);
166        mNormalWidth = getLayoutParams().width;
167        mPadding = getResources().getDimensionPixelSize(R.dimen.notification_side_padding);
168        mMultiUserExpandedMargin =
169                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_expanded_margin);
170        mMultiUserCollapsedMargin =
171                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_collapsed_margin);
172        mMultiUserKeyguardMargin =
173                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_keyguard_margin);
174        mSystemIconsSwitcherHiddenExpandedMargin = getResources().getDimensionPixelSize(
175                R.dimen.system_icons_switcher_hidden_expanded_margin);
176        mClockMarginBottomExpanded =
177                getResources().getDimensionPixelSize(R.dimen.clock_expanded_bottom_margin);
178        mMultiUserSwitchWidthCollapsed =
179                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_collapsed);
180        mMultiUserSwitchWidthExpanded =
181                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_expanded);
182        mAvatarCollapsedScaleFactor =
183                getResources().getDimensionPixelSize(R.dimen.multi_user_avatar_collapsed_size)
184                / (float) mMultiUserAvatar.getLayoutParams().width;
185        mClockCollapsedScaleFactor =
186                (float) getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size)
187                / (float) getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size);
188        mBatteryPaddingEnd =
189                getResources().getDimensionPixelSize(R.dimen.battery_level_padding_end);
190    }
191
192    public void setActivityStarter(ActivityStarter activityStarter) {
193        mActivityStarter = activityStarter;
194    }
195
196    public void setBatteryController(BatteryController batteryController) {
197        mBatteryController = batteryController;
198    }
199
200    public int getCollapsedHeight() {
201        return mKeyguardShowing ? mKeyguardHeight : mCollapsedHeight;
202    }
203
204    public int getExpandedHeight() {
205        return mExpandedHeight;
206    }
207
208    public void setListening(boolean listening) {
209        if (listening == mListening) {
210            return;
211        }
212        mListening = listening;
213        updateBatteryListening();
214    }
215
216    public void setExpanded(boolean expanded, boolean overscrolled) {
217        boolean changed = expanded != mExpanded;
218        boolean overscrollChanged = overscrolled != mOverscrolled;
219        mExpanded = expanded;
220        mOverscrolled = overscrolled;
221        if (changed || overscrollChanged) {
222            updateHeights();
223            updateVisibilities();
224            updateSystemIconsLayoutParams();
225            updateZTranslation();
226            updateClickTargets();
227            updateWidth();
228            updatePadding();
229            updateMultiUserSwitch();
230            if (mQSPanel != null) {
231                mQSPanel.setExpanded(expanded && !overscrolled);
232            }
233            updateClockScale();
234            updateAvatarScale();
235            updateClockLp();
236            updateBatteryLevelPaddingEnd();
237            mStatusIconClipper.update();
238        }
239    }
240
241    private void updateHeights() {
242        boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
243        int height;
244        if (mExpanded) {
245            height = mExpandedHeight;
246        } else if (onKeyguardAndCollapsed) {
247            height = mKeyguardHeight;
248        } else {
249            height = mCollapsedHeight;
250        }
251        ViewGroup.LayoutParams lp = getLayoutParams();
252        if (lp.height != height) {
253            lp.height = height;
254            setLayoutParams(lp);
255        }
256        int systemIconsContainerHeight = onKeyguardAndCollapsed ? mKeyguardHeight : mCollapsedHeight;
257        lp = mSystemIconsSuperContainer.getLayoutParams();
258        if (lp.height != systemIconsContainerHeight) {
259            lp.height = systemIconsContainerHeight;
260            mSystemIconsSuperContainer.setLayoutParams(lp);
261        }
262        lp = mMultiUserSwitch.getLayoutParams();
263        if (lp.height != systemIconsContainerHeight) {
264            lp.height = systemIconsContainerHeight;
265            mMultiUserSwitch.setLayoutParams(lp);
266        }
267    }
268
269    private void updateWidth() {
270        int width = (mKeyguardShowing && !mExpanded) ? mKeyguardWidth : mNormalWidth;
271        ViewGroup.LayoutParams lp = getLayoutParams();
272        if (width != lp.width) {
273            lp.width = width;
274            setLayoutParams(lp);
275        }
276    }
277
278    private void updateVisibilities() {
279        boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
280        if (onKeyguardAndCollapsed) {
281            setBackground(null);
282        } else {
283            setBackgroundResource(R.drawable.notification_header_bg);
284        }
285        mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
286        mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE);
287        mDateCollapsed.setVisibility(mExpanded && !mOverscrolled ? View.GONE : View.VISIBLE);
288        mDateExpanded.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
289        mSettingsButton.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
290        mQsDetailHeader.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
291        if (mStatusIcons != null) {
292            mStatusIcons.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
293        }
294        if (mSignalCluster != null) {
295            mSignalCluster.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
296        }
297        mEmergencyCallsOnly.setVisibility(mExpanded && !mOverscrolled && mShowEmergencyCallsOnly
298                ? VISIBLE : GONE);
299        mMultiUserSwitch.setVisibility(mExpanded || !mKeyguardUserSwitcherShowing
300                ? VISIBLE : GONE);
301        mBatteryLevel.setVisibility(mKeyguardShowing && mCharging || mExpanded && !mOverscrolled
302                ? View.VISIBLE : View.GONE);
303        if (mExpanded && !mOverscrolled && mKeyguardUserSwitcherShowing) {
304            mKeyguardUserSwitcher.hide();
305        }
306    }
307
308    private void updateSystemIconsLayoutParams() {
309        RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
310        lp.addRule(RelativeLayout.START_OF, mExpanded && !mOverscrolled
311                ? mSettingsButton.getId()
312                : mMultiUserSwitch.getId());
313        lp.removeRule(ALIGN_PARENT_START);
314        if (mMultiUserSwitch.getVisibility() == GONE) {
315            lp.setMarginEnd(mSystemIconsSwitcherHiddenExpandedMargin);
316        } else {
317            lp.setMarginEnd(0);
318        }
319        mSystemIconsSuperContainer.setLayoutParams(lp);
320    }
321
322    private void updateBatteryListening() {
323        if (mListening) {
324            mBatteryController.addStateChangedCallback(this);
325        } else {
326            mBatteryController.removeStateChangedCallback(this);
327        }
328    }
329
330    private void updateAvatarScale() {
331        if (!mExpanded || mOverscrolled) {
332            mMultiUserSwitch.setScaleX(mAvatarCollapsedScaleFactor);
333            mMultiUserSwitch.setScaleY(mAvatarCollapsedScaleFactor);
334        } else {
335            mMultiUserSwitch.setScaleX(1f);
336            mMultiUserSwitch.setScaleY(1f);
337        }
338    }
339
340    private void updateClockScale() {
341        mAmPm.setScaleX(mClockCollapsedScaleFactor);
342        mAmPm.setScaleY(mClockCollapsedScaleFactor);
343        if (!mExpanded || mOverscrolled) {
344            mTime.setScaleX(mClockCollapsedScaleFactor);
345            mTime.setScaleY(mClockCollapsedScaleFactor);
346        } else {
347            mTime.setScaleX(1f);
348            mTime.setScaleY(1f);
349        }
350        updateAmPmTranslation();
351    }
352
353    private void updateAmPmTranslation() {
354        boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
355        mAmPm.setTranslationX((rtl ? 1 : -1) * mTime.getWidth() * (1 - mTime.getScaleX()));
356    }
357
358    private void updateBatteryLevelPaddingEnd() {
359        mBatteryLevel.setPaddingRelative(0, 0,
360                mKeyguardShowing && !mExpanded ? 0 : mBatteryPaddingEnd, 0);
361    }
362
363    @Override
364    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
365        mBatteryLevel.setText(getResources().getString(R.string.battery_level_template, level));
366        boolean changed = mCharging != charging;
367        mCharging = charging;
368        if (changed) {
369            updateVisibilities();
370        }
371    }
372
373    @Override
374    public void onPowerSaveChanged() {
375        // could not care less
376    }
377
378    private void updateClickTargets() {
379        setClickable(!mKeyguardShowing || mExpanded);
380        mDateTime.setClickable(mExpanded);
381
382        boolean keyguardSwitcherAvailable =
383                mKeyguardUserSwitcher != null && mKeyguardShowing && !mExpanded;
384        mMultiUserSwitch.setClickable(mExpanded || keyguardSwitcherAvailable);
385        mMultiUserSwitch.setKeyguardMode(keyguardSwitcherAvailable);
386        mSystemIconsSuperContainer.setClickable(mExpanded);
387    }
388
389    private void updateZTranslation() {
390
391        // If we are on the Keyguard, we need to set our z position to zero, so we don't get
392        // shadows.
393        if (mKeyguardShowing && !mExpanded) {
394            setZ(0);
395        } else {
396            setTranslationZ(0);
397        }
398    }
399
400    private void updatePadding() {
401        boolean padded = !mKeyguardShowing || mExpanded;
402        int padding = padded ? mPadding : 0;
403        setPaddingRelative(padding, 0, padding, 0);
404    }
405
406    private void updateClockLp() {
407        int marginBottom = mExpanded && !mOverscrolled ? mClockMarginBottomExpanded : 0;
408        LayoutParams lp = (LayoutParams) mDateTime.getLayoutParams();
409        int rule = mExpanded && !mOverscrolled ? TRUE : 0;
410        if (marginBottom != lp.bottomMargin
411                || lp.getRules()[RelativeLayout.ALIGN_PARENT_BOTTOM] != rule) {
412            lp.bottomMargin = marginBottom;
413            lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, rule);
414            mDateTime.setLayoutParams(lp);
415        }
416    }
417
418    private void updateMultiUserSwitch() {
419        int marginEnd;
420        if (mExpanded && !mOverscrolled) {
421            marginEnd = mMultiUserExpandedMargin;
422        } else if (mKeyguardShowing) {
423            marginEnd = mMultiUserKeyguardMargin;
424        } else {
425            marginEnd = mMultiUserCollapsedMargin;
426        }
427        int width = mExpanded && !mOverscrolled
428                ? mMultiUserSwitchWidthExpanded
429                : mMultiUserSwitchWidthCollapsed;
430        MarginLayoutParams lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
431        if (marginEnd != lp.getMarginEnd() || lp.width != width) {
432            lp.setMarginEnd(marginEnd);
433            lp.width = width;
434            mMultiUserSwitch.setLayoutParams(lp);
435        }
436    }
437
438    public void setExpansion(float t) {
439        float height = mCollapsedHeight + t * (mExpandedHeight - mCollapsedHeight);
440        if (height < mCollapsedHeight) {
441            height = mCollapsedHeight;
442        }
443        if (height > mExpandedHeight) {
444            height = mExpandedHeight;
445        }
446        setClipping(height);
447    }
448
449    private void setClipping(float height) {
450        mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height);
451        setClipBounds(mClipBounds);
452        invalidateOutline();
453    }
454
455    public void attachSystemIcons(LinearLayout systemIcons) {
456        mSystemIconsContainer.addView(systemIcons);
457        mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
458        mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
459        mSignalCluster.addOnLayoutChangeListener(mStatusIconClipper);
460    }
461
462    public void onSystemIconsDetached() {
463        if (mStatusIcons != null) {
464            mStatusIcons.setVisibility(View.VISIBLE);
465        }
466        if (mSignalCluster != null) {
467            mSignalCluster.removeOnLayoutChangeListener(mStatusIconClipper);
468            mSignalCluster.setVisibility(View.VISIBLE);
469        }
470        mStatusIcons = null;
471        mSignalCluster = null;
472    }
473
474    public void setKeyguardShowing(boolean keyguardShowing) {
475        mKeyguardShowing = keyguardShowing;
476        updateHeights();
477        updateWidth();
478        updateVisibilities();
479        updateZTranslation();
480        updatePadding();
481        updateMultiUserSwitch();
482        updateClickTargets();
483        updateBatteryLevelPaddingEnd();
484        mStatusIconClipper.update();
485    }
486
487    public void setUserInfoController(UserInfoController userInfoController) {
488        userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() {
489            @Override
490            public void onUserInfoChanged(String name, Drawable picture) {
491                mMultiUserAvatar.setImageDrawable(picture);
492            }
493        });
494    }
495
496    @Override
497    public void onClick(View v) {
498        if (v == mSettingsButton) {
499            startSettingsActivity();
500        } else if (v == mSystemIconsSuperContainer) {
501            startBatteryActivity();
502        }
503    }
504
505    private void startSettingsActivity() {
506        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS));
507    }
508
509    private void startBatteryActivity() {
510        mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
511    }
512
513    public void setQSPanel(QSPanel qsp) {
514        mQSPanel = qsp;
515        if (mQSPanel != null) {
516            mQSPanel.setCallback(mQsPanelCallback);
517        }
518        mMultiUserSwitch.setQsPanel(qsp);
519    }
520
521    public void setKeyguarUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
522        mKeyguardUserSwitcher = keyguardUserSwitcher;
523        mMultiUserSwitch.setKeyguardUserSwitcher(keyguardUserSwitcher);
524    }
525
526    @Override
527    public boolean shouldDelayChildPressedState() {
528        return true;
529    }
530
531    public void setShowEmergencyCallsOnly(boolean show) {
532        mShowEmergencyCallsOnly = show;
533        if (mExpanded) {
534            updateVisibilities();
535        }
536    }
537
538    public void setKeyguardUserSwitcherShowing(boolean showing) {
539        mKeyguardUserSwitcherShowing = showing;
540        updateVisibilities();
541        updateSystemIconsLayoutParams();
542    }
543
544    @Override
545    public boolean hasOverlappingRendering() {
546        return !mKeyguardShowing || mExpanded;
547    }
548
549    @Override
550    protected void dispatchSetPressed(boolean pressed) {
551        // We don't want that everything lights up when we click on the header, so block the request
552        // here.
553    }
554
555    private final class StatusIconClipper implements OnLayoutChangeListener {
556        private final Rect mClipBounds = new Rect();
557
558        @Override
559        public void onLayoutChange(View v, int left, int top, int right,
560                int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
561            // Hide the statusIcons in the header by clipping them.  Can't touch visibility since
562            // they are mirrored to the real status bar.
563            mClipBounds.set(left, 0, mSystemIconsContainer.getWidth(),
564                    mSystemIconsContainer.getHeight());
565            update();
566        }
567
568        public void update() {
569            final boolean collapsedKeyguard = mKeyguardShowing && !mExpanded;
570            final boolean expanded = mExpanded && !mOverscrolled;
571            mSystemIconsContainer.setClipBounds(collapsedKeyguard || expanded ? null : mClipBounds);
572        }
573    }
574
575    private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
576        @Override
577        public void onToggleStateChanged(final boolean state) {
578            post(new Runnable() {
579                @Override
580                public void run() {
581                    handleToggleStateChanged(state);
582                }
583            });
584        }
585
586        @Override
587        public void onShowingDetail(final QSTile.DetailAdapter detail) {
588            post(new Runnable() {
589                @Override
590                public void run() {
591                    handleShowingDetail(detail);
592                }
593            });
594        }
595
596        @Override
597        public void onScanStateChanged(final boolean state) {
598            post(new Runnable() {
599                @Override
600                public void run() {
601                    handleScanStateChanged(state);
602                }
603            });
604        }
605
606        private void handleToggleStateChanged(boolean state) {
607            mQsDetailHeaderSwitch.setChecked(state);
608        }
609
610        private void handleScanStateChanged(boolean state) {
611            // TODO - waiting on framework asset
612        }
613
614        private void handleShowingDetail(final QSTile.DetailAdapter detail) {
615            final boolean showingDetail = detail != null;
616            transition(mDateTime, !showingDetail);
617            transition(mQsDetailHeader, showingDetail);
618            if (showingDetail) {
619                mQsDetailHeaderTitle.setText(detail.getTitle());
620                final Boolean toggleState = detail.getToggleState();
621                if (toggleState == null) {
622                    mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
623                    mQsDetailHeader.setClickable(false);
624                } else {
625                    mQsDetailHeaderSwitch.setVisibility(VISIBLE);
626                    mQsDetailHeaderSwitch.setChecked(toggleState);
627                    mQsDetailHeader.setClickable(true);
628                    mQsDetailHeader.setOnClickListener(new OnClickListener() {
629                        @Override
630                        public void onClick(View v) {
631                            detail.setToggleState(!mQsDetailHeaderSwitch.isChecked());
632                        }
633                    });
634                }
635            } else {
636                mQsDetailHeader.setClickable(false);
637            }
638        }
639
640        private void transition(final View v, final boolean in) {
641            if (in) {
642                v.bringToFront();
643            }
644            v.animate().alpha(in ? 1 : 0).withLayer().start();
645        }
646    };
647}
648