2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 package com.android.systemui.statusbar.phone;
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;
56 import java.text.NumberFormat;
59 * The view to manage the header area in the expanded status bar.
61 public class StatusBarHeaderView extends BaseStatusBarHeader implements View.OnClickListener,
62 BatteryController.BatteryStateChangeCallback, NextAlarmController.NextAlarmChangeCallback,
65 private boolean mExpanded;
66 private boolean mListening;
68 private ViewGroup mSystemIconsContainer;
69 private View mSystemIconsSuperContainer;
70 private View mDateGroup;
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;
90 private boolean mShowEmergencyCallsOnly;
91 private boolean mAlarmShowing;
92 private AlarmManager.AlarmClockInfo mNextAlarm;
94 private int mCollapsedHeight;
95 private int mExpandedHeight;
97 private int mMultiUserExpandedMargin;
98 private int mMultiUserCollapsedMargin;
100 private int mClockMarginBottomExpanded;
101 private int mClockMarginBottomCollapsed;
102 private int mMultiUserSwitchWidthCollapsed;
103 private int mMultiUserSwitchWidthExpanded;
105 private int mClockCollapsedSize;
106 private int mClockExpandedSize;
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.
112 private float mClockCollapsedScaleFactor;
113 private float mAvatarCollapsedScaleFactor;
115 private ActivityStarter mActivityStarter;
116 private BatteryController mBatteryController;
117 private NextAlarmController mNextAlarmController;
118 private QSPanel mQSPanel;
120 private final Rect mClipBounds = new Rect();
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();
128 private float mCurrentT;
129 private boolean mShowingDetail;
130 private boolean mDetailTransitioning;
132 private boolean mAllowExpand = true;
134 public StatusBarHeaderView(Context context, AttributeSet attrs) {
135 super(context, attrs);
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);
167 updateVisibilities();
170 addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
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());
178 boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
179 mTime.setPivotX(rtl ? mTime.getWidth() : 0);
180 mTime.setPivotY(mTime.getBaseline());
181 updateAmPmTranslation();
184 setOutlineProvider(new ViewOutlineProvider() {
186 public void getOutline(View view, Outline outline) {
187 outline.setRect(mClipBounds);
190 requestCaptureValues();
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);
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) {
204 captureLayoutValues(mExpandedValues);
206 captureLayoutValues(mCollapsedValues);
208 mCaptureValues = false;
209 updateLayoutValues(mCurrentT);
211 mAlarmStatus.setX(mDateGroup.getLeft() + mDateCollapsed.getRight());
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);
228 mEmergencyCallsOnly.setText(com.android.internal.R.string.emergency_calls_only);
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;
235 updateClockCollapsedMargin();
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);
249 private void requestCaptureValues() {
250 mCaptureValues = true;
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;
278 public void setActivityStarter(ActivityStarter activityStarter) {
279 mActivityStarter = activityStarter;
282 public void setBatteryController(BatteryController batteryController) {
283 mBatteryController = batteryController;
284 ((BatteryMeterView) findViewById(R.id.battery)).setBatteryController(batteryController);
287 public void setNextAlarmController(NextAlarmController nextAlarmController) {
288 mNextAlarmController = nextAlarmController;
291 public int getCollapsedHeight() {
292 return mCollapsedHeight;
295 public int getExpandedHeight() {
296 return mAllowExpand ? mExpandedHeight : mCollapsedHeight;
299 public void setListening(boolean listening) {
300 if (listening == mListening) {
303 mListening = listening;
307 public void setExpanded(boolean expanded) {
311 boolean changed = expanded != mExpanded;
312 mExpanded = expanded;
318 public void updateEverything() {
320 updateVisibilities();
321 updateSystemIconsLayoutParams();
322 updateClickTargets();
323 updateMultiUserSwitch();
327 requestCaptureValues();
330 private void updateHeights() {
331 int height = mExpanded ? mExpandedHeight : mCollapsedHeight;
332 ViewGroup.LayoutParams lp = getLayoutParams();
333 if (lp.height != height) {
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();
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);
354 private void updateSignalClusterDetachment() {
355 boolean detached = mExpanded;
356 if (detached != mSignalClusterDetached) {
358 getOverlay().add(mSignalCluster);
360 reattachSignalCluster();
363 mSignalClusterDetached = detached;
366 private void reattachSignalCluster() {
367 getOverlay().remove(mSignalCluster);
368 mSystemIcons.addView(mSignalCluster, 1);
371 private void updateSystemIconsLayoutParams() {
372 RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
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);
382 private void updateListeners() {
384 mBatteryController.addStateChangedCallback(this);
385 mNextAlarmController.addStateChangedCallback(this);
387 mBatteryController.removeStateChangedCallback(this);
388 mNextAlarmController.removeStateChangedCallback(this);
392 private void updateAvatarScale() {
394 mMultiUserAvatar.setScaleX(1f);
395 mMultiUserAvatar.setScaleY(1f);
397 mMultiUserAvatar.setScaleX(mAvatarCollapsedScaleFactor);
398 mMultiUserAvatar.setScaleY(mAvatarCollapsedScaleFactor);
402 private void updateClockScale() {
403 mTime.setTextSize(TypedValue.COMPLEX_UNIT_PX, mExpanded
405 : mClockCollapsedSize);
408 updateAmPmTranslation();
411 private void updateAmPmTranslation() {
412 boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
413 mAmPm.setTranslationX((rtl ? 1 : -1) * mTime.getWidth() * (1 - mTime.getScaleX()));
417 public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
418 String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0);
419 mBatteryLevel.setText(percentage);
423 public void onPowerSaveChanged(boolean isPowerSave) {
424 // could not care less
428 public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
429 mNextAlarm = nextAlarm;
430 if (nextAlarm != null) {
431 mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
433 mAlarmShowing = nextAlarm != null;
435 requestCaptureValues();
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);
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);
457 private void updateMultiUserSwitch() {
461 marginEnd = mMultiUserExpandedMargin;
462 width = mMultiUserSwitchWidthExpanded;
464 marginEnd = mMultiUserCollapsedMargin;
465 width = mMultiUserSwitchWidthCollapsed;
467 MarginLayoutParams lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
468 if (marginEnd != lp.getMarginEnd() || lp.width != width) {
469 lp.setMarginEnd(marginEnd);
471 mMultiUserSwitch.setLayoutParams(lp);
475 public void setExpansion(float t) {
480 float height = mCollapsedHeight + t * (mExpandedHeight - mCollapsedHeight);
481 if (height < mCollapsedHeight) {
482 height = mCollapsedHeight;
484 if (height > mExpandedHeight) {
485 height = mExpandedHeight;
488 updateLayoutValues(t);
491 private void updateLayoutValues(float t) {
492 if (mCaptureValues) {
495 mCurrentValues.interpoloate(mCollapsedValues, mExpandedValues, t);
496 applyLayoutValues(mCurrentValues);
499 private void setClipping(float height) {
500 mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height);
501 setClipBounds(mClipBounds);
505 public void setUserInfoController(UserInfoController userInfoController) {
506 userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() {
508 public void onUserInfoChanged(String name, Drawable picture) {
509 mMultiUserAvatar.setImageDrawable(picture);
515 public void setCallback(Callback qsPanelCallback) {
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() {
526 // Relaunch settings so that the tuner disappears.
527 startSettingsActivity();
531 Toast.makeText(getContext(), R.string.tuner_toast, Toast.LENGTH_LONG).show();
532 TunerService.setTunerEnabled(mContext, true);
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);
546 private void startSettingsActivity() {
547 mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
548 true /* dismissShade */);
551 private void startBatteryActivity() {
552 mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY),
553 true /* dismissShade */);
556 public void setQSPanel(QSPanel qsp) {
558 if (mQSPanel != null) {
559 mQSPanel.setCallback(mQsPanelCallback);
561 mMultiUserSwitch.setQsPanel(qsp);
565 public boolean shouldDelayChildPressedState() {
570 public void setEmergencyCallsOnly(boolean show) {
571 boolean changed = show != mShowEmergencyCallsOnly;
573 mShowEmergencyCallsOnly = show;
576 requestCaptureValues();
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
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();
602 target.batteryX = mSystemIconsSuperContainer.getLeft()
603 + mSystemIconsContainer.getLeft();
605 target.batteryY = mSystemIconsSuperContainer.getTop() + mSystemIconsContainer.getTop();
606 target.batteryLevelAlpha = getAlphaForVisibility(mBatteryLevel);
607 target.settingsAlpha = getAlphaForVisibility(mSettingsContainer);
608 target.settingsTranslation = mExpanded
610 : mMultiUserSwitch.getLeft() - mSettingsContainer.getLeft();
611 target.signalClusterAlpha = mSignalClusterDetached ? 0f : 1f;
612 target.settingsRotation = !mExpanded ? 90f : 0f;
615 private float getAlphaForVisibility(View v) {
616 return v == null || v.getVisibility() == View.VISIBLE ? 1f : 0f;
619 private void applyAlpha(View v, float alpha) {
620 if (v == null || v.getVisibility() == View.GONE) {
624 v.setVisibility(View.INVISIBLE);
626 v.setVisibility(View.VISIBLE);
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());
644 mSystemIconsSuperContainer.setX(values.batteryX - mSystemIconsContainer.getLeft());
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());
652 mSignalCluster.setX(mSystemIconsSuperContainer.getX()
653 + mSystemIconsSuperContainer.getWidth());
656 mSystemIconsSuperContainer.getY() + mSystemIconsSuperContainer.getHeight()/2
657 - mSignalCluster.getHeight()/2);
658 } else if (mSignalCluster != null) {
659 mSignalCluster.setTranslationX(0f);
660 mSignalCluster.setTranslationY(0f);
662 if (!mSettingsButton.isAnimating()) {
663 mSettingsContainer.setTranslationY(mSystemIconsSuperContainer.getTranslationY());
664 mSettingsContainer.setTranslationX(values.settingsTranslation);
665 mSettingsButton.setRotation(values.settingsRotation);
667 applyAlpha(mEmergencyCallsOnly, values.emergencyCallsOnlyAlpha);
668 if (!mShowingDetail && !mDetailTransitioning) {
669 // Otherwise it needs to stay invisible
670 applyAlpha(mAlarmStatus, values.alarmStatusAlpha);
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);
681 updateAmPmTranslation();
685 * Captures all layout values (position, visibility) for a certain state. This is used for
688 private static final class LayoutValues {
690 float dateExpandedAlpha;
691 float dateCollapsedAlpha;
692 float emergencyCallsOnlyAlpha;
693 float alarmStatusAlpha;
694 float timeScale = 1f;
702 float batteryLevelAlpha;
704 float settingsTranslation;
705 float signalClusterAlpha;
706 float settingsRotation;
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;
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;
724 float t2 = Math.min(1, 2 * t);
725 signalClusterAlpha = v1.signalClusterAlpha * (1 - t2) + v2.signalClusterAlpha * t2;
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;
736 private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
737 private boolean mScanState;
740 public void onToggleStateChanged(final boolean state) {
741 post(new Runnable() {
744 handleToggleStateChanged(state);
750 public void onShowingDetail(final DetailAdapter detail, int x, int y) {
751 mDetailTransitioning = true;
752 post(new Runnable() {
755 handleShowingDetail(detail);
761 public void onScanStateChanged(final boolean state) {
762 post(new Runnable() {
765 handleScanStateChanged(state);
770 private void handleToggleStateChanged(boolean state) {
771 mQsDetailHeaderSwitch.setChecked(state);
774 private void handleScanStateChanged(boolean state) {
775 if (mScanState == state) return;
777 final Animatable anim = (Animatable) mQsDetailHeaderProgress.getDrawable();
779 mQsDetailHeaderProgress.animate().alpha(1f);
782 mQsDetailHeaderProgress.animate().alpha(0f);
787 private void handleShowingDetail(final QSTile.DetailAdapter detail) {
788 final boolean showingDetail = detail != null;
789 transition(mClock, !showingDetail);
790 transition(mDateGroup, !showingDetail);
792 transition(mAlarmStatus, !showingDetail);
794 transition(mQsDetailHeader, showingDetail);
795 mShowingDetail = showingDetail;
797 mQsDetailHeaderTitle.setText(detail.getTitle());
798 final Boolean toggleState = detail.getToggleState();
799 if (toggleState == null) {
800 mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
801 mQsDetailHeader.setClickable(false);
803 mQsDetailHeaderSwitch.setVisibility(VISIBLE);
804 mQsDetailHeaderSwitch.setChecked(toggleState);
805 mQsDetailHeader.setClickable(true);
806 mQsDetailHeader.setOnClickListener(new OnClickListener() {
808 public void onClick(View v) {
809 boolean checked = !mQsDetailHeaderSwitch.isChecked();
810 mQsDetailHeaderSwitch.setChecked(checked);
811 detail.setToggleState(checked);
816 mQsDetailHeader.setClickable(false);
820 private void transition(final View v, final boolean in) {
823 v.setVisibility(VISIBLE);
825 if (v.hasOverlappingRendering()) {
826 v.animate().withLayer();
830 .withEndAction(new Runnable() {
834 v.setVisibility(INVISIBLE);
836 mDetailTransitioning = false;