StatusBarHeaderView.java revision e17c4b49a41ec9de5c1d7f229273fc4677ec24b1
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 } 529 } 530 } 531 532 private void startSettingsActivity() { 533 mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS), 534 true /* dismissShade */); 535 } 536 537 private void startBatteryActivity() { 538 mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY), 539 true /* dismissShade */); 540 } 541 542 public void setQSPanel(QSPanel qsp) { 543 mQSPanel = qsp; 544 if (mQSPanel != null) { 545 mQSPanel.setCallback(mQsPanelCallback); 546 } 547 mMultiUserSwitch.setQsPanel(qsp); 548 } 549 550 @Override 551 public boolean shouldDelayChildPressedState() { 552 return true; 553 } 554 555 public void setShowEmergencyCallsOnly(boolean show) { 556 boolean changed = show != mShowEmergencyCallsOnly; 557 if (changed) { 558 mShowEmergencyCallsOnly = show; 559 if (mExpanded) { 560 updateEverything(); 561 requestCaptureValues(); 562 } 563 } 564 } 565 566 @Override 567 protected void dispatchSetPressed(boolean pressed) { 568 // We don't want that everything lights up when we click on the header, so block the request 569 // here. 570 } 571 572 private void captureLayoutValues(LayoutValues target) { 573 target.timeScale = mExpanded ? 1f : mClockCollapsedScaleFactor; 574 target.clockY = mClock.getBottom(); 575 target.dateY = mDateGroup.getTop(); 576 target.emergencyCallsOnlyAlpha = getAlphaForVisibility(mEmergencyCallsOnly); 577 target.alarmStatusAlpha = getAlphaForVisibility(mAlarmStatus); 578 target.dateCollapsedAlpha = getAlphaForVisibility(mDateCollapsed); 579 target.dateExpandedAlpha = getAlphaForVisibility(mDateExpanded); 580 target.avatarScale = mMultiUserAvatar.getScaleX(); 581 target.avatarX = mMultiUserSwitch.getLeft() + mMultiUserAvatar.getLeft(); 582 target.avatarY = mMultiUserSwitch.getTop() + mMultiUserAvatar.getTop(); 583 if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) { 584 target.batteryX = mSystemIconsSuperContainer.getLeft() 585 + mSystemIconsContainer.getRight(); 586 } else { 587 target.batteryX = mSystemIconsSuperContainer.getLeft() 588 + mSystemIconsContainer.getLeft(); 589 } 590 target.batteryY = mSystemIconsSuperContainer.getTop() + mSystemIconsContainer.getTop(); 591 target.batteryLevelAlpha = getAlphaForVisibility(mBatteryLevel); 592 target.settingsAlpha = getAlphaForVisibility(mSettingsButton); 593 target.settingsTranslation = mExpanded 594 ? 0 595 : mMultiUserSwitch.getLeft() - mSettingsButton.getLeft(); 596 target.signalClusterAlpha = mSignalClusterDetached ? 0f : 1f; 597 target.settingsRotation = !mExpanded ? 90f : 0f; 598 } 599 600 private float getAlphaForVisibility(View v) { 601 return v == null || v.getVisibility() == View.VISIBLE ? 1f : 0f; 602 } 603 604 private void applyAlpha(View v, float alpha) { 605 if (v == null || v.getVisibility() == View.GONE) { 606 return; 607 } 608 if (alpha == 0f) { 609 v.setVisibility(View.INVISIBLE); 610 } else { 611 v.setVisibility(View.VISIBLE); 612 v.setAlpha(alpha); 613 } 614 } 615 616 private void applyLayoutValues(LayoutValues values) { 617 mTime.setScaleX(values.timeScale); 618 mTime.setScaleY(values.timeScale); 619 mClock.setY(values.clockY - mClock.getHeight()); 620 mDateGroup.setY(values.dateY); 621 mAlarmStatus.setY(values.dateY - mAlarmStatus.getPaddingTop()); 622 mMultiUserAvatar.setScaleX(values.avatarScale); 623 mMultiUserAvatar.setScaleY(values.avatarScale); 624 mMultiUserAvatar.setX(values.avatarX - mMultiUserSwitch.getLeft()); 625 mMultiUserAvatar.setY(values.avatarY - mMultiUserSwitch.getTop()); 626 if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) { 627 mSystemIconsSuperContainer.setX(values.batteryX - mSystemIconsContainer.getRight()); 628 } else { 629 mSystemIconsSuperContainer.setX(values.batteryX - mSystemIconsContainer.getLeft()); 630 } 631 mSystemIconsSuperContainer.setY(values.batteryY - mSystemIconsContainer.getTop()); 632 if (mSignalCluster != null && mExpanded) { 633 if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) { 634 mSignalCluster.setX(mSystemIconsSuperContainer.getX() 635 - mSignalCluster.getWidth()); 636 } else { 637 mSignalCluster.setX(mSystemIconsSuperContainer.getX() 638 + mSystemIconsSuperContainer.getWidth()); 639 } 640 mSignalCluster.setY( 641 mSystemIconsSuperContainer.getY() + mSystemIconsSuperContainer.getHeight()/2 642 - mSignalCluster.getHeight()/2); 643 } else if (mSignalCluster != null) { 644 mSignalCluster.setTranslationX(0f); 645 mSignalCluster.setTranslationY(0f); 646 } 647 mSettingsButton.setTranslationY(mSystemIconsSuperContainer.getTranslationY()); 648 mSettingsButton.setTranslationX(values.settingsTranslation); 649 mSettingsButton.setRotation(values.settingsRotation); 650 applyAlpha(mEmergencyCallsOnly, values.emergencyCallsOnlyAlpha); 651 applyAlpha(mAlarmStatus, values.alarmStatusAlpha); 652 applyAlpha(mDateCollapsed, values.dateCollapsedAlpha); 653 applyAlpha(mDateExpanded, values.dateExpandedAlpha); 654 applyAlpha(mBatteryLevel, values.batteryLevelAlpha); 655 applyAlpha(mSettingsButton, values.settingsAlpha); 656 applyAlpha(mSignalCluster, values.signalClusterAlpha); 657 if (!mExpanded) { 658 mTime.setScaleX(1f); 659 mTime.setScaleY(1f); 660 } 661 updateAmPmTranslation(); 662 } 663 664 /** 665 * Captures all layout values (position, visibility) for a certain state. This is used for 666 * animations. 667 */ 668 private static final class LayoutValues { 669 670 float dateExpandedAlpha; 671 float dateCollapsedAlpha; 672 float emergencyCallsOnlyAlpha; 673 float alarmStatusAlpha; 674 float timeScale = 1f; 675 float clockY; 676 float dateY; 677 float avatarScale; 678 float avatarX; 679 float avatarY; 680 float batteryX; 681 float batteryY; 682 float batteryLevelAlpha; 683 float settingsAlpha; 684 float settingsTranslation; 685 float signalClusterAlpha; 686 float settingsRotation; 687 688 public void interpoloate(LayoutValues v1, LayoutValues v2, float t) { 689 timeScale = v1.timeScale * (1 - t) + v2.timeScale * t; 690 clockY = v1.clockY * (1 - t) + v2.clockY * t; 691 dateY = v1.dateY * (1 - t) + v2.dateY * t; 692 avatarScale = v1.avatarScale * (1 - t) + v2.avatarScale * t; 693 avatarX = v1.avatarX * (1 - t) + v2.avatarX * t; 694 avatarY = v1.avatarY * (1 - t) + v2.avatarY * t; 695 batteryX = v1.batteryX * (1 - t) + v2.batteryX * t; 696 batteryY = v1.batteryY * (1 - t) + v2.batteryY * t; 697 settingsTranslation = v1.settingsTranslation * (1 - t) + v2.settingsTranslation * t; 698 699 float t1 = Math.max(0, t - 0.5f) * 2; 700 settingsRotation = v1.settingsRotation * (1 - t1) + v2.settingsRotation * t1; 701 emergencyCallsOnlyAlpha = 702 v1.emergencyCallsOnlyAlpha * (1 - t1) + v2.emergencyCallsOnlyAlpha * t1; 703 704 float t2 = Math.min(1, 2 * t); 705 signalClusterAlpha = v1.signalClusterAlpha * (1 - t2) + v2.signalClusterAlpha * t2; 706 707 float t3 = Math.max(0, t - 0.7f) / 0.3f; 708 batteryLevelAlpha = v1.batteryLevelAlpha * (1 - t3) + v2.batteryLevelAlpha * t3; 709 settingsAlpha = v1.settingsAlpha * (1 - t3) + v2.settingsAlpha * t3; 710 dateExpandedAlpha = v1.dateExpandedAlpha * (1 - t3) + v2.dateExpandedAlpha * t3; 711 dateCollapsedAlpha = v1.dateCollapsedAlpha * (1 - t3) + v2.dateCollapsedAlpha * t3; 712 alarmStatusAlpha = v1.alarmStatusAlpha * (1 - t3) + v2.alarmStatusAlpha * t3; 713 } 714 } 715 716 private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() { 717 private boolean mScanState; 718 719 @Override 720 public void onToggleStateChanged(final boolean state) { 721 post(new Runnable() { 722 @Override 723 public void run() { 724 handleToggleStateChanged(state); 725 } 726 }); 727 } 728 729 @Override 730 public void onShowingDetail(final QSTile.DetailAdapter detail) { 731 post(new Runnable() { 732 @Override 733 public void run() { 734 handleShowingDetail(detail); 735 } 736 }); 737 } 738 739 @Override 740 public void onScanStateChanged(final boolean state) { 741 post(new Runnable() { 742 @Override 743 public void run() { 744 handleScanStateChanged(state); 745 } 746 }); 747 } 748 749 private void handleToggleStateChanged(boolean state) { 750 mQsDetailHeaderSwitch.setChecked(state); 751 } 752 753 private void handleScanStateChanged(boolean state) { 754 if (mScanState == state) return; 755 mScanState = state; 756 final Animatable anim = (Animatable) mQsDetailHeaderProgress.getDrawable(); 757 if (state) { 758 mQsDetailHeaderProgress.animate().alpha(1f); 759 anim.start(); 760 } else { 761 mQsDetailHeaderProgress.animate().alpha(0f); 762 anim.stop(); 763 } 764 } 765 766 private void handleShowingDetail(final QSTile.DetailAdapter detail) { 767 final boolean showingDetail = detail != null; 768 transition(mClock, !showingDetail); 769 transition(mDateGroup, !showingDetail); 770 if (mAlarmShowing) { 771 transition(mAlarmStatus, !showingDetail); 772 } 773 transition(mQsDetailHeader, showingDetail); 774 mShowingDetail = showingDetail; 775 if (showingDetail) { 776 mQsDetailHeaderTitle.setText(detail.getTitle()); 777 final Boolean toggleState = detail.getToggleState(); 778 if (toggleState == null) { 779 mQsDetailHeaderSwitch.setVisibility(INVISIBLE); 780 mQsDetailHeader.setClickable(false); 781 } else { 782 mQsDetailHeaderSwitch.setVisibility(VISIBLE); 783 mQsDetailHeaderSwitch.setChecked(toggleState); 784 mQsDetailHeader.setClickable(true); 785 mQsDetailHeader.setOnClickListener(new OnClickListener() { 786 @Override 787 public void onClick(View v) { 788 detail.setToggleState(!mQsDetailHeaderSwitch.isChecked()); 789 } 790 }); 791 } 792 } else { 793 mQsDetailHeader.setClickable(false); 794 } 795 } 796 797 private void transition(final View v, final boolean in) { 798 if (in) { 799 v.bringToFront(); 800 v.setVisibility(VISIBLE); 801 } 802 v.animate() 803 .alpha(in ? 1 : 0) 804 .withLayer() 805 .withEndAction(new Runnable() { 806 @Override 807 public void run() { 808 if (!in) { 809 v.setVisibility(INVISIBLE); 810 } 811 } 812 }) 813 .start(); 814 } 815 }; 816} 817