StatusBarHeaderView.java revision 805131627369b35fbf78d491a6018851517a8314
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 mEmergencyCallsOnly.setText(com.android.internal.R.string.emergency_calls_only); 211 212 mClockCollapsedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size); 213 mClockExpandedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size); 214 mClockCollapsedScaleFactor = (float) mClockCollapsedSize / (float) mClockExpandedSize; 215 216 updateClockScale(); 217 updateClockCollapsedMargin(); 218 } 219 220 private void updateClockCollapsedMargin() { 221 Resources res = getResources(); 222 int padding = res.getDimensionPixelSize(R.dimen.clock_collapsed_bottom_margin); 223 int largePadding = res.getDimensionPixelSize( 224 R.dimen.clock_collapsed_bottom_margin_large_text); 225 float largeFactor = (MathUtils.constrain(getResources().getConfiguration().fontScale, 1.0f, 226 FontSizeUtils.LARGE_TEXT_SCALE) - 1f) / (FontSizeUtils.LARGE_TEXT_SCALE - 1f); 227 mClockMarginBottomCollapsed = Math.round((1 - largeFactor) * padding + largeFactor * largePadding); 228 requestLayout(); 229 } 230 231 private void requestCaptureValues() { 232 mCaptureValues = true; 233 requestLayout(); 234 } 235 236 private void loadDimens() { 237 mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height); 238 mExpandedHeight = getResources().getDimensionPixelSize( 239 R.dimen.status_bar_header_height_expanded); 240 mMultiUserExpandedMargin = 241 getResources().getDimensionPixelSize(R.dimen.multi_user_switch_expanded_margin); 242 mMultiUserCollapsedMargin = 243 getResources().getDimensionPixelSize(R.dimen.multi_user_switch_collapsed_margin); 244 mClockMarginBottomExpanded = 245 getResources().getDimensionPixelSize(R.dimen.clock_expanded_bottom_margin); 246 updateClockCollapsedMargin(); 247 mMultiUserSwitchWidthCollapsed = 248 getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_collapsed); 249 mMultiUserSwitchWidthExpanded = 250 getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_expanded); 251 mAvatarCollapsedScaleFactor = 252 getResources().getDimensionPixelSize(R.dimen.multi_user_avatar_collapsed_size) 253 / (float) mMultiUserAvatar.getLayoutParams().width; 254 mClockCollapsedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size); 255 mClockExpandedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size); 256 mClockCollapsedScaleFactor = (float) mClockCollapsedSize / (float) mClockExpandedSize; 257 258 } 259 260 public void setActivityStarter(ActivityStarter activityStarter) { 261 mActivityStarter = activityStarter; 262 } 263 264 public void setBatteryController(BatteryController batteryController) { 265 mBatteryController = batteryController; 266 ((BatteryMeterView) findViewById(R.id.battery)).setBatteryController(batteryController); 267 } 268 269 public void setNextAlarmController(NextAlarmController nextAlarmController) { 270 mNextAlarmController = nextAlarmController; 271 } 272 273 public int getCollapsedHeight() { 274 return mCollapsedHeight; 275 } 276 277 public int getExpandedHeight() { 278 return mExpandedHeight; 279 } 280 281 public void setListening(boolean listening) { 282 if (listening == mListening) { 283 return; 284 } 285 mListening = listening; 286 updateListeners(); 287 } 288 289 public void setExpanded(boolean expanded) { 290 boolean changed = expanded != mExpanded; 291 mExpanded = expanded; 292 if (changed) { 293 updateEverything(); 294 } 295 } 296 297 public void updateEverything() { 298 updateHeights(); 299 updateVisibilities(); 300 updateSystemIconsLayoutParams(); 301 updateClickTargets(); 302 updateMultiUserSwitch(); 303 if (mQSPanel != null) { 304 mQSPanel.setExpanded(mExpanded); 305 } 306 updateClockScale(); 307 updateAvatarScale(); 308 updateClockLp(); 309 requestCaptureValues(); 310 } 311 312 private void updateHeights() { 313 int height = mExpanded ? mExpandedHeight : mCollapsedHeight; 314 ViewGroup.LayoutParams lp = getLayoutParams(); 315 if (lp.height != height) { 316 lp.height = height; 317 setLayoutParams(lp); 318 } 319 } 320 321 private void updateVisibilities() { 322 mDateCollapsed.setVisibility(mExpanded && mAlarmShowing ? View.VISIBLE : View.INVISIBLE); 323 mDateExpanded.setVisibility(mExpanded && mAlarmShowing ? View.INVISIBLE : View.VISIBLE); 324 mAlarmStatus.setVisibility(mExpanded && mAlarmShowing ? View.VISIBLE : View.INVISIBLE); 325 mSettingsButton.setVisibility(mExpanded ? View.VISIBLE : View.INVISIBLE); 326 mQsDetailHeader.setVisibility(mExpanded && mShowingDetail? View.VISIBLE : View.INVISIBLE); 327 if (mSignalCluster != null) { 328 updateSignalClusterDetachment(); 329 } 330 mEmergencyCallsOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly ? VISIBLE : GONE); 331 mBatteryLevel.setVisibility(mExpanded ? View.VISIBLE : View.GONE); 332 } 333 334 private void updateSignalClusterDetachment() { 335 boolean detached = mExpanded; 336 if (detached != mSignalClusterDetached) { 337 if (detached) { 338 getOverlay().add(mSignalCluster); 339 } else { 340 reattachSignalCluster(); 341 } 342 } 343 mSignalClusterDetached = detached; 344 } 345 346 private void reattachSignalCluster() { 347 getOverlay().remove(mSignalCluster); 348 mSystemIcons.addView(mSignalCluster, 1); 349 } 350 351 private void updateSystemIconsLayoutParams() { 352 RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsSuperContainer.getLayoutParams(); 353 int rule = mExpanded 354 ? mSettingsButton.getId() 355 : mMultiUserSwitch.getId(); 356 if (rule != lp.getRules()[RelativeLayout.START_OF]) { 357 lp.addRule(RelativeLayout.START_OF, rule); 358 mSystemIconsSuperContainer.setLayoutParams(lp); 359 } 360 } 361 362 private void updateListeners() { 363 if (mListening) { 364 mBatteryController.addStateChangedCallback(this); 365 mNextAlarmController.addStateChangedCallback(this); 366 } else { 367 mBatteryController.removeStateChangedCallback(this); 368 mNextAlarmController.removeStateChangedCallback(this); 369 } 370 } 371 372 private void updateAvatarScale() { 373 if (mExpanded) { 374 mMultiUserAvatar.setScaleX(1f); 375 mMultiUserAvatar.setScaleY(1f); 376 } else { 377 mMultiUserAvatar.setScaleX(mAvatarCollapsedScaleFactor); 378 mMultiUserAvatar.setScaleY(mAvatarCollapsedScaleFactor); 379 } 380 } 381 382 private void updateClockScale() { 383 mTime.setTextSize(TypedValue.COMPLEX_UNIT_PX, mExpanded 384 ? mClockExpandedSize 385 : mClockCollapsedSize); 386 mTime.setScaleX(1f); 387 mTime.setScaleY(1f); 388 updateAmPmTranslation(); 389 } 390 391 private void updateAmPmTranslation() { 392 boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; 393 mAmPm.setTranslationX((rtl ? 1 : -1) * mTime.getWidth() * (1 - mTime.getScaleX())); 394 } 395 396 @Override 397 public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { 398 mBatteryLevel.setText(getResources().getString(R.string.battery_level_template, level)); 399 } 400 401 @Override 402 public void onPowerSaveChanged() { 403 // could not care less 404 } 405 406 @Override 407 public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) { 408 mNextAlarm = nextAlarm; 409 if (nextAlarm != null) { 410 mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm)); 411 } 412 mAlarmShowing = nextAlarm != null; 413 updateEverything(); 414 requestCaptureValues(); 415 } 416 417 private void updateClickTargets() { 418 mMultiUserSwitch.setClickable(mExpanded); 419 mMultiUserSwitch.setFocusable(mExpanded); 420 mSystemIconsSuperContainer.setClickable(mExpanded); 421 mSystemIconsSuperContainer.setFocusable(mExpanded); 422 mAlarmStatus.setClickable(mNextAlarm != null && mNextAlarm.getShowIntent() != null); 423 } 424 425 private void updateClockLp() { 426 int marginBottom = mExpanded 427 ? mClockMarginBottomExpanded 428 : mClockMarginBottomCollapsed; 429 LayoutParams lp = (LayoutParams) mDateGroup.getLayoutParams(); 430 if (marginBottom != lp.bottomMargin) { 431 lp.bottomMargin = marginBottom; 432 mDateGroup.setLayoutParams(lp); 433 } 434 } 435 436 private void updateMultiUserSwitch() { 437 int marginEnd; 438 int width; 439 if (mExpanded) { 440 marginEnd = mMultiUserExpandedMargin; 441 width = mMultiUserSwitchWidthExpanded; 442 } else { 443 marginEnd = mMultiUserCollapsedMargin; 444 width = mMultiUserSwitchWidthCollapsed; 445 } 446 MarginLayoutParams lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams(); 447 if (marginEnd != lp.getMarginEnd() || lp.width != width) { 448 lp.setMarginEnd(marginEnd); 449 lp.width = width; 450 mMultiUserSwitch.setLayoutParams(lp); 451 } 452 } 453 454 public void setExpansion(float t) { 455 if (!mExpanded) { 456 t = 0f; 457 } 458 mCurrentT = t; 459 float height = mCollapsedHeight + t * (mExpandedHeight - mCollapsedHeight); 460 if (height < mCollapsedHeight) { 461 height = mCollapsedHeight; 462 } 463 if (height > mExpandedHeight) { 464 height = mExpandedHeight; 465 } 466 setClipping(height); 467 updateLayoutValues(t); 468 } 469 470 private void updateLayoutValues(float t) { 471 if (mCaptureValues) { 472 return; 473 } 474 mCurrentValues.interpoloate(mCollapsedValues, mExpandedValues, t); 475 applyLayoutValues(mCurrentValues); 476 } 477 478 private void setClipping(float height) { 479 mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height); 480 setClipBounds(mClipBounds); 481 invalidateOutline(); 482 } 483 484 public void setUserInfoController(UserInfoController userInfoController) { 485 userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() { 486 @Override 487 public void onUserInfoChanged(String name, Drawable picture) { 488 mMultiUserAvatar.setImageDrawable(picture); 489 } 490 }); 491 } 492 493 @Override 494 public void onClick(View v) { 495 if (v == mSettingsButton) { 496 startSettingsActivity(); 497 } else if (v == mSystemIconsSuperContainer) { 498 startBatteryActivity(); 499 } else if (v == mAlarmStatus && mNextAlarm != null) { 500 PendingIntent showIntent = mNextAlarm.getShowIntent(); 501 if (showIntent != null && showIntent.isActivity()) { 502 mActivityStarter.startActivity(showIntent.getIntent(), true /* dismissShade */); 503 } 504 } 505 } 506 507 private void startSettingsActivity() { 508 mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS), 509 true /* dismissShade */); 510 } 511 512 private void startBatteryActivity() { 513 mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY), 514 true /* dismissShade */); 515 } 516 517 public void setQSPanel(QSPanel qsp) { 518 mQSPanel = qsp; 519 if (mQSPanel != null) { 520 mQSPanel.setCallback(mQsPanelCallback); 521 } 522 mMultiUserSwitch.setQsPanel(qsp); 523 } 524 525 @Override 526 public boolean shouldDelayChildPressedState() { 527 return true; 528 } 529 530 public void setShowEmergencyCallsOnly(boolean show) { 531 boolean changed = show != mShowEmergencyCallsOnly; 532 if (changed) { 533 mShowEmergencyCallsOnly = show; 534 if (mExpanded) { 535 updateEverything(); 536 requestCaptureValues(); 537 } 538 } 539 } 540 541 @Override 542 protected void dispatchSetPressed(boolean pressed) { 543 // We don't want that everything lights up when we click on the header, so block the request 544 // here. 545 } 546 547 private void captureLayoutValues(LayoutValues target) { 548 target.timeScale = mExpanded ? 1f : mClockCollapsedScaleFactor; 549 target.clockY = mClock.getBottom(); 550 target.dateY = mDateGroup.getTop(); 551 target.emergencyCallsOnlyAlpha = getAlphaForVisibility(mEmergencyCallsOnly); 552 target.alarmStatusAlpha = getAlphaForVisibility(mAlarmStatus); 553 target.dateCollapsedAlpha = getAlphaForVisibility(mDateCollapsed); 554 target.dateExpandedAlpha = getAlphaForVisibility(mDateExpanded); 555 target.avatarScale = mMultiUserAvatar.getScaleX(); 556 target.avatarX = mMultiUserSwitch.getLeft() + mMultiUserAvatar.getLeft(); 557 target.avatarY = mMultiUserSwitch.getTop() + mMultiUserAvatar.getTop(); 558 if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) { 559 target.batteryX = mSystemIconsSuperContainer.getLeft() 560 + mSystemIconsContainer.getRight(); 561 } else { 562 target.batteryX = mSystemIconsSuperContainer.getLeft() 563 + mSystemIconsContainer.getLeft(); 564 } 565 target.batteryY = mSystemIconsSuperContainer.getTop() + mSystemIconsContainer.getTop(); 566 target.batteryLevelAlpha = getAlphaForVisibility(mBatteryLevel); 567 target.settingsAlpha = getAlphaForVisibility(mSettingsButton); 568 target.settingsTranslation = mExpanded 569 ? 0 570 : mMultiUserSwitch.getLeft() - mSettingsButton.getLeft(); 571 target.signalClusterAlpha = mSignalClusterDetached ? 0f : 1f; 572 target.settingsRotation = !mExpanded ? 90f : 0f; 573 } 574 575 private float getAlphaForVisibility(View v) { 576 return v == null || v.getVisibility() == View.VISIBLE ? 1f : 0f; 577 } 578 579 private void applyAlpha(View v, float alpha) { 580 if (v == null || v.getVisibility() == View.GONE) { 581 return; 582 } 583 if (alpha == 0f) { 584 v.setVisibility(View.INVISIBLE); 585 } else { 586 v.setVisibility(View.VISIBLE); 587 v.setAlpha(alpha); 588 } 589 } 590 591 private void applyLayoutValues(LayoutValues values) { 592 mTime.setScaleX(values.timeScale); 593 mTime.setScaleY(values.timeScale); 594 mClock.setY(values.clockY - mClock.getHeight()); 595 mDateGroup.setY(values.dateY); 596 mAlarmStatus.setY(values.dateY - mAlarmStatus.getPaddingTop()); 597 mMultiUserAvatar.setScaleX(values.avatarScale); 598 mMultiUserAvatar.setScaleY(values.avatarScale); 599 mMultiUserAvatar.setX(values.avatarX - mMultiUserSwitch.getLeft()); 600 mMultiUserAvatar.setY(values.avatarY - mMultiUserSwitch.getTop()); 601 if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) { 602 mSystemIconsSuperContainer.setX(values.batteryX - mSystemIconsContainer.getRight()); 603 } else { 604 mSystemIconsSuperContainer.setX(values.batteryX - mSystemIconsContainer.getLeft()); 605 } 606 mSystemIconsSuperContainer.setY(values.batteryY - mSystemIconsContainer.getTop()); 607 if (mSignalCluster != null && mExpanded) { 608 if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) { 609 mSignalCluster.setX(mSystemIconsSuperContainer.getX() 610 - mSignalCluster.getWidth()); 611 } else { 612 mSignalCluster.setX(mSystemIconsSuperContainer.getX() 613 + mSystemIconsSuperContainer.getWidth()); 614 } 615 mSignalCluster.setY( 616 mSystemIconsSuperContainer.getY() + mSystemIconsSuperContainer.getHeight()/2 617 - mSignalCluster.getHeight()/2); 618 } else if (mSignalCluster != null) { 619 mSignalCluster.setTranslationX(0f); 620 mSignalCluster.setTranslationY(0f); 621 } 622 mSettingsButton.setTranslationY(mSystemIconsSuperContainer.getTranslationY()); 623 mSettingsButton.setTranslationX(values.settingsTranslation); 624 mSettingsButton.setRotation(values.settingsRotation); 625 applyAlpha(mEmergencyCallsOnly, values.emergencyCallsOnlyAlpha); 626 applyAlpha(mAlarmStatus, values.alarmStatusAlpha); 627 applyAlpha(mDateCollapsed, values.dateCollapsedAlpha); 628 applyAlpha(mDateExpanded, values.dateExpandedAlpha); 629 applyAlpha(mBatteryLevel, values.batteryLevelAlpha); 630 applyAlpha(mSettingsButton, values.settingsAlpha); 631 applyAlpha(mSignalCluster, values.signalClusterAlpha); 632 if (!mExpanded) { 633 mTime.setScaleX(1f); 634 mTime.setScaleY(1f); 635 } 636 updateAmPmTranslation(); 637 } 638 639 /** 640 * Captures all layout values (position, visibility) for a certain state. This is used for 641 * animations. 642 */ 643 private static final class LayoutValues { 644 645 float dateExpandedAlpha; 646 float dateCollapsedAlpha; 647 float emergencyCallsOnlyAlpha; 648 float alarmStatusAlpha; 649 float timeScale = 1f; 650 float clockY; 651 float dateY; 652 float avatarScale; 653 float avatarX; 654 float avatarY; 655 float batteryX; 656 float batteryY; 657 float batteryLevelAlpha; 658 float settingsAlpha; 659 float settingsTranslation; 660 float signalClusterAlpha; 661 float settingsRotation; 662 663 public void interpoloate(LayoutValues v1, LayoutValues v2, float t) { 664 timeScale = v1.timeScale * (1 - t) + v2.timeScale * t; 665 clockY = v1.clockY * (1 - t) + v2.clockY * t; 666 dateY = v1.dateY * (1 - t) + v2.dateY * t; 667 avatarScale = v1.avatarScale * (1 - t) + v2.avatarScale * t; 668 avatarX = v1.avatarX * (1 - t) + v2.avatarX * t; 669 avatarY = v1.avatarY * (1 - t) + v2.avatarY * t; 670 batteryX = v1.batteryX * (1 - t) + v2.batteryX * t; 671 batteryY = v1.batteryY * (1 - t) + v2.batteryY * t; 672 settingsTranslation = v1.settingsTranslation * (1 - t) + v2.settingsTranslation * t; 673 674 float t1 = Math.max(0, t - 0.5f) * 2; 675 settingsRotation = v1.settingsRotation * (1 - t1) + v2.settingsRotation * t1; 676 emergencyCallsOnlyAlpha = 677 v1.emergencyCallsOnlyAlpha * (1 - t1) + v2.emergencyCallsOnlyAlpha * t1; 678 679 float t2 = Math.min(1, 2 * t); 680 signalClusterAlpha = v1.signalClusterAlpha * (1 - t2) + v2.signalClusterAlpha * t2; 681 682 float t3 = Math.max(0, t - 0.7f) / 0.3f; 683 batteryLevelAlpha = v1.batteryLevelAlpha * (1 - t3) + v2.batteryLevelAlpha * t3; 684 settingsAlpha = v1.settingsAlpha * (1 - t3) + v2.settingsAlpha * t3; 685 dateExpandedAlpha = v1.dateExpandedAlpha * (1 - t3) + v2.dateExpandedAlpha * t3; 686 dateCollapsedAlpha = v1.dateCollapsedAlpha * (1 - t3) + v2.dateCollapsedAlpha * t3; 687 alarmStatusAlpha = v1.alarmStatusAlpha * (1 - t3) + v2.alarmStatusAlpha * t3; 688 } 689 } 690 691 private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() { 692 private boolean mScanState; 693 694 @Override 695 public void onToggleStateChanged(final boolean state) { 696 post(new Runnable() { 697 @Override 698 public void run() { 699 handleToggleStateChanged(state); 700 } 701 }); 702 } 703 704 @Override 705 public void onShowingDetail(final QSTile.DetailAdapter detail) { 706 post(new Runnable() { 707 @Override 708 public void run() { 709 handleShowingDetail(detail); 710 } 711 }); 712 } 713 714 @Override 715 public void onScanStateChanged(final boolean state) { 716 post(new Runnable() { 717 @Override 718 public void run() { 719 handleScanStateChanged(state); 720 } 721 }); 722 } 723 724 private void handleToggleStateChanged(boolean state) { 725 mQsDetailHeaderSwitch.setChecked(state); 726 } 727 728 private void handleScanStateChanged(boolean state) { 729 if (mScanState == state) return; 730 mScanState = state; 731 final Animatable anim = (Animatable) mQsDetailHeaderProgress.getDrawable(); 732 if (state) { 733 mQsDetailHeaderProgress.animate().alpha(1f); 734 anim.start(); 735 } else { 736 mQsDetailHeaderProgress.animate().alpha(0f); 737 anim.stop(); 738 } 739 } 740 741 private void handleShowingDetail(final QSTile.DetailAdapter detail) { 742 final boolean showingDetail = detail != null; 743 transition(mClock, !showingDetail); 744 transition(mDateGroup, !showingDetail); 745 if (mAlarmShowing) { 746 transition(mAlarmStatus, !showingDetail); 747 } 748 transition(mQsDetailHeader, showingDetail); 749 mShowingDetail = showingDetail; 750 if (showingDetail) { 751 String title = mContext.getString(detail.getTitle()); 752 mQsDetailHeaderTitle.setText(title); 753 announceForAccessibility(title); 754 final Boolean toggleState = detail.getToggleState(); 755 if (toggleState == null) { 756 mQsDetailHeaderSwitch.setVisibility(INVISIBLE); 757 mQsDetailHeader.setClickable(false); 758 } else { 759 mQsDetailHeaderSwitch.setVisibility(VISIBLE); 760 mQsDetailHeaderSwitch.setChecked(toggleState); 761 mQsDetailHeader.setClickable(true); 762 mQsDetailHeader.setOnClickListener(new OnClickListener() { 763 @Override 764 public void onClick(View v) { 765 detail.setToggleState(!mQsDetailHeaderSwitch.isChecked()); 766 } 767 }); 768 } 769 } else { 770 mQsDetailHeader.setClickable(false); 771 } 772 } 773 774 private void transition(final View v, final boolean in) { 775 if (in) { 776 v.bringToFront(); 777 v.setVisibility(VISIBLE); 778 } 779 v.animate() 780 .alpha(in ? 1 : 0) 781 .withLayer() 782 .withEndAction(new Runnable() { 783 @Override 784 public void run() { 785 if (!in) { 786 v.setVisibility(INVISIBLE); 787 } 788 } 789 }) 790 .start(); 791 } 792 }; 793} 794