StatusBarHeaderView.java revision 329fa24048ffc8a35566bbacb891ac144be85b1c
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.content.Context; 20import android.content.Intent; 21import android.graphics.Outline; 22import android.graphics.Rect; 23import android.graphics.drawable.Drawable; 24import android.util.AttributeSet; 25import android.view.View; 26import android.view.ViewGroup; 27import android.widget.ImageView; 28import android.widget.LinearLayout; 29import android.widget.RelativeLayout; 30import android.widget.Switch; 31import android.widget.TextView; 32 33import com.android.systemui.R; 34import com.android.systemui.qs.QSPanel; 35import com.android.systemui.qs.QSTile; 36import com.android.systemui.statusbar.policy.BatteryController; 37import com.android.systemui.statusbar.policy.UserInfoController; 38 39/** 40 * The view to manage the header area in the expanded status bar. 41 */ 42public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener, 43 BatteryController.BatteryStateChangeCallback { 44 45 private boolean mExpanded; 46 private boolean mListening; 47 private boolean mOverscrolled; 48 private boolean mKeyguardShowing; 49 private boolean mCharging; 50 51 private ViewGroup mSystemIconsContainer; 52 private View mSystemIconsSuperContainer; 53 private View mDateTime; 54 private View mTime; 55 private View mAmPm; 56 private View mKeyguardCarrierText; 57 private MultiUserSwitch mMultiUserSwitch; 58 private ImageView mMultiUserAvatar; 59 private View mDateCollapsed; 60 private View mDateExpanded; 61 private View mStatusIcons; 62 private View mSignalCluster; 63 private View mSettingsButton; 64 private View mQsDetailHeader; 65 private View mEmergencyCallsOnly; 66 private TextView mBatteryLevel; 67 68 private boolean mShowEmergencyCallsOnly; 69 private boolean mKeyguardUserSwitcherShowing; 70 71 private int mCollapsedHeight; 72 private int mExpandedHeight; 73 private int mKeyguardHeight; 74 75 private int mKeyguardWidth = ViewGroup.LayoutParams.MATCH_PARENT; 76 private int mNormalWidth; 77 private int mPadding; 78 private int mMultiUserExpandedMargin; 79 private int mMultiUserCollapsedMargin; 80 private int mMultiUserKeyguardMargin; 81 private int mSystemIconsSwitcherHiddenExpandedMargin; 82 private int mClockMarginBottomExpanded; 83 private int mMultiUserSwitchWidthCollapsed; 84 private int mMultiUserSwitchWidthExpanded; 85 private int mBatteryPaddingEnd; 86 87 /** 88 * In collapsed QS, the clock and avatar are scaled down a bit post-layout to allow for a nice 89 * transition. These values determine that factor. 90 */ 91 private float mClockCollapsedScaleFactor; 92 private float mAvatarCollapsedScaleFactor; 93 94 private ActivityStarter mActivityStarter; 95 private BatteryController mBatteryController; 96 private QSPanel mQSPanel; 97 98 private final Rect mClipBounds = new Rect(); 99 private final Outline mOutline = new Outline(); 100 101 public StatusBarHeaderView(Context context, AttributeSet attrs) { 102 super(context, attrs); 103 } 104 105 @Override 106 protected void onFinishInflate() { 107 super.onFinishInflate(); 108 mSystemIconsSuperContainer = findViewById(R.id.system_icons_super_container); 109 mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container); 110 mSystemIconsSuperContainer.setOnClickListener(this); 111 mDateTime = findViewById(R.id.datetime); 112 mTime = findViewById(R.id.time_view); 113 mAmPm = findViewById(R.id.am_pm_view); 114 mKeyguardCarrierText = findViewById(R.id.keyguard_carrier_text); 115 mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch); 116 mMultiUserAvatar = (ImageView) findViewById(R.id.multi_user_avatar); 117 mDateCollapsed = findViewById(R.id.date_collapsed); 118 mDateExpanded = findViewById(R.id.date_expanded); 119 mSettingsButton = findViewById(R.id.settings_button); 120 mSettingsButton.setOnClickListener(this); 121 mQsDetailHeader = findViewById(R.id.qs_detail_header); 122 mQsDetailHeader.setAlpha(0); 123 mEmergencyCallsOnly = findViewById(R.id.header_emergency_calls_only); 124 mBatteryLevel = (TextView) findViewById(R.id.battery_level); 125 loadDimens(); 126 updateVisibilities(); 127 updateClockScale(); 128 updateAvatarScale(); 129 addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 130 @Override 131 public void onLayoutChange(View v, int left, int top, int right, 132 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 133 if ((right - left) != (oldRight - oldLeft)) { 134 // width changed, update clipping 135 setClipping(getHeight()); 136 } 137 boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; 138 mTime.setPivotX(rtl ? mTime.getWidth() : 0); 139 mTime.setPivotY(mTime.getBaseline()); 140 mAmPm.setPivotX(rtl ? mAmPm.getWidth() : 0); 141 mAmPm.setPivotY(mAmPm.getBaseline()); 142 updateAmPmTranslation(); 143 } 144 }); 145 } 146 147 private void loadDimens() { 148 mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height); 149 mExpandedHeight = getResources().getDimensionPixelSize( 150 R.dimen.status_bar_header_height_expanded); 151 mKeyguardHeight = getResources().getDimensionPixelSize( 152 R.dimen.status_bar_header_height_keyguard); 153 mNormalWidth = getLayoutParams().width; 154 mPadding = getResources().getDimensionPixelSize(R.dimen.notification_side_padding); 155 mMultiUserExpandedMargin = 156 getResources().getDimensionPixelSize(R.dimen.multi_user_switch_expanded_margin); 157 mMultiUserCollapsedMargin = 158 getResources().getDimensionPixelSize(R.dimen.multi_user_switch_collapsed_margin); 159 mMultiUserKeyguardMargin = 160 getResources().getDimensionPixelSize(R.dimen.multi_user_switch_keyguard_margin); 161 mSystemIconsSwitcherHiddenExpandedMargin = getResources().getDimensionPixelSize( 162 R.dimen.system_icons_switcher_hidden_expanded_margin); 163 mClockMarginBottomExpanded = 164 getResources().getDimensionPixelSize(R.dimen.clock_expanded_bottom_margin); 165 mMultiUserSwitchWidthCollapsed = 166 getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_collapsed); 167 mMultiUserSwitchWidthExpanded = 168 getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_expanded); 169 mAvatarCollapsedScaleFactor = 170 getResources().getDimensionPixelSize(R.dimen.multi_user_avatar_collapsed_size) 171 / (float) mMultiUserAvatar.getLayoutParams().width; 172 mClockCollapsedScaleFactor = 173 (float) getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size) 174 / (float) getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size); 175 mBatteryPaddingEnd = 176 getResources().getDimensionPixelSize(R.dimen.battery_level_padding_end); 177 } 178 179 public void setActivityStarter(ActivityStarter activityStarter) { 180 mActivityStarter = activityStarter; 181 } 182 183 public void setBatteryController(BatteryController batteryController) { 184 mBatteryController = batteryController; 185 } 186 187 public int getCollapsedHeight() { 188 return mKeyguardShowing ? mKeyguardHeight : mCollapsedHeight; 189 } 190 191 public int getExpandedHeight() { 192 return mExpandedHeight; 193 } 194 195 public void setListening(boolean listening) { 196 if (listening == mListening) { 197 return; 198 } 199 mListening = listening; 200 updateBatteryListening(); 201 } 202 203 public void setExpanded(boolean expanded, boolean overscrolled) { 204 boolean changed = expanded != mExpanded; 205 boolean overscrollChanged = overscrolled != mOverscrolled; 206 mExpanded = expanded; 207 mOverscrolled = overscrolled; 208 if (changed || overscrollChanged) { 209 updateHeights(); 210 updateVisibilities(); 211 updateSystemIconsLayoutParams(); 212 updateZTranslation(); 213 updateClickTargets(); 214 updateWidth(); 215 updatePadding(); 216 updateMultiUserSwitch(); 217 if (mQSPanel != null) { 218 mQSPanel.setExpanded(expanded && !overscrolled); 219 } 220 updateClockScale(); 221 updateAvatarScale(); 222 updateClockLp(); 223 updateBatteryLevelPaddingEnd(); 224 } 225 } 226 227 private void updateHeights() { 228 boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded; 229 int height; 230 if (mExpanded) { 231 height = mExpandedHeight; 232 } else if (onKeyguardAndCollapsed) { 233 height = mKeyguardHeight; 234 } else { 235 height = mCollapsedHeight; 236 } 237 ViewGroup.LayoutParams lp = getLayoutParams(); 238 if (lp.height != height) { 239 lp.height = height; 240 setLayoutParams(lp); 241 } 242 int systemIconsContainerHeight = onKeyguardAndCollapsed ? mKeyguardHeight : mCollapsedHeight; 243 lp = mSystemIconsSuperContainer.getLayoutParams(); 244 if (lp.height != systemIconsContainerHeight) { 245 lp.height = systemIconsContainerHeight; 246 mSystemIconsSuperContainer.setLayoutParams(lp); 247 } 248 lp = mMultiUserSwitch.getLayoutParams(); 249 if (lp.height != systemIconsContainerHeight) { 250 lp.height = systemIconsContainerHeight; 251 mMultiUserSwitch.setLayoutParams(lp); 252 } 253 } 254 255 private void updateWidth() { 256 int width = (mKeyguardShowing && !mExpanded) ? mKeyguardWidth : mNormalWidth; 257 ViewGroup.LayoutParams lp = getLayoutParams(); 258 if (width != lp.width) { 259 lp.width = width; 260 setLayoutParams(lp); 261 } 262 } 263 264 private void updateVisibilities() { 265 boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded; 266 if (onKeyguardAndCollapsed) { 267 setBackground(null); 268 } else { 269 setBackgroundResource(R.drawable.notification_header_bg); 270 } 271 mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE); 272 mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE); 273 mDateCollapsed.setVisibility(mExpanded && !mOverscrolled ? View.GONE : View.VISIBLE); 274 mDateExpanded.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE); 275 mSettingsButton.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE); 276 mQsDetailHeader.setVisibility(mExpanded ? View.VISIBLE : View.GONE); 277 if (mStatusIcons != null) { 278 mStatusIcons.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE); 279 } 280 if (mSignalCluster != null) { 281 mSignalCluster.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE); 282 } 283 mEmergencyCallsOnly.setVisibility(mExpanded && !mOverscrolled && mShowEmergencyCallsOnly 284 ? VISIBLE : GONE); 285 mMultiUserSwitch.setVisibility(mExpanded || !mKeyguardUserSwitcherShowing 286 ? VISIBLE : GONE); 287 mBatteryLevel.setVisibility(mKeyguardShowing && mCharging || mExpanded && !mOverscrolled 288 ? View.VISIBLE : View.GONE); 289 } 290 291 private void updateSystemIconsLayoutParams() { 292 RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsSuperContainer.getLayoutParams(); 293 lp.addRule(RelativeLayout.START_OF, mExpanded && !mOverscrolled 294 ? mSettingsButton.getId() 295 : mMultiUserSwitch.getId()); 296 lp.removeRule(ALIGN_PARENT_START); 297 if (mMultiUserSwitch.getVisibility() == GONE) { 298 lp.setMarginEnd(mSystemIconsSwitcherHiddenExpandedMargin); 299 } else { 300 lp.setMarginEnd(0); 301 } 302 mSystemIconsSuperContainer.setLayoutParams(lp); 303 } 304 305 private void updateBatteryListening() { 306 if (mListening) { 307 mBatteryController.addStateChangedCallback(this); 308 } else { 309 mBatteryController.removeStateChangedCallback(this); 310 } 311 } 312 313 private void updateAvatarScale() { 314 if (!mExpanded || mOverscrolled) { 315 mMultiUserSwitch.setScaleX(mAvatarCollapsedScaleFactor); 316 mMultiUserSwitch.setScaleY(mAvatarCollapsedScaleFactor); 317 } else { 318 mMultiUserSwitch.setScaleX(1f); 319 mMultiUserSwitch.setScaleY(1f); 320 } 321 } 322 323 private void updateClockScale() { 324 mAmPm.setScaleX(mClockCollapsedScaleFactor); 325 mAmPm.setScaleY(mClockCollapsedScaleFactor); 326 if (!mExpanded || mOverscrolled) { 327 mTime.setScaleX(mClockCollapsedScaleFactor); 328 mTime.setScaleY(mClockCollapsedScaleFactor); 329 } else { 330 mTime.setScaleX(1f); 331 mTime.setScaleY(1f); 332 } 333 updateAmPmTranslation(); 334 } 335 336 private void updateAmPmTranslation() { 337 boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; 338 mAmPm.setTranslationX((rtl ? 1 : -1) * mTime.getWidth() * (1 - mTime.getScaleX())); 339 } 340 341 private void updateBatteryLevelPaddingEnd() { 342 mBatteryLevel.setPaddingRelative(0, 0, 343 mKeyguardShowing && !mExpanded ? 0 : mBatteryPaddingEnd, 0); 344 } 345 346 @Override 347 public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { 348 mBatteryLevel.setText(getResources().getString(R.string.battery_level_template, level)); 349 boolean changed = mCharging != charging; 350 mCharging = charging; 351 if (changed) { 352 updateVisibilities(); 353 } 354 } 355 356 private void updateClickTargets() { 357 setClickable(!mKeyguardShowing || mExpanded); 358 mDateTime.setClickable(mExpanded); 359 mMultiUserSwitch.setClickable(mExpanded); 360 mSystemIconsSuperContainer.setClickable(mExpanded); 361 } 362 363 private void updateZTranslation() { 364 365 // If we are on the Keyguard, we need to set our z position to zero, so we don't get 366 // shadows. 367 if (mKeyguardShowing && !mExpanded) { 368 setZ(0); 369 } else { 370 setTranslationZ(0); 371 } 372 } 373 374 private void updatePadding() { 375 boolean padded = !mKeyguardShowing || mExpanded; 376 int padding = padded ? mPadding : 0; 377 setPaddingRelative(padding, 0, padding, 0); 378 } 379 380 private void updateClockLp() { 381 int marginBottom = mExpanded && !mOverscrolled ? mClockMarginBottomExpanded : 0; 382 LayoutParams lp = (LayoutParams) mDateTime.getLayoutParams(); 383 int rule = mExpanded && !mOverscrolled ? TRUE : 0; 384 if (marginBottom != lp.bottomMargin 385 || lp.getRules()[RelativeLayout.ALIGN_PARENT_BOTTOM] != rule) { 386 lp.bottomMargin = marginBottom; 387 lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, rule); 388 mDateTime.setLayoutParams(lp); 389 } 390 } 391 392 private void updateMultiUserSwitch() { 393 int marginEnd; 394 if (mExpanded && !mOverscrolled) { 395 marginEnd = mMultiUserExpandedMargin; 396 } else if (mKeyguardShowing) { 397 marginEnd = mMultiUserKeyguardMargin; 398 } else { 399 marginEnd = mMultiUserCollapsedMargin; 400 } 401 int width = mExpanded && !mOverscrolled 402 ? mMultiUserSwitchWidthExpanded 403 : mMultiUserSwitchWidthCollapsed; 404 MarginLayoutParams lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams(); 405 if (marginEnd != lp.getMarginEnd() || lp.width != width) { 406 lp.setMarginEnd(marginEnd); 407 lp.width = width; 408 mMultiUserSwitch.setLayoutParams(lp); 409 } 410 } 411 412 public void setExpansion(float 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 } 422 423 private void setClipping(float height) { 424 mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height); 425 setClipBounds(mClipBounds); 426 mOutline.setRect(mClipBounds); 427 setOutline(mOutline); 428 } 429 430 public void attachSystemIcons(LinearLayout systemIcons) { 431 mSystemIconsContainer.addView(systemIcons); 432 mStatusIcons = systemIcons.findViewById(R.id.statusIcons); 433 mSignalCluster = systemIcons.findViewById(R.id.signal_cluster); 434 } 435 436 public void onSystemIconsDetached() { 437 if (mStatusIcons != null) { 438 mStatusIcons.setVisibility(View.VISIBLE); 439 } 440 if (mSignalCluster != null) { 441 mSignalCluster.setVisibility(View.VISIBLE); 442 } 443 mStatusIcons = null; 444 mSignalCluster = null; 445 } 446 447 public void setKeyguardShowing(boolean keyguardShowing) { 448 mKeyguardShowing = keyguardShowing; 449 updateHeights(); 450 updateWidth(); 451 updateVisibilities(); 452 updateZTranslation(); 453 updatePadding(); 454 updateMultiUserSwitch(); 455 updateClickTargets(); 456 updateBatteryLevelPaddingEnd(); 457 } 458 459 public void setUserInfoController(UserInfoController userInfoController) { 460 userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() { 461 @Override 462 public void onUserInfoChanged(String name, Drawable picture) { 463 mMultiUserAvatar.setImageDrawable(picture); 464 } 465 }); 466 } 467 468 public void setOverlayParent(ViewGroup parent) { 469 mMultiUserSwitch.setOverlayParent(parent); 470 } 471 472 @Override 473 public void onClick(View v) { 474 if (v == mSettingsButton) { 475 startSettingsActivity(); 476 } else if (v == mSystemIconsSuperContainer) { 477 startBatteryActivity(); 478 } 479 } 480 481 private void startSettingsActivity() { 482 mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS)); 483 } 484 485 private void startBatteryActivity() { 486 mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY)); 487 } 488 489 public void setQSPanel(QSPanel qsp) { 490 mQSPanel = qsp; 491 if (mQSPanel != null) { 492 mQSPanel.setCallback(mQsPanelCallback); 493 } 494 } 495 496 @Override 497 public boolean shouldDelayChildPressedState() { 498 return true; 499 } 500 501 public void setShowEmergencyCallsOnly(boolean show) { 502 mShowEmergencyCallsOnly = show; 503 if (mExpanded) { 504 updateVisibilities(); 505 } 506 } 507 508 public void setKeyguardUserSwitcherShowing(boolean showing) { 509 // STOPSHIP: NOT CALLED PROPERLY WHEN GOING TO FULL SHADE AND RETURNING!?! 510 mKeyguardUserSwitcherShowing = showing; 511 updateVisibilities(); 512 updateSystemIconsLayoutParams(); 513 } 514 515 @Override 516 public boolean hasOverlappingRendering() { 517 return !mKeyguardShowing || mExpanded; 518 } 519 520 @Override 521 protected void dispatchSetPressed(boolean pressed) { 522 // We don't want that everything lights up when we click on the header, so block the request 523 // here. 524 } 525 526 private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() { 527 @Override 528 public void onToggleStateChanged(final boolean state) { 529 post(new Runnable() { 530 @Override 531 public void run() { 532 handleToggleStateChanged(state); 533 } 534 }); 535 } 536 537 @Override 538 public void onShowingDetail(final QSTile.DetailAdapter detail) { 539 post(new Runnable() { 540 @Override 541 public void run() { 542 handleShowingDetail(detail); 543 } 544 }); 545 } 546 547 private void handleToggleStateChanged(boolean state) { 548 final Switch headerSwitch = (Switch) 549 mQsDetailHeader.findViewById(android.R.id.toggle); 550 headerSwitch.setChecked(state); 551 } 552 553 private void handleShowingDetail(final QSTile.DetailAdapter detail) { 554 final boolean showingDetail = detail != null; 555 transition(mDateTime, !showingDetail); 556 transition(mQsDetailHeader, showingDetail); 557 if (showingDetail) { 558 final TextView headerTitle = (TextView) 559 mQsDetailHeader.findViewById(android.R.id.title); 560 headerTitle.setText(detail.getTitle()); 561 final Switch headerSwitch = (Switch) 562 mQsDetailHeader.findViewById(android.R.id.toggle); 563 final Boolean toggleState = detail.getToggleState(); 564 if (toggleState == null) { 565 headerSwitch.setVisibility(INVISIBLE); 566 mQsDetailHeader.setClickable(false); 567 } else { 568 headerSwitch.setVisibility(VISIBLE); 569 headerSwitch.setChecked(toggleState); 570 mQsDetailHeader.setClickable(true); 571 mQsDetailHeader.setOnClickListener(new OnClickListener() { 572 @Override 573 public void onClick(View v) { 574 detail.setToggleState(!toggleState); 575 } 576 }); 577 } 578 } else { 579 mQsDetailHeader.setClickable(false); 580 } 581 } 582 583 private void transition(final View v, final boolean in) { 584 if (in) { 585 v.bringToFront(); 586 } 587 v.animate().alpha(in ? 1 : 0).withLayer().start(); 588 } 589 }; 590} 591