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