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