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