StatusBarHeaderView.java revision 235510e67210f90de30c2d5582a2077ccc589619
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.view.View;
28import android.view.ViewGroup;
29import android.view.ViewOutlineProvider;
30import android.widget.ImageView;
31import android.widget.LinearLayout;
32import android.widget.RelativeLayout;
33import android.widget.Switch;
34import android.widget.TextView;
35
36import com.android.keyguard.KeyguardStatusView;
37import com.android.systemui.R;
38import com.android.systemui.qs.QSPanel;
39import com.android.systemui.qs.QSTile;
40import com.android.systemui.statusbar.policy.BatteryController;
41import com.android.systemui.statusbar.policy.NextAlarmController;
42import com.android.systemui.statusbar.policy.UserInfoController;
43
44/**
45 * The view to manage the header area in the expanded status bar.
46 */
47public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener,
48        BatteryController.BatteryStateChangeCallback, NextAlarmController.NextAlarmChangeCallback {
49
50    private boolean mExpanded;
51    private boolean mListening;
52
53    private ViewGroup mSystemIconsContainer;
54    private View mSystemIconsSuperContainer;
55    private View mDateGroup;
56    private View mClock;
57    private View mTime;
58    private View mAmPm;
59    private MultiUserSwitch mMultiUserSwitch;
60    private ImageView mMultiUserAvatar;
61    private View mDateCollapsed;
62    private View mDateExpanded;
63    private LinearLayout mSystemIcons;
64    private View mStatusIcons;
65    private View mSignalCluster;
66    private View mSettingsButton;
67    private View mQsDetailHeader;
68    private TextView mQsDetailHeaderTitle;
69    private Switch mQsDetailHeaderSwitch;
70    private View mEmergencyCallsOnly;
71    private TextView mBatteryLevel;
72    private TextView mAlarmStatus;
73
74    private boolean mShowEmergencyCallsOnly;
75    private boolean mAlarmShowing;
76    private AlarmManager.AlarmClockInfo mNextAlarm;
77
78    private int mCollapsedHeight;
79    private int mExpandedHeight;
80
81    private int mMultiUserExpandedMargin;
82    private int mMultiUserCollapsedMargin;
83
84    private int mClockMarginBottomExpanded;
85    private int mClockMarginBottomCollapsed;
86    private int mMultiUserSwitchWidthCollapsed;
87    private int mMultiUserSwitchWidthExpanded;
88
89    /**
90     * In collapsed QS, the clock and avatar are scaled down a bit post-layout to allow for a nice
91     * transition. These values determine that factor.
92     */
93    private float mClockCollapsedScaleFactor;
94    private float mAvatarCollapsedScaleFactor;
95
96    private ActivityStarter mActivityStarter;
97    private BatteryController mBatteryController;
98    private NextAlarmController mNextAlarmController;
99    private QSPanel mQSPanel;
100
101
102    private final Rect mClipBounds = new Rect();
103
104    private boolean mCaptureValues;
105    private boolean mSignalClusterDetached;
106    private final LayoutValues mCollapsedValues = new LayoutValues();
107    private final LayoutValues mExpandedValues = new LayoutValues();
108    private final LayoutValues mCurrentValues = new LayoutValues();
109
110    private float mCurrentT;
111
112    public StatusBarHeaderView(Context context, AttributeSet attrs) {
113        super(context, attrs);
114    }
115
116    @Override
117    protected void onFinishInflate() {
118        super.onFinishInflate();
119        mSystemIconsSuperContainer = findViewById(R.id.system_icons_super_container);
120        mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container);
121        mSystemIconsSuperContainer.setOnClickListener(this);
122        mDateGroup = findViewById(R.id.date_group);
123        mClock = findViewById(R.id.clock);
124        mTime = findViewById(R.id.time_view);
125        mAmPm = findViewById(R.id.am_pm_view);
126        mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
127        mMultiUserAvatar = (ImageView) findViewById(R.id.multi_user_avatar);
128        mDateCollapsed = findViewById(R.id.date_collapsed);
129        mDateExpanded = findViewById(R.id.date_expanded);
130        mSettingsButton = findViewById(R.id.settings_button);
131        mSettingsButton.setOnClickListener(this);
132        mQsDetailHeader = findViewById(R.id.qs_detail_header);
133        mQsDetailHeader.setAlpha(0);
134        mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
135        mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
136        mEmergencyCallsOnly = findViewById(R.id.header_emergency_calls_only);
137        mBatteryLevel = (TextView) findViewById(R.id.battery_level);
138        mAlarmStatus = (TextView) findViewById(R.id.alarm_status);
139        mAlarmStatus.setOnClickListener(this);
140        loadDimens();
141        updateVisibilities();
142        updateClockScale();
143        updateAvatarScale();
144        addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
145            @Override
146            public void onLayoutChange(View v, int left, int top, int right,
147                    int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
148                if ((right - left) != (oldRight - oldLeft)) {
149                    // width changed, update clipping
150                    setClipping(getHeight());
151                }
152                boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
153                mTime.setPivotX(rtl ? mTime.getWidth() : 0);
154                mTime.setPivotY(mTime.getBaseline());
155                mAmPm.setPivotX(rtl ? mAmPm.getWidth() : 0);
156                mAmPm.setPivotY(mAmPm.getBaseline());
157                updateAmPmTranslation();
158            }
159        });
160        setOutlineProvider(new ViewOutlineProvider() {
161            @Override
162            public void getOutline(View view, Outline outline) {
163                outline.setRect(mClipBounds);
164            }
165        });
166        requestCaptureValues();
167    }
168
169    @Override
170    protected void onLayout(boolean changed, int l, int t, int r, int b) {
171        super.onLayout(changed, l, t, r, b);
172        if (mCaptureValues) {
173            if (mExpanded) {
174                captureLayoutValues(mExpandedValues);
175            } else {
176                captureLayoutValues(mCollapsedValues);
177            }
178            mCaptureValues = false;
179            updateLayoutValues(mCurrentT);
180        }
181        mAlarmStatus.setX(mDateGroup.getLeft() + mDateCollapsed.getRight());
182    }
183
184    private void requestCaptureValues() {
185        mCaptureValues = true;
186        requestLayout();
187    }
188
189    private void loadDimens() {
190        mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height);
191        mExpandedHeight = getResources().getDimensionPixelSize(
192                R.dimen.status_bar_header_height_expanded);
193        mMultiUserExpandedMargin =
194                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_expanded_margin);
195        mMultiUserCollapsedMargin =
196                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_collapsed_margin);
197
198        mClockMarginBottomExpanded =
199                getResources().getDimensionPixelSize(R.dimen.clock_expanded_bottom_margin);
200        mClockMarginBottomCollapsed =
201                getResources().getDimensionPixelSize(R.dimen.clock_collapsed_bottom_margin);
202        mMultiUserSwitchWidthCollapsed =
203                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_collapsed);
204        mMultiUserSwitchWidthExpanded =
205                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_expanded);
206        mAvatarCollapsedScaleFactor =
207                getResources().getDimensionPixelSize(R.dimen.multi_user_avatar_collapsed_size)
208                / (float) mMultiUserAvatar.getLayoutParams().width;
209        mClockCollapsedScaleFactor =
210                (float) getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size)
211                / (float) getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size);
212    }
213
214    public void setActivityStarter(ActivityStarter activityStarter) {
215        mActivityStarter = activityStarter;
216    }
217
218    public void setBatteryController(BatteryController batteryController) {
219        mBatteryController = batteryController;
220    }
221
222    public void setNextAlarmController(NextAlarmController nextAlarmController) {
223        mNextAlarmController = nextAlarmController;
224    }
225
226    public int getCollapsedHeight() {
227        return mCollapsedHeight;
228    }
229
230    public int getExpandedHeight() {
231        return mExpandedHeight;
232    }
233
234    public void setListening(boolean listening) {
235        if (listening == mListening) {
236            return;
237        }
238        mListening = listening;
239        updateListeners();
240    }
241
242    public void setExpanded(boolean expanded) {
243        boolean changed = expanded != mExpanded;
244        mExpanded = expanded;
245        if (changed) {
246            updateEverything();
247        }
248    }
249
250    public void updateEverything() {
251        updateHeights();
252        updateVisibilities();
253        updateSystemIconsLayoutParams();
254        updateClickTargets();
255        updateMultiUserSwitch();
256        if (mQSPanel != null) {
257            mQSPanel.setExpanded(mExpanded);
258        }
259        updateClockScale();
260        updateAvatarScale();
261        updateClockLp();
262        requestCaptureValues();
263    }
264
265    private void updateHeights() {
266        int height = mExpanded ? mExpandedHeight : mCollapsedHeight;
267        ViewGroup.LayoutParams lp = getLayoutParams();
268        if (lp.height != height) {
269            lp.height = height;
270            setLayoutParams(lp);
271        }
272    }
273
274    private void updateVisibilities() {
275        mDateCollapsed.setVisibility(mExpanded && mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
276        mDateExpanded.setVisibility(mExpanded && mAlarmShowing ? View.INVISIBLE : View.VISIBLE);
277        mAlarmStatus.setVisibility(mExpanded && mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
278        mSettingsButton.setVisibility(mExpanded ? View.VISIBLE : View.INVISIBLE);
279        mQsDetailHeader.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
280        if (mSignalCluster != null) {
281            updateSignalClusterDetachment();
282        }
283        mEmergencyCallsOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly ? VISIBLE : GONE);
284        mBatteryLevel.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
285    }
286
287    private void updateSignalClusterDetachment() {
288        boolean detached = mExpanded;
289        if (detached != mSignalClusterDetached) {
290            if (detached) {
291                getOverlay().add(mSignalCluster);
292            } else {
293                reattachSignalCluster();
294            }
295        }
296        mSignalClusterDetached = detached;
297    }
298
299    private void reattachSignalCluster() {
300        getOverlay().remove(mSignalCluster);
301        mSystemIcons.addView(mSignalCluster, 1);
302    }
303
304    private void updateSystemIconsLayoutParams() {
305        RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
306        int rule = mExpanded
307                ? mSettingsButton.getId()
308                : mMultiUserSwitch.getId();
309        if (rule != lp.getRules()[RelativeLayout.START_OF]) {
310            lp.addRule(RelativeLayout.START_OF, rule);
311            mSystemIconsSuperContainer.setLayoutParams(lp);
312        }
313    }
314
315    private void updateListeners() {
316        if (mListening) {
317            mBatteryController.addStateChangedCallback(this);
318            mNextAlarmController.addStateChangedCallback(this);
319        } else {
320            mBatteryController.removeStateChangedCallback(this);
321            mNextAlarmController.removeStateChangedCallback(this);
322        }
323    }
324
325    private void updateAvatarScale() {
326        if (mExpanded) {
327            mMultiUserAvatar.setScaleX(1f);
328            mMultiUserAvatar.setScaleY(1f);
329        } else {
330            mMultiUserAvatar.setScaleX(mAvatarCollapsedScaleFactor);
331            mMultiUserAvatar.setScaleY(mAvatarCollapsedScaleFactor);
332        }
333    }
334
335    private void updateClockScale() {
336        mAmPm.setScaleX(mClockCollapsedScaleFactor);
337        mAmPm.setScaleY(mClockCollapsedScaleFactor);
338        mTime.setScaleX(getTimeScale());
339        mTime.setScaleY(getTimeScale());
340        updateAmPmTranslation();
341    }
342
343    private float getTimeScale() {
344        return !mExpanded ? mClockCollapsedScaleFactor : 1f;
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        updateVisibilities();
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        mShowEmergencyCallsOnly = show;
516        if (mExpanded) {
517            updateVisibilities();
518            requestCaptureValues();
519        }
520    }
521
522    @Override
523    protected void dispatchSetPressed(boolean pressed) {
524        // We don't want that everything lights up when we click on the header, so block the request
525        // here.
526    }
527
528    private void captureLayoutValues(LayoutValues target) {
529        target.timeScale = mTime.getScaleX();
530        target.clockY = mClock.getTop();
531        target.dateY = mDateGroup.getTop();
532        target.emergencyCallsOnlyAlpha = getAlphaForVisibility(mEmergencyCallsOnly);
533        target.alarmStatusAlpha = getAlphaForVisibility(mAlarmStatus);
534        target.dateCollapsedAlpha = getAlphaForVisibility(mDateCollapsed);
535        target.dateExpandedAlpha = getAlphaForVisibility(mDateExpanded);
536        target.avatarScale = mMultiUserAvatar.getScaleX();
537        target.avatarX = mMultiUserSwitch.getLeft() + mMultiUserAvatar.getLeft();
538        target.avatarY = mMultiUserSwitch.getTop() + mMultiUserAvatar.getTop();
539        target.batteryX = mSystemIconsSuperContainer.getLeft() + mSystemIconsContainer.getRight();
540        target.batteryY = mSystemIconsSuperContainer.getTop() + mSystemIconsContainer.getTop();
541        target.batteryLevelAlpha = getAlphaForVisibility(mBatteryLevel);
542        target.settingsAlpha = getAlphaForVisibility(mSettingsButton);
543        target.settingsTranslation = mExpanded
544                ? 0
545                : mMultiUserSwitch.getLeft() - mSettingsButton.getLeft();
546        target.signalClusterAlpha = mSignalClusterDetached ? 0f : 1f;
547        target.settingsRotation = !mExpanded ? 90f : 0f;
548    }
549
550    private float getAlphaForVisibility(View v) {
551        return v == null || v.getVisibility() == View.VISIBLE ? 1f : 0f;
552    }
553
554    private void applyAlpha(View v, float alpha) {
555        if (v == null) {
556            return;
557        }
558        if (alpha == 0f) {
559            v.setVisibility(View.INVISIBLE);
560        } else {
561            v.setVisibility(View.VISIBLE);
562            v.setAlpha(alpha);
563        }
564    }
565
566    private void applyLayoutValues(LayoutValues values) {
567        mTime.setScaleX(values.timeScale);
568        mTime.setScaleY(values.timeScale);
569        mClock.setY(values.clockY);
570        mDateGroup.setY(values.dateY);
571        mAlarmStatus.setY(values.dateY - mAlarmStatus.getPaddingTop());
572        mMultiUserAvatar.setScaleX(values.avatarScale);
573        mMultiUserAvatar.setScaleY(values.avatarScale);
574        mMultiUserAvatar.setX(values.avatarX - mMultiUserSwitch.getLeft());
575        mMultiUserAvatar.setY(values.avatarY - mMultiUserSwitch.getTop());
576        mSystemIconsSuperContainer.setX(values.batteryX - mSystemIconsContainer.getRight());
577        mSystemIconsSuperContainer.setY(values.batteryY - mSystemIconsContainer.getTop());
578        if (mSignalCluster != null && mExpanded) {
579            mSignalCluster.setX(mSystemIconsSuperContainer.getX()
580                    - mSignalCluster.getWidth());
581            mSignalCluster.setY(
582                    mSystemIconsSuperContainer.getY() + mSystemIconsSuperContainer.getHeight()/2
583                            - mSignalCluster.getHeight()/2);
584        } else if (mSignalCluster != null) {
585            mSignalCluster.setTranslationX(0f);
586            mSignalCluster.setTranslationY(0f);
587        }
588        mSettingsButton.setTranslationY(mSystemIconsSuperContainer.getTranslationY());
589        mSettingsButton.setTranslationX(values.settingsTranslation);
590        mSettingsButton.setRotation(values.settingsRotation);
591        applyAlpha(mEmergencyCallsOnly, values.emergencyCallsOnlyAlpha);
592        applyAlpha(mAlarmStatus, values.alarmStatusAlpha);
593        applyAlpha(mDateCollapsed, values.dateCollapsedAlpha);
594        applyAlpha(mDateExpanded, values.dateExpandedAlpha);
595        applyAlpha(mBatteryLevel, values.batteryLevelAlpha);
596        applyAlpha(mSettingsButton, values.settingsAlpha);
597        applyAlpha(mSignalCluster, values.signalClusterAlpha);
598        updateAmPmTranslation();
599    }
600
601    /**
602     * Captures all layout values (position, visibility) for a certain state. This is used for
603     * animations.
604     */
605    private static final class LayoutValues {
606
607        float dateExpandedAlpha;
608        float dateCollapsedAlpha;
609        float emergencyCallsOnlyAlpha;
610        float alarmStatusAlpha;
611        float timeScale = 1f;
612        float clockY;
613        float dateY;
614        float avatarScale;
615        float avatarX;
616        float avatarY;
617        float batteryX;
618        float batteryY;
619        float batteryLevelAlpha;
620        float settingsAlpha;
621        float settingsTranslation;
622        float signalClusterAlpha;
623        float settingsRotation;
624
625        public void interpoloate(LayoutValues v1, LayoutValues v2, float t) {
626            timeScale = v1.timeScale * (1 - t) + v2.timeScale * t;
627            clockY = v1.clockY * (1 - t) + v2.clockY * t;
628            dateY = v1.dateY * (1 - t) + v2.dateY * t;
629            avatarScale = v1.avatarScale * (1 - t) + v2.avatarScale * t;
630            avatarX = v1.avatarX * (1 - t) + v2.avatarX * t;
631            avatarY = v1.avatarY * (1 - t) + v2.avatarY * t;
632            batteryX = v1.batteryX * (1 - t) + v2.batteryX * t;
633            batteryY = v1.batteryY * (1 - t) + v2.batteryY * t;
634            settingsTranslation = v1.settingsTranslation * (1 - t) + v2.settingsTranslation * t;
635
636            float t1 = Math.max(0, t - 0.5f) * 2;
637            settingsRotation = v1.settingsRotation * (1 - t1) + v2.settingsRotation * t1;
638            emergencyCallsOnlyAlpha =
639                    v1.emergencyCallsOnlyAlpha * (1 - t1) + v2.emergencyCallsOnlyAlpha * t1;
640
641            float t2 = Math.min(1, 2 * t);
642            signalClusterAlpha = v1.signalClusterAlpha * (1 - t2) + v2.signalClusterAlpha * t2;
643
644            float t3 = Math.max(0, t - 0.7f) / 0.3f;
645            batteryLevelAlpha = v1.batteryLevelAlpha * (1 - t3) + v2.batteryLevelAlpha * t3;
646            settingsAlpha = v1.settingsAlpha * (1 - t3) + v2.settingsAlpha * t3;
647            dateExpandedAlpha = v1.dateExpandedAlpha * (1 - t3) + v2.dateExpandedAlpha * t3;
648            dateCollapsedAlpha = v1.dateCollapsedAlpha * (1 - t3) + v2.dateCollapsedAlpha * t3;
649            alarmStatusAlpha = v1.alarmStatusAlpha * (1 - t3) + v2.alarmStatusAlpha * t3;
650        }
651    }
652
653    private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
654        @Override
655        public void onToggleStateChanged(final boolean state) {
656            post(new Runnable() {
657                @Override
658                public void run() {
659                    handleToggleStateChanged(state);
660                }
661            });
662        }
663
664        @Override
665        public void onShowingDetail(final QSTile.DetailAdapter detail) {
666            post(new Runnable() {
667                @Override
668                public void run() {
669                    handleShowingDetail(detail);
670                }
671            });
672        }
673
674        @Override
675        public void onScanStateChanged(final boolean state) {
676            post(new Runnable() {
677                @Override
678                public void run() {
679                    handleScanStateChanged(state);
680                }
681            });
682        }
683
684        private void handleToggleStateChanged(boolean state) {
685            mQsDetailHeaderSwitch.setChecked(state);
686        }
687
688        private void handleScanStateChanged(boolean state) {
689            // TODO - waiting on framework asset
690        }
691
692        private void handleShowingDetail(final QSTile.DetailAdapter detail) {
693            final boolean showingDetail = detail != null;
694            transition(mClock, !showingDetail);
695            transition(mDateGroup, !showingDetail);
696            transition(mQsDetailHeader, showingDetail);
697            if (showingDetail) {
698                mQsDetailHeaderTitle.setText(detail.getTitle());
699                final Boolean toggleState = detail.getToggleState();
700                if (toggleState == null) {
701                    mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
702                    mQsDetailHeader.setClickable(false);
703                } else {
704                    mQsDetailHeaderSwitch.setVisibility(VISIBLE);
705                    mQsDetailHeaderSwitch.setChecked(toggleState);
706                    mQsDetailHeader.setClickable(true);
707                    mQsDetailHeader.setOnClickListener(new OnClickListener() {
708                        @Override
709                        public void onClick(View v) {
710                            detail.setToggleState(!mQsDetailHeaderSwitch.isChecked());
711                        }
712                    });
713                }
714            } else {
715                mQsDetailHeader.setClickable(false);
716            }
717        }
718
719        private void transition(final View v, final boolean in) {
720            if (in) {
721                v.bringToFront();
722            }
723            v.animate().alpha(in ? 1 : 0).withLayer().start();
724        }
725    };
726}
727