StatusBarHeaderView.java revision 723632ea1ae30b33e9c2055194a4464f00c48c61
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 boolean mHasKeyguardUserSwitcher;
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    }
304
305    private void updateSystemIconsLayoutParams() {
306        RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
307        lp.addRule(RelativeLayout.START_OF, mExpanded && !mOverscrolled
308                ? mSettingsButton.getId()
309                : mMultiUserSwitch.getId());
310        lp.removeRule(ALIGN_PARENT_START);
311        if (mMultiUserSwitch.getVisibility() == GONE) {
312            lp.setMarginEnd(mSystemIconsSwitcherHiddenExpandedMargin);
313        } else {
314            lp.setMarginEnd(0);
315        }
316        mSystemIconsSuperContainer.setLayoutParams(lp);
317    }
318
319    private void updateBatteryListening() {
320        if (mListening) {
321            mBatteryController.addStateChangedCallback(this);
322        } else {
323            mBatteryController.removeStateChangedCallback(this);
324        }
325    }
326
327    private void updateAvatarScale() {
328        if (!mExpanded || mOverscrolled) {
329            mMultiUserSwitch.setScaleX(mAvatarCollapsedScaleFactor);
330            mMultiUserSwitch.setScaleY(mAvatarCollapsedScaleFactor);
331        } else {
332            mMultiUserSwitch.setScaleX(1f);
333            mMultiUserSwitch.setScaleY(1f);
334        }
335    }
336
337    private void updateClockScale() {
338        mAmPm.setScaleX(mClockCollapsedScaleFactor);
339        mAmPm.setScaleY(mClockCollapsedScaleFactor);
340        if (!mExpanded || mOverscrolled) {
341            mTime.setScaleX(mClockCollapsedScaleFactor);
342            mTime.setScaleY(mClockCollapsedScaleFactor);
343        } else {
344            mTime.setScaleX(1f);
345            mTime.setScaleY(1f);
346        }
347        updateAmPmTranslation();
348    }
349
350    private void updateAmPmTranslation() {
351        boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
352        mAmPm.setTranslationX((rtl ? 1 : -1) * mTime.getWidth() * (1 - mTime.getScaleX()));
353    }
354
355    private void updateBatteryLevelPaddingEnd() {
356        mBatteryLevel.setPaddingRelative(0, 0,
357                mKeyguardShowing && !mExpanded ? 0 : mBatteryPaddingEnd, 0);
358    }
359
360    @Override
361    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
362        mBatteryLevel.setText(getResources().getString(R.string.battery_level_template, level));
363        boolean changed = mCharging != charging;
364        mCharging = charging;
365        if (changed) {
366            updateVisibilities();
367        }
368    }
369
370    @Override
371    public void onPowerSaveChanged() {
372        // could not care less
373    }
374
375    private void updateClickTargets() {
376        setClickable(!mKeyguardShowing || mExpanded);
377        mDateTime.setClickable(mExpanded);
378
379        boolean keyguardSwitcherAvailable =
380                mHasKeyguardUserSwitcher && mKeyguardShowing && !mExpanded;
381        mMultiUserSwitch.setClickable(mExpanded || keyguardSwitcherAvailable);
382        mMultiUserSwitch.setKeyguardMode(keyguardSwitcherAvailable);
383        mSystemIconsSuperContainer.setClickable(mExpanded);
384    }
385
386    private void updateZTranslation() {
387
388        // If we are on the Keyguard, we need to set our z position to zero, so we don't get
389        // shadows.
390        if (mKeyguardShowing && !mExpanded) {
391            setZ(0);
392        } else {
393            setTranslationZ(0);
394        }
395    }
396
397    private void updatePadding() {
398        boolean padded = !mKeyguardShowing || mExpanded;
399        int padding = padded ? mPadding : 0;
400        setPaddingRelative(padding, 0, padding, 0);
401    }
402
403    private void updateClockLp() {
404        int marginBottom = mExpanded && !mOverscrolled ? mClockMarginBottomExpanded : 0;
405        LayoutParams lp = (LayoutParams) mDateTime.getLayoutParams();
406        int rule = mExpanded && !mOverscrolled ? TRUE : 0;
407        if (marginBottom != lp.bottomMargin
408                || lp.getRules()[RelativeLayout.ALIGN_PARENT_BOTTOM] != rule) {
409            lp.bottomMargin = marginBottom;
410            lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, rule);
411            mDateTime.setLayoutParams(lp);
412        }
413    }
414
415    private void updateMultiUserSwitch() {
416        int marginEnd;
417        if (mExpanded && !mOverscrolled) {
418            marginEnd = mMultiUserExpandedMargin;
419        } else if (mKeyguardShowing) {
420            marginEnd = mMultiUserKeyguardMargin;
421        } else {
422            marginEnd = mMultiUserCollapsedMargin;
423        }
424        int width = mExpanded && !mOverscrolled
425                ? mMultiUserSwitchWidthExpanded
426                : mMultiUserSwitchWidthCollapsed;
427        MarginLayoutParams lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
428        if (marginEnd != lp.getMarginEnd() || lp.width != width) {
429            lp.setMarginEnd(marginEnd);
430            lp.width = width;
431            mMultiUserSwitch.setLayoutParams(lp);
432        }
433    }
434
435    public void setExpansion(float t) {
436        float height = mCollapsedHeight + t * (mExpandedHeight - mCollapsedHeight);
437        if (height < mCollapsedHeight) {
438            height = mCollapsedHeight;
439        }
440        if (height > mExpandedHeight) {
441            height = mExpandedHeight;
442        }
443        setClipping(height);
444    }
445
446    private void setClipping(float height) {
447        mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height);
448        setClipBounds(mClipBounds);
449        invalidateOutline();
450    }
451
452    public void attachSystemIcons(LinearLayout systemIcons) {
453        mSystemIconsContainer.addView(systemIcons);
454        mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
455        mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
456        mSignalCluster.addOnLayoutChangeListener(mStatusIconClipper);
457    }
458
459    public void onSystemIconsDetached() {
460        if (mStatusIcons != null) {
461            mStatusIcons.setVisibility(View.VISIBLE);
462        }
463        if (mSignalCluster != null) {
464            mSignalCluster.removeOnLayoutChangeListener(mStatusIconClipper);
465            mSignalCluster.setVisibility(View.VISIBLE);
466        }
467        mStatusIcons = null;
468        mSignalCluster = null;
469    }
470
471    public void setKeyguardShowing(boolean keyguardShowing) {
472        mKeyguardShowing = keyguardShowing;
473        updateHeights();
474        updateWidth();
475        updateVisibilities();
476        updateZTranslation();
477        updatePadding();
478        updateMultiUserSwitch();
479        updateClickTargets();
480        updateBatteryLevelPaddingEnd();
481        mStatusIconClipper.update();
482    }
483
484    public void setUserInfoController(UserInfoController userInfoController) {
485        userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() {
486            @Override
487            public void onUserInfoChanged(String name, Drawable picture) {
488                mMultiUserAvatar.setImageDrawable(picture);
489            }
490        });
491    }
492
493    @Override
494    public void onClick(View v) {
495        if (v == mSettingsButton) {
496            startSettingsActivity();
497        } else if (v == mSystemIconsSuperContainer) {
498            startBatteryActivity();
499        }
500    }
501
502    private void startSettingsActivity() {
503        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS));
504    }
505
506    private void startBatteryActivity() {
507        mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
508    }
509
510    public void setQSPanel(QSPanel qsp) {
511        mQSPanel = qsp;
512        if (mQSPanel != null) {
513            mQSPanel.setCallback(mQsPanelCallback);
514        }
515        mMultiUserSwitch.setQsPanel(qsp);
516    }
517
518    public void setKeyguarUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
519        mHasKeyguardUserSwitcher = true;
520        mMultiUserSwitch.setKeyguardUserSwitcher(keyguardUserSwitcher);
521    }
522
523    @Override
524    public boolean shouldDelayChildPressedState() {
525        return true;
526    }
527
528    public void setShowEmergencyCallsOnly(boolean show) {
529        mShowEmergencyCallsOnly = show;
530        if (mExpanded) {
531            updateVisibilities();
532        }
533    }
534
535    public void setKeyguardUserSwitcherShowing(boolean showing) {
536        mKeyguardUserSwitcherShowing = showing;
537        updateVisibilities();
538        updateSystemIconsLayoutParams();
539    }
540
541    @Override
542    public boolean hasOverlappingRendering() {
543        return !mKeyguardShowing || mExpanded;
544    }
545
546    @Override
547    protected void dispatchSetPressed(boolean pressed) {
548        // We don't want that everything lights up when we click on the header, so block the request
549        // here.
550    }
551
552    private final class StatusIconClipper implements OnLayoutChangeListener {
553        private final Rect mClipBounds = new Rect();
554
555        @Override
556        public void onLayoutChange(View v, int left, int top, int right,
557                int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
558            // Hide the statusIcons in the header by clipping them.  Can't touch visibility since
559            // they are mirrored to the real status bar.
560            mClipBounds.set(left, 0, mSystemIconsContainer.getWidth(),
561                    mSystemIconsContainer.getHeight());
562            update();
563        }
564
565        public void update() {
566            final boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
567            mSystemIconsContainer.setClipBounds(onKeyguardAndCollapsed ? null : mClipBounds);
568        }
569    }
570
571    private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
572        @Override
573        public void onToggleStateChanged(final boolean state) {
574            post(new Runnable() {
575                @Override
576                public void run() {
577                    handleToggleStateChanged(state);
578                }
579            });
580        }
581
582        @Override
583        public void onShowingDetail(final QSTile.DetailAdapter detail) {
584            post(new Runnable() {
585                @Override
586                public void run() {
587                    handleShowingDetail(detail);
588                }
589            });
590        }
591
592        @Override
593        public void onScanStateChanged(final boolean state) {
594            post(new Runnable() {
595                @Override
596                public void run() {
597                    handleScanStateChanged(state);
598                }
599            });
600        }
601
602        private void handleToggleStateChanged(boolean state) {
603            mQsDetailHeaderSwitch.setChecked(state);
604        }
605
606        private void handleScanStateChanged(boolean state) {
607            // TODO - waiting on framework asset
608        }
609
610        private void handleShowingDetail(final QSTile.DetailAdapter detail) {
611            final boolean showingDetail = detail != null;
612            transition(mDateTime, !showingDetail);
613            transition(mQsDetailHeader, showingDetail);
614            if (showingDetail) {
615                mQsDetailHeaderTitle.setText(detail.getTitle());
616                final Boolean toggleState = detail.getToggleState();
617                if (toggleState == null) {
618                    mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
619                    mQsDetailHeader.setClickable(false);
620                } else {
621                    mQsDetailHeaderSwitch.setVisibility(VISIBLE);
622                    mQsDetailHeaderSwitch.setChecked(toggleState);
623                    mQsDetailHeader.setClickable(true);
624                    mQsDetailHeader.setOnClickListener(new OnClickListener() {
625                        @Override
626                        public void onClick(View v) {
627                            detail.setToggleState(!mQsDetailHeaderSwitch.isChecked());
628                        }
629                    });
630                }
631            } else {
632                mQsDetailHeader.setClickable(false);
633            }
634        }
635
636        private void transition(final View v, final boolean in) {
637            if (in) {
638                v.bringToFront();
639            }
640            v.animate().alpha(in ? 1 : 0).withLayer().start();
641        }
642    };
643}
644