StatusBarHeaderView.java revision 11c071a5506727e0845201a1c9dcba5782ece4ba
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.app.AlarmManager;
20import android.app.PendingIntent;
21import android.content.Context;
22import android.content.Intent;
23import android.graphics.Outline;
24import android.graphics.Rect;
25import android.graphics.drawable.Drawable;
26import android.util.AttributeSet;
27import android.util.TypedValue;
28import android.view.View;
29import android.view.ViewGroup;
30import android.view.ViewOutlineProvider;
31import android.widget.ImageView;
32import android.widget.LinearLayout;
33import android.widget.RelativeLayout;
34import android.widget.Switch;
35import android.widget.TextView;
36
37import com.android.keyguard.KeyguardStatusView;
38import com.android.systemui.R;
39import com.android.systemui.qs.QSPanel;
40import com.android.systemui.qs.QSTile;
41import com.android.systemui.statusbar.policy.BatteryController;
42import com.android.systemui.statusbar.policy.NextAlarmController;
43import com.android.systemui.statusbar.policy.UserInfoController;
44
45/**
46 * The view to manage the header area in the expanded status bar.
47 */
48public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener,
49        BatteryController.BatteryStateChangeCallback, NextAlarmController.NextAlarmChangeCallback {
50
51    private boolean mExpanded;
52    private boolean mListening;
53
54    private ViewGroup mSystemIconsContainer;
55    private View mSystemIconsSuperContainer;
56    private View mDateGroup;
57    private View mClock;
58    private TextView mTime;
59    private View mAmPm;
60    private MultiUserSwitch mMultiUserSwitch;
61    private ImageView mMultiUserAvatar;
62    private View mDateCollapsed;
63    private View mDateExpanded;
64    private LinearLayout mSystemIcons;
65    private View mStatusIcons;
66    private View mSignalCluster;
67    private View mSettingsButton;
68    private View mQsDetailHeader;
69    private TextView mQsDetailHeaderTitle;
70    private Switch mQsDetailHeaderSwitch;
71    private View mEmergencyCallsOnly;
72    private TextView mBatteryLevel;
73    private TextView mAlarmStatus;
74
75    private boolean mShowEmergencyCallsOnly;
76    private boolean mAlarmShowing;
77    private AlarmManager.AlarmClockInfo mNextAlarm;
78
79    private int mCollapsedHeight;
80    private int mExpandedHeight;
81
82    private int mMultiUserExpandedMargin;
83    private int mMultiUserCollapsedMargin;
84
85    private int mClockMarginBottomExpanded;
86    private int mClockMarginBottomCollapsed;
87    private int mMultiUserSwitchWidthCollapsed;
88    private int mMultiUserSwitchWidthExpanded;
89
90    private int mClockCollapsedSize;
91    private int mClockExpandedSize;
92
93    /**
94     * In collapsed QS, the clock and avatar are scaled down a bit post-layout to allow for a nice
95     * transition. These values determine that factor.
96     */
97    private float mClockCollapsedScaleFactor;
98    private float mAvatarCollapsedScaleFactor;
99
100    private ActivityStarter mActivityStarter;
101    private BatteryController mBatteryController;
102    private NextAlarmController mNextAlarmController;
103    private QSPanel mQSPanel;
104
105
106    private final Rect mClipBounds = new Rect();
107
108    private boolean mCaptureValues;
109    private boolean mSignalClusterDetached;
110    private final LayoutValues mCollapsedValues = new LayoutValues();
111    private final LayoutValues mExpandedValues = new LayoutValues();
112    private final LayoutValues mCurrentValues = new LayoutValues();
113
114    private float mCurrentT;
115
116    public StatusBarHeaderView(Context context, AttributeSet attrs) {
117        super(context, attrs);
118    }
119
120    @Override
121    protected void onFinishInflate() {
122        super.onFinishInflate();
123        mSystemIconsSuperContainer = findViewById(R.id.system_icons_super_container);
124        mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container);
125        mSystemIconsSuperContainer.setOnClickListener(this);
126        mDateGroup = findViewById(R.id.date_group);
127        mClock = findViewById(R.id.clock);
128        mTime = (TextView) findViewById(R.id.time_view);
129        mAmPm = findViewById(R.id.am_pm_view);
130        mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
131        mMultiUserAvatar = (ImageView) findViewById(R.id.multi_user_avatar);
132        mDateCollapsed = findViewById(R.id.date_collapsed);
133        mDateExpanded = findViewById(R.id.date_expanded);
134        mSettingsButton = findViewById(R.id.settings_button);
135        mSettingsButton.setOnClickListener(this);
136        mQsDetailHeader = findViewById(R.id.qs_detail_header);
137        mQsDetailHeader.setAlpha(0);
138        mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
139        mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
140        mEmergencyCallsOnly = findViewById(R.id.header_emergency_calls_only);
141        mBatteryLevel = (TextView) findViewById(R.id.battery_level);
142        mAlarmStatus = (TextView) findViewById(R.id.alarm_status);
143        mAlarmStatus.setOnClickListener(this);
144        loadDimens();
145        updateVisibilities();
146        updateClockScale();
147        updateAvatarScale();
148        addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
149            @Override
150            public void onLayoutChange(View v, int left, int top, int right,
151                    int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
152                if ((right - left) != (oldRight - oldLeft)) {
153                    // width changed, update clipping
154                    setClipping(getHeight());
155                }
156                boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
157                mTime.setPivotX(rtl ? mTime.getWidth() : 0);
158                mTime.setPivotY(mTime.getBaseline());
159                updateAmPmTranslation();
160            }
161        });
162        setOutlineProvider(new ViewOutlineProvider() {
163            @Override
164            public void getOutline(View view, Outline outline) {
165                outline.setRect(mClipBounds);
166            }
167        });
168        requestCaptureValues();
169    }
170
171    @Override
172    protected void onLayout(boolean changed, int l, int t, int r, int b) {
173        super.onLayout(changed, l, t, r, b);
174        if (mCaptureValues) {
175            if (mExpanded) {
176                captureLayoutValues(mExpandedValues);
177            } else {
178                captureLayoutValues(mCollapsedValues);
179            }
180            mCaptureValues = false;
181            updateLayoutValues(mCurrentT);
182        }
183        mAlarmStatus.setX(mDateGroup.getLeft() + mDateCollapsed.getRight());
184    }
185
186    private void requestCaptureValues() {
187        mCaptureValues = true;
188        requestLayout();
189    }
190
191    private void loadDimens() {
192        mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height);
193        mExpandedHeight = getResources().getDimensionPixelSize(
194                R.dimen.status_bar_header_height_expanded);
195        mMultiUserExpandedMargin =
196                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_expanded_margin);
197        mMultiUserCollapsedMargin =
198                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_collapsed_margin);
199
200        mClockMarginBottomExpanded =
201                getResources().getDimensionPixelSize(R.dimen.clock_expanded_bottom_margin);
202        mClockMarginBottomCollapsed =
203                getResources().getDimensionPixelSize(R.dimen.clock_collapsed_bottom_margin);
204        mMultiUserSwitchWidthCollapsed =
205                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_collapsed);
206        mMultiUserSwitchWidthExpanded =
207                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_expanded);
208        mAvatarCollapsedScaleFactor =
209                getResources().getDimensionPixelSize(R.dimen.multi_user_avatar_collapsed_size)
210                / (float) mMultiUserAvatar.getLayoutParams().width;
211        mClockCollapsedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size);
212        mClockExpandedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size);
213        mClockCollapsedScaleFactor = (float) mClockCollapsedSize / (float) mClockExpandedSize;
214
215    }
216
217    public void setActivityStarter(ActivityStarter activityStarter) {
218        mActivityStarter = activityStarter;
219    }
220
221    public void setBatteryController(BatteryController batteryController) {
222        mBatteryController = batteryController;
223    }
224
225    public void setNextAlarmController(NextAlarmController nextAlarmController) {
226        mNextAlarmController = nextAlarmController;
227    }
228
229    public int getCollapsedHeight() {
230        return mCollapsedHeight;
231    }
232
233    public int getExpandedHeight() {
234        return mExpandedHeight;
235    }
236
237    public void setListening(boolean listening) {
238        if (listening == mListening) {
239            return;
240        }
241        mListening = listening;
242        updateListeners();
243    }
244
245    public void setExpanded(boolean expanded) {
246        boolean changed = expanded != mExpanded;
247        mExpanded = expanded;
248        if (changed) {
249            updateEverything();
250        }
251    }
252
253    public void updateEverything() {
254        updateHeights();
255        updateVisibilities();
256        updateSystemIconsLayoutParams();
257        updateClickTargets();
258        updateMultiUserSwitch();
259        if (mQSPanel != null) {
260            mQSPanel.setExpanded(mExpanded);
261        }
262        updateClockScale();
263        updateAvatarScale();
264        updateClockLp();
265        requestCaptureValues();
266    }
267
268    private void updateHeights() {
269        int height = mExpanded ? mExpandedHeight : mCollapsedHeight;
270        ViewGroup.LayoutParams lp = getLayoutParams();
271        if (lp.height != height) {
272            lp.height = height;
273            setLayoutParams(lp);
274        }
275    }
276
277    private void updateVisibilities() {
278        mDateCollapsed.setVisibility(mExpanded && mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
279        mDateExpanded.setVisibility(mExpanded && mAlarmShowing ? View.INVISIBLE : View.VISIBLE);
280        mAlarmStatus.setVisibility(mExpanded && mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
281        mSettingsButton.setVisibility(mExpanded ? View.VISIBLE : View.INVISIBLE);
282        mQsDetailHeader.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
283        if (mSignalCluster != null) {
284            updateSignalClusterDetachment();
285        }
286        mEmergencyCallsOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly ? VISIBLE : GONE);
287        mBatteryLevel.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
288    }
289
290    private void updateSignalClusterDetachment() {
291        boolean detached = mExpanded;
292        if (detached != mSignalClusterDetached) {
293            if (detached) {
294                getOverlay().add(mSignalCluster);
295            } else {
296                reattachSignalCluster();
297            }
298        }
299        mSignalClusterDetached = detached;
300    }
301
302    private void reattachSignalCluster() {
303        getOverlay().remove(mSignalCluster);
304        mSystemIcons.addView(mSignalCluster, 1);
305    }
306
307    private void updateSystemIconsLayoutParams() {
308        RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
309        int rule = mExpanded
310                ? mSettingsButton.getId()
311                : mMultiUserSwitch.getId();
312        if (rule != lp.getRules()[RelativeLayout.START_OF]) {
313            lp.addRule(RelativeLayout.START_OF, rule);
314            mSystemIconsSuperContainer.setLayoutParams(lp);
315        }
316    }
317
318    private void updateListeners() {
319        if (mListening) {
320            mBatteryController.addStateChangedCallback(this);
321            mNextAlarmController.addStateChangedCallback(this);
322        } else {
323            mBatteryController.removeStateChangedCallback(this);
324            mNextAlarmController.removeStateChangedCallback(this);
325        }
326    }
327
328    private void updateAvatarScale() {
329        if (mExpanded) {
330            mMultiUserAvatar.setScaleX(1f);
331            mMultiUserAvatar.setScaleY(1f);
332        } else {
333            mMultiUserAvatar.setScaleX(mAvatarCollapsedScaleFactor);
334            mMultiUserAvatar.setScaleY(mAvatarCollapsedScaleFactor);
335        }
336    }
337
338    private void updateClockScale() {
339        mTime.setTextSize(TypedValue.COMPLEX_UNIT_PX, mExpanded
340                ? mClockExpandedSize
341                : mClockCollapsedSize);
342        mTime.setScaleX(1f);
343        mTime.setScaleY(1f);
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    @Override
353    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
354        mBatteryLevel.setText(getResources().getString(R.string.battery_level_template, level));
355    }
356
357    @Override
358    public void onPowerSaveChanged() {
359        // could not care less
360    }
361
362    @Override
363    public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
364        mNextAlarm = nextAlarm;
365        if (nextAlarm != null) {
366            mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
367        }
368        mAlarmShowing = nextAlarm != null;
369        updateEverything();
370        requestCaptureValues();
371    }
372
373    private void updateClickTargets() {
374        mMultiUserSwitch.setClickable(mExpanded);
375        mSystemIconsSuperContainer.setClickable(mExpanded);
376        mAlarmStatus.setClickable(mNextAlarm != null && mNextAlarm.getShowIntent() != null);
377    }
378
379    private void updateClockLp() {
380        int marginBottom = mExpanded
381                ? mClockMarginBottomExpanded
382                : mClockMarginBottomCollapsed;
383        LayoutParams lp = (LayoutParams) mDateGroup.getLayoutParams();
384        if (marginBottom != lp.bottomMargin) {
385            lp.bottomMargin = marginBottom;
386            mDateGroup.setLayoutParams(lp);
387        }
388    }
389
390    private void updateMultiUserSwitch() {
391        int marginEnd;
392        int width;
393        if (mExpanded) {
394            marginEnd = mMultiUserExpandedMargin;
395            width = mMultiUserSwitchWidthExpanded;
396        } else {
397            marginEnd = mMultiUserCollapsedMargin;
398            width = mMultiUserSwitchWidthCollapsed;
399        }
400        MarginLayoutParams lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
401        if (marginEnd != lp.getMarginEnd() || lp.width != width) {
402            lp.setMarginEnd(marginEnd);
403            lp.width = width;
404            mMultiUserSwitch.setLayoutParams(lp);
405        }
406    }
407
408    public void setExpansion(float t) {
409        if (!mExpanded) {
410            t = 0f;
411        }
412        mCurrentT = 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        updateLayoutValues(t);
422    }
423
424    private void updateLayoutValues(float t) {
425        if (mCaptureValues) {
426            return;
427        }
428        mCurrentValues.interpoloate(mCollapsedValues, mExpandedValues, t);
429        applyLayoutValues(mCurrentValues);
430    }
431
432    private void setClipping(float height) {
433        mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height);
434        setClipBounds(mClipBounds);
435        invalidateOutline();
436    }
437
438    public void attachSystemIcons(LinearLayout systemIcons) {
439        mSystemIconsContainer.addView(systemIcons);
440        mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
441        mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
442        mSystemIcons = systemIcons;
443        updateVisibilities();
444        if (mStatusIcons != null) {
445            mStatusIcons.setVisibility(View.GONE);
446        }
447    }
448
449    public void onSystemIconsDetached() {
450        if (mSignalClusterDetached) {
451            reattachSignalCluster();
452            mSignalClusterDetached = false;
453        }
454        if (mStatusIcons != null) {
455            mStatusIcons.setVisibility(View.VISIBLE);
456        }
457        if (mSignalCluster != null) {
458            mSignalCluster.setVisibility(View.VISIBLE);
459            mSignalCluster.setAlpha(1f);
460            mSignalCluster.setTranslationX(0f);
461            mSignalCluster.setTranslationY(0f);
462        }
463        mStatusIcons = null;
464        mSignalCluster = null;
465        mSystemIcons = null;
466    }
467
468    public void setUserInfoController(UserInfoController userInfoController) {
469        userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() {
470            @Override
471            public void onUserInfoChanged(String name, Drawable picture) {
472                mMultiUserAvatar.setImageDrawable(picture);
473            }
474        });
475    }
476
477    @Override
478    public void onClick(View v) {
479        if (v == mSettingsButton) {
480            startSettingsActivity();
481        } else if (v == mSystemIconsSuperContainer) {
482            startBatteryActivity();
483        } else if (v == mAlarmStatus && mNextAlarm != null) {
484            PendingIntent showIntent = mNextAlarm.getShowIntent();
485            if (showIntent != null && showIntent.isActivity()) {
486                mActivityStarter.startActivity(showIntent.getIntent(), true /* dismissShade */);
487            }
488        }
489    }
490
491    private void startSettingsActivity() {
492        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
493                true /* dismissShade */);
494    }
495
496    private void startBatteryActivity() {
497        mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY),
498                true /* dismissShade */);
499    }
500
501    public void setQSPanel(QSPanel qsp) {
502        mQSPanel = qsp;
503        if (mQSPanel != null) {
504            mQSPanel.setCallback(mQsPanelCallback);
505        }
506        mMultiUserSwitch.setQsPanel(qsp);
507    }
508
509    @Override
510    public boolean shouldDelayChildPressedState() {
511        return true;
512    }
513
514    public void setShowEmergencyCallsOnly(boolean show) {
515        boolean changed = show != mShowEmergencyCallsOnly;
516        if (changed) {
517            mShowEmergencyCallsOnly = show;
518            if (mExpanded) {
519                updateEverything();
520                requestCaptureValues();
521            }
522        }
523    }
524
525    @Override
526    protected void dispatchSetPressed(boolean pressed) {
527        // We don't want that everything lights up when we click on the header, so block the request
528        // here.
529    }
530
531    private void captureLayoutValues(LayoutValues target) {
532        target.timeScale = mExpanded ? 1f : mClockCollapsedScaleFactor;
533        target.clockY = mClock.getBottom();
534        target.dateY = mDateGroup.getTop();
535        target.emergencyCallsOnlyAlpha = getAlphaForVisibility(mEmergencyCallsOnly);
536        target.alarmStatusAlpha = getAlphaForVisibility(mAlarmStatus);
537        target.dateCollapsedAlpha = getAlphaForVisibility(mDateCollapsed);
538        target.dateExpandedAlpha = getAlphaForVisibility(mDateExpanded);
539        target.avatarScale = mMultiUserAvatar.getScaleX();
540        target.avatarX = mMultiUserSwitch.getLeft() + mMultiUserAvatar.getLeft();
541        target.avatarY = mMultiUserSwitch.getTop() + mMultiUserAvatar.getTop();
542        target.batteryX = mSystemIconsSuperContainer.getLeft() + mSystemIconsContainer.getRight();
543        target.batteryY = mSystemIconsSuperContainer.getTop() + mSystemIconsContainer.getTop();
544        target.batteryLevelAlpha = getAlphaForVisibility(mBatteryLevel);
545        target.settingsAlpha = getAlphaForVisibility(mSettingsButton);
546        target.settingsTranslation = mExpanded
547                ? 0
548                : mMultiUserSwitch.getLeft() - mSettingsButton.getLeft();
549        target.signalClusterAlpha = mSignalClusterDetached ? 0f : 1f;
550        target.settingsRotation = !mExpanded ? 90f : 0f;
551    }
552
553    private float getAlphaForVisibility(View v) {
554        return v == null || v.getVisibility() == View.VISIBLE ? 1f : 0f;
555    }
556
557    private void applyAlpha(View v, float alpha) {
558        if (v == null || v.getVisibility() == View.GONE) {
559            return;
560        }
561        if (alpha == 0f) {
562            v.setVisibility(View.INVISIBLE);
563        } else {
564            v.setVisibility(View.VISIBLE);
565            v.setAlpha(alpha);
566        }
567    }
568
569    private void applyLayoutValues(LayoutValues values) {
570        mTime.setScaleX(values.timeScale);
571        mTime.setScaleY(values.timeScale);
572        mClock.setY(values.clockY - mClock.getHeight());
573        mDateGroup.setY(values.dateY);
574        mAlarmStatus.setY(values.dateY - mAlarmStatus.getPaddingTop());
575        mMultiUserAvatar.setScaleX(values.avatarScale);
576        mMultiUserAvatar.setScaleY(values.avatarScale);
577        mMultiUserAvatar.setX(values.avatarX - mMultiUserSwitch.getLeft());
578        mMultiUserAvatar.setY(values.avatarY - mMultiUserSwitch.getTop());
579        mSystemIconsSuperContainer.setX(values.batteryX - mSystemIconsContainer.getRight());
580        mSystemIconsSuperContainer.setY(values.batteryY - mSystemIconsContainer.getTop());
581        if (mSignalCluster != null && mExpanded) {
582            mSignalCluster.setX(mSystemIconsSuperContainer.getX()
583                    - mSignalCluster.getWidth());
584            mSignalCluster.setY(
585                    mSystemIconsSuperContainer.getY() + mSystemIconsSuperContainer.getHeight()/2
586                            - mSignalCluster.getHeight()/2);
587        } else if (mSignalCluster != null) {
588            mSignalCluster.setTranslationX(0f);
589            mSignalCluster.setTranslationY(0f);
590        }
591        mSettingsButton.setTranslationY(mSystemIconsSuperContainer.getTranslationY());
592        mSettingsButton.setTranslationX(values.settingsTranslation);
593        mSettingsButton.setRotation(values.settingsRotation);
594        applyAlpha(mEmergencyCallsOnly, values.emergencyCallsOnlyAlpha);
595        applyAlpha(mAlarmStatus, values.alarmStatusAlpha);
596        applyAlpha(mDateCollapsed, values.dateCollapsedAlpha);
597        applyAlpha(mDateExpanded, values.dateExpandedAlpha);
598        applyAlpha(mBatteryLevel, values.batteryLevelAlpha);
599        applyAlpha(mSettingsButton, values.settingsAlpha);
600        applyAlpha(mSignalCluster, values.signalClusterAlpha);
601        if (!mExpanded) {
602            mTime.setScaleX(1f);
603            mTime.setScaleY(1f);
604        }
605        updateAmPmTranslation();
606    }
607
608    /**
609     * Captures all layout values (position, visibility) for a certain state. This is used for
610     * animations.
611     */
612    private static final class LayoutValues {
613
614        float dateExpandedAlpha;
615        float dateCollapsedAlpha;
616        float emergencyCallsOnlyAlpha;
617        float alarmStatusAlpha;
618        float timeScale = 1f;
619        float clockY;
620        float dateY;
621        float avatarScale;
622        float avatarX;
623        float avatarY;
624        float batteryX;
625        float batteryY;
626        float batteryLevelAlpha;
627        float settingsAlpha;
628        float settingsTranslation;
629        float signalClusterAlpha;
630        float settingsRotation;
631
632        public void interpoloate(LayoutValues v1, LayoutValues v2, float t) {
633            timeScale = v1.timeScale * (1 - t) + v2.timeScale * t;
634            clockY = v1.clockY * (1 - t) + v2.clockY * t;
635            dateY = v1.dateY * (1 - t) + v2.dateY * t;
636            avatarScale = v1.avatarScale * (1 - t) + v2.avatarScale * t;
637            avatarX = v1.avatarX * (1 - t) + v2.avatarX * t;
638            avatarY = v1.avatarY * (1 - t) + v2.avatarY * t;
639            batteryX = v1.batteryX * (1 - t) + v2.batteryX * t;
640            batteryY = v1.batteryY * (1 - t) + v2.batteryY * t;
641            settingsTranslation = v1.settingsTranslation * (1 - t) + v2.settingsTranslation * t;
642
643            float t1 = Math.max(0, t - 0.5f) * 2;
644            settingsRotation = v1.settingsRotation * (1 - t1) + v2.settingsRotation * t1;
645            emergencyCallsOnlyAlpha =
646                    v1.emergencyCallsOnlyAlpha * (1 - t1) + v2.emergencyCallsOnlyAlpha * t1;
647
648            float t2 = Math.min(1, 2 * t);
649            signalClusterAlpha = v1.signalClusterAlpha * (1 - t2) + v2.signalClusterAlpha * t2;
650
651            float t3 = Math.max(0, t - 0.7f) / 0.3f;
652            batteryLevelAlpha = v1.batteryLevelAlpha * (1 - t3) + v2.batteryLevelAlpha * t3;
653            settingsAlpha = v1.settingsAlpha * (1 - t3) + v2.settingsAlpha * t3;
654            dateExpandedAlpha = v1.dateExpandedAlpha * (1 - t3) + v2.dateExpandedAlpha * t3;
655            dateCollapsedAlpha = v1.dateCollapsedAlpha * (1 - t3) + v2.dateCollapsedAlpha * t3;
656            alarmStatusAlpha = v1.alarmStatusAlpha * (1 - t3) + v2.alarmStatusAlpha * t3;
657        }
658    }
659
660    private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
661        @Override
662        public void onToggleStateChanged(final boolean state) {
663            post(new Runnable() {
664                @Override
665                public void run() {
666                    handleToggleStateChanged(state);
667                }
668            });
669        }
670
671        @Override
672        public void onShowingDetail(final QSTile.DetailAdapter detail) {
673            post(new Runnable() {
674                @Override
675                public void run() {
676                    handleShowingDetail(detail);
677                }
678            });
679        }
680
681        @Override
682        public void onScanStateChanged(final boolean state) {
683            post(new Runnable() {
684                @Override
685                public void run() {
686                    handleScanStateChanged(state);
687                }
688            });
689        }
690
691        private void handleToggleStateChanged(boolean state) {
692            mQsDetailHeaderSwitch.setChecked(state);
693        }
694
695        private void handleScanStateChanged(boolean state) {
696            // TODO - waiting on framework asset
697        }
698
699        private void handleShowingDetail(final QSTile.DetailAdapter detail) {
700            final boolean showingDetail = detail != null;
701            transition(mClock, !showingDetail);
702            transition(mDateGroup, !showingDetail);
703            transition(mQsDetailHeader, showingDetail);
704            if (showingDetail) {
705                mQsDetailHeaderTitle.setText(detail.getTitle());
706                final Boolean toggleState = detail.getToggleState();
707                if (toggleState == null) {
708                    mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
709                    mQsDetailHeader.setClickable(false);
710                } else {
711                    mQsDetailHeaderSwitch.setVisibility(VISIBLE);
712                    mQsDetailHeaderSwitch.setChecked(toggleState);
713                    mQsDetailHeader.setClickable(true);
714                    mQsDetailHeader.setOnClickListener(new OnClickListener() {
715                        @Override
716                        public void onClick(View v) {
717                            detail.setToggleState(!mQsDetailHeaderSwitch.isChecked());
718                        }
719                    });
720                }
721            } else {
722                mQsDetailHeader.setClickable(false);
723            }
724        }
725
726        private void transition(final View v, final boolean in) {
727            if (in) {
728                v.bringToFront();
729            }
730            v.animate().alpha(in ? 1 : 0).withLayer().start();
731        }
732    };
733}
734