OSDN Git Service

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