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