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