ExpandableNotificationRow.java revision 4bb593492ccf560382fac9ae533a7429cec53c94
1/* 2 * Copyright (C) 2013 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; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.AnimatorSet; 22import android.animation.ObjectAnimator; 23import android.animation.ValueAnimator; 24import android.animation.ValueAnimator.AnimatorUpdateListener; 25import android.app.Notification; 26import android.content.Context; 27import android.graphics.drawable.AnimatedVectorDrawable; 28import android.graphics.drawable.AnimationDrawable; 29import android.graphics.drawable.ColorDrawable; 30import android.graphics.drawable.Drawable; 31import android.os.Build; 32import android.service.notification.StatusBarNotification; 33import android.util.AttributeSet; 34import android.view.LayoutInflater; 35import android.view.MotionEvent; 36import android.view.NotificationHeaderView; 37import android.view.View; 38import android.view.ViewStub; 39import android.view.accessibility.AccessibilityEvent; 40import android.widget.Chronometer; 41import android.widget.ImageView; 42import android.widget.RemoteViews; 43 44import com.android.internal.util.NotificationColorUtil; 45import com.android.systemui.R; 46import com.android.systemui.classifier.FalsingManager; 47import com.android.systemui.statusbar.notification.HybridNotificationView; 48import com.android.systemui.statusbar.notification.NotificationViewWrapper; 49import com.android.systemui.statusbar.phone.NotificationGroupManager; 50import com.android.systemui.statusbar.policy.HeadsUpManager; 51import com.android.systemui.statusbar.stack.NotificationChildrenContainer; 52import com.android.systemui.statusbar.stack.StackScrollState; 53import com.android.systemui.statusbar.stack.StackStateAnimator; 54import com.android.systemui.statusbar.stack.StackViewState; 55 56import java.util.ArrayList; 57import java.util.List; 58 59public class ExpandableNotificationRow extends ActivatableNotificationView { 60 61 private static final int DEFAULT_DIVIDER_ALPHA = 0x29; 62 private static final int COLORED_DIVIDER_ALPHA = 0x7B; 63 private int mNotificationMinHeightLegacy; 64 private int mMaxHeadsUpHeightLegacy; 65 private int mMaxHeadsUpHeight; 66 private int mNotificationMinHeight; 67 private int mNotificationMaxHeight; 68 69 /** Does this row contain layouts that can adapt to row expansion */ 70 private boolean mExpandable; 71 /** Has the user actively changed the expansion state of this row */ 72 private boolean mHasUserChangedExpansion; 73 /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */ 74 private boolean mUserExpanded; 75 76 /** 77 * Has this notification been expanded while it was pinned 78 */ 79 private boolean mExpandedWhenPinned; 80 /** Is the user touching this row */ 81 private boolean mUserLocked; 82 /** Are we showing the "public" version */ 83 private boolean mShowingPublic; 84 private boolean mSensitive; 85 private boolean mSensitiveHiddenInGeneral; 86 private boolean mShowingPublicInitialized; 87 private boolean mHideSensitiveForIntrinsicHeight; 88 89 /** 90 * Is this notification expanded by the system. The expansion state can be overridden by the 91 * user expansion. 92 */ 93 private boolean mIsSystemExpanded; 94 95 /** 96 * Whether the notification is on the keyguard and the expansion is disabled. 97 */ 98 private boolean mOnKeyguard; 99 100 private AnimatorSet mTranslateAnim; 101 private ArrayList<View> mTranslateableViews; 102 private NotificationContentView mPublicLayout; 103 private NotificationContentView mPrivateLayout; 104 private int mMaxExpandHeight; 105 private int mHeadsUpHeight; 106 private View mVetoButton; 107 private int mNotificationColor; 108 private boolean mClearable; 109 private ExpansionLogger mLogger; 110 private String mLoggingKey; 111 private NotificationSettingsIconRow mSettingsIconRow; 112 private NotificationGuts mGuts; 113 private NotificationData.Entry mEntry; 114 private StatusBarNotification mStatusBarNotification; 115 private String mAppName; 116 private boolean mIsHeadsUp; 117 private boolean mLastChronometerRunning = true; 118 private NotificationHeaderView mNotificationHeader; 119 private NotificationViewWrapper mNotificationHeaderWrapper; 120 private ViewStub mChildrenContainerStub; 121 private NotificationGroupManager mGroupManager; 122 private boolean mChildrenExpanded; 123 private boolean mIsSummaryWithChildren; 124 private NotificationChildrenContainer mChildrenContainer; 125 private ViewStub mSettingsIconRowStub; 126 private ViewStub mGutsStub; 127 private boolean mIsSystemChildExpanded; 128 private boolean mIsPinned; 129 private FalsingManager mFalsingManager; 130 private HeadsUpManager mHeadsUpManager; 131 private NotificationHeaderUtil mHeaderUtil = new NotificationHeaderUtil(this); 132 133 private boolean mJustClicked; 134 private boolean mIconAnimationRunning; 135 private boolean mShowNoBackground; 136 private ExpandableNotificationRow mNotificationParent; 137 private OnExpandClickListener mOnExpandClickListener; 138 private OnClickListener mExpandClickListener = new OnClickListener() { 139 @Override 140 public void onClick(View v) { 141 if (!mShowingPublic && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) { 142 mGroupManager.toggleGroupExpansion(mStatusBarNotification); 143 mOnExpandClickListener.onExpandClicked(mEntry, 144 mGroupManager.isGroupExpanded(mStatusBarNotification)); 145 } else { 146 boolean nowExpanded; 147 if (isPinned()) { 148 nowExpanded = !mExpandedWhenPinned; 149 mExpandedWhenPinned = nowExpanded; 150 } else { 151 nowExpanded = !isExpanded(); 152 setUserExpanded(nowExpanded); 153 } 154 notifyHeightChanged(true); 155 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded); 156 } 157 } 158 }; 159 160 public NotificationContentView getPrivateLayout() { 161 return mPrivateLayout; 162 } 163 164 public NotificationContentView getPublicLayout() { 165 return mPublicLayout; 166 } 167 168 public void setIconAnimationRunning(boolean running) { 169 setIconAnimationRunning(running, mPublicLayout); 170 setIconAnimationRunning(running, mPrivateLayout); 171 setIconAnimationRunningForChild(running, mNotificationHeader); 172 if (mIsSummaryWithChildren) { 173 List<ExpandableNotificationRow> notificationChildren = 174 mChildrenContainer.getNotificationChildren(); 175 for (int i = 0; i < notificationChildren.size(); i++) { 176 ExpandableNotificationRow child = notificationChildren.get(i); 177 child.setIconAnimationRunning(running); 178 } 179 } 180 mIconAnimationRunning = running; 181 } 182 183 private void setIconAnimationRunning(boolean running, NotificationContentView layout) { 184 if (layout != null) { 185 View contractedChild = layout.getContractedChild(); 186 View expandedChild = layout.getExpandedChild(); 187 View headsUpChild = layout.getHeadsUpChild(); 188 setIconAnimationRunningForChild(running, contractedChild); 189 setIconAnimationRunningForChild(running, expandedChild); 190 setIconAnimationRunningForChild(running, headsUpChild); 191 } 192 } 193 194 private void setIconAnimationRunningForChild(boolean running, View child) { 195 if (child != null) { 196 ImageView icon = (ImageView) child.findViewById(com.android.internal.R.id.icon); 197 setIconRunning(icon, running); 198 ImageView rightIcon = (ImageView) child.findViewById( 199 com.android.internal.R.id.right_icon); 200 setIconRunning(rightIcon, running); 201 } 202 } 203 204 private void setIconRunning(ImageView imageView, boolean running) { 205 if (imageView != null) { 206 Drawable drawable = imageView.getDrawable(); 207 if (drawable instanceof AnimationDrawable) { 208 AnimationDrawable animationDrawable = (AnimationDrawable) drawable; 209 if (running) { 210 animationDrawable.start(); 211 } else { 212 animationDrawable.stop(); 213 } 214 } else if (drawable instanceof AnimatedVectorDrawable) { 215 AnimatedVectorDrawable animationDrawable = (AnimatedVectorDrawable) drawable; 216 if (running) { 217 animationDrawable.start(); 218 } else { 219 animationDrawable.stop(); 220 } 221 } 222 } 223 } 224 225 public void onNotificationUpdated(NotificationData.Entry entry) { 226 mEntry = entry; 227 mStatusBarNotification = entry.notification; 228 mPrivateLayout.onNotificationUpdated(entry); 229 mPublicLayout.onNotificationUpdated(entry); 230 mShowingPublicInitialized = false; 231 updateNotificationColor(); 232 updateClearability(); 233 if (mIsSummaryWithChildren) { 234 recreateNotificationHeader(); 235 mChildrenContainer.onNotificationUpdated(); 236 } 237 if (mIconAnimationRunning) { 238 setIconAnimationRunning(true); 239 } 240 if (mNotificationParent != null) { 241 mNotificationParent.updateChildrenHeaderAppearance(); 242 } 243 onChildrenCountChanged(); 244 // The public layouts expand button is always visible 245 mPublicLayout.updateExpandButtons(true); 246 updateLimits(); 247 } 248 249 private void updateLimits() { 250 updateLimitsForView(mPrivateLayout); 251 updateLimitsForView(mPublicLayout); 252 } 253 254 private void updateLimitsForView(NotificationContentView layout) { 255 boolean customView = layout.getContractedChild().getId() 256 != com.android.internal.R.id.status_bar_latest_event_content; 257 boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N; 258 int minHeight = customView && beforeN && !mIsSummaryWithChildren ? 259 mNotificationMinHeightLegacy : mNotificationMinHeight; 260 boolean headsUpCustom = layout.getHeadsUpChild() != null && 261 layout.getHeadsUpChild().getId() 262 != com.android.internal.R.id.status_bar_latest_event_content; 263 int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy 264 : mMaxHeadsUpHeight; 265 layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight); 266 } 267 268 public StatusBarNotification getStatusBarNotification() { 269 return mStatusBarNotification; 270 } 271 272 public boolean isHeadsUp() { 273 return mIsHeadsUp; 274 } 275 276 public void setHeadsUp(boolean isHeadsUp) { 277 int intrinsicBefore = getIntrinsicHeight(); 278 mIsHeadsUp = isHeadsUp; 279 mPrivateLayout.setHeadsUp(isHeadsUp); 280 if (intrinsicBefore != getIntrinsicHeight()) { 281 notifyHeightChanged(false /* needsAnimation */); 282 } 283 } 284 285 public void setGroupManager(NotificationGroupManager groupManager) { 286 mGroupManager = groupManager; 287 mPrivateLayout.setGroupManager(groupManager); 288 } 289 290 public void setRemoteInputController(RemoteInputController r) { 291 mPrivateLayout.setRemoteInputController(r); 292 } 293 294 public void setAppName(String appName) { 295 mAppName = appName; 296 if (mSettingsIconRow != null) { 297 mSettingsIconRow.setAppName(mAppName); 298 } 299 } 300 301 public void addChildNotification(ExpandableNotificationRow row) { 302 addChildNotification(row, -1); 303 } 304 305 /** 306 * Add a child notification to this view. 307 * 308 * @param row the row to add 309 * @param childIndex the index to add it at, if -1 it will be added at the end 310 */ 311 public void addChildNotification(ExpandableNotificationRow row, int childIndex) { 312 if (mChildrenContainer == null) { 313 mChildrenContainerStub.inflate(); 314 } 315 mChildrenContainer.addNotification(row, childIndex); 316 onChildrenCountChanged(); 317 row.setIsChildInGroup(true, this); 318 } 319 320 public void removeChildNotification(ExpandableNotificationRow row) { 321 if (mChildrenContainer != null) { 322 mChildrenContainer.removeNotification(row); 323 } 324 mHeaderUtil.restoreNotificationHeader(row); 325 onChildrenCountChanged(); 326 row.setIsChildInGroup(false, null); 327 } 328 329 public boolean isChildInGroup() { 330 return mNotificationParent != null; 331 } 332 333 public ExpandableNotificationRow getNotificationParent() { 334 return mNotificationParent; 335 } 336 337 /** 338 * @param isChildInGroup Is this notification now in a group 339 * @param parent the new parent notification 340 */ 341 public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {; 342 boolean childInGroup = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup; 343 mNotificationParent = childInGroup ? parent : null; 344 mPrivateLayout.setIsChildInGroup(childInGroup); 345 updateNoBackgroundState(); 346 } 347 348 @Override 349 public boolean onTouchEvent(MotionEvent event) { 350 if (event.getActionMasked() != MotionEvent.ACTION_DOWN 351 || !isChildInGroup() || isGroupExpanded()) { 352 return super.onTouchEvent(event); 353 } else { 354 return false; 355 } 356 } 357 358 @Override 359 protected boolean handleSlideBack() { 360 if (mSettingsIconRow != null && mSettingsIconRow.isVisible()) { 361 animateTranslateNotification(0 /* targetLeft */); 362 return true; 363 } 364 return false; 365 } 366 367 @Override 368 protected boolean shouldHideBackground() { 369 return super.shouldHideBackground() || mShowNoBackground; 370 } 371 372 @Override 373 public boolean isSummaryWithChildren() { 374 return mIsSummaryWithChildren; 375 } 376 377 @Override 378 public boolean areChildrenExpanded() { 379 return mChildrenExpanded; 380 } 381 382 public List<ExpandableNotificationRow> getNotificationChildren() { 383 return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren(); 384 } 385 386 public int getNumberOfNotificationChildren() { 387 if (mChildrenContainer == null) { 388 return 0; 389 } 390 return mChildrenContainer.getNotificationChildren().size(); 391 } 392 393 /** 394 * Apply the order given in the list to the children. 395 * 396 * @param childOrder the new list order 397 * @return whether the list order has changed 398 */ 399 public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder) { 400 return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder); 401 } 402 403 public void getChildrenStates(StackScrollState resultState) { 404 if (mIsSummaryWithChildren) { 405 StackViewState parentState = resultState.getViewStateForView(this); 406 mChildrenContainer.getState(resultState, parentState); 407 } 408 } 409 410 public void applyChildrenState(StackScrollState state) { 411 if (mIsSummaryWithChildren) { 412 mChildrenContainer.applyState(state); 413 } 414 } 415 416 public void prepareExpansionChanged(StackScrollState state) { 417 if (mIsSummaryWithChildren) { 418 mChildrenContainer.prepareExpansionChanged(state); 419 } 420 } 421 422 public void startChildAnimation(StackScrollState finalState, 423 StackStateAnimator stateAnimator, long delay, long duration) { 424 if (mIsSummaryWithChildren) { 425 mChildrenContainer.startAnimationToState(finalState, stateAnimator, delay, 426 duration); 427 } 428 } 429 430 public ExpandableNotificationRow getViewAtPosition(float y) { 431 if (!mIsSummaryWithChildren || !mChildrenExpanded) { 432 return this; 433 } else { 434 ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y); 435 return view == null ? this : view; 436 } 437 } 438 439 public NotificationGuts getGuts() { 440 return mGuts; 441 } 442 443 /** 444 * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this 445 * the notification will be rendered on top of the screen. 446 * 447 * @param pinned whether it is pinned 448 */ 449 public void setPinned(boolean pinned) { 450 mIsPinned = pinned; 451 if (pinned) { 452 setIconAnimationRunning(true); 453 mExpandedWhenPinned = false; 454 } else if (mExpandedWhenPinned) { 455 setUserExpanded(true); 456 } 457 setChronometerRunning(mLastChronometerRunning); 458 } 459 460 public boolean isPinned() { 461 return mIsPinned; 462 } 463 464 /** 465 * @param atLeastMinHeight should the value returned be at least the minimum height. 466 * Used to avoid cyclic calls 467 * @return the height of the heads up notification when pinned 468 */ 469 public int getPinnedHeadsUpHeight(boolean atLeastMinHeight) { 470 if (mIsSummaryWithChildren) { 471 return mChildrenContainer.getIntrinsicHeight(); 472 } 473 if(mExpandedWhenPinned) { 474 return Math.max(getMaxExpandHeight(), mHeadsUpHeight); 475 } else if (atLeastMinHeight) { 476 return Math.max(getCollapsedHeight(), mHeadsUpHeight); 477 } else { 478 return mHeadsUpHeight; 479 } 480 } 481 482 /** 483 * Mark whether this notification was just clicked, i.e. the user has just clicked this 484 * notification in this frame. 485 */ 486 public void setJustClicked(boolean justClicked) { 487 mJustClicked = justClicked; 488 } 489 490 /** 491 * @return true if this notification has been clicked in this frame, false otherwise 492 */ 493 public boolean wasJustClicked() { 494 return mJustClicked; 495 } 496 497 public void setChronometerRunning(boolean running) { 498 mLastChronometerRunning = running; 499 setChronometerRunning(running, mPrivateLayout); 500 setChronometerRunning(running, mPublicLayout); 501 if (mChildrenContainer != null) { 502 List<ExpandableNotificationRow> notificationChildren = 503 mChildrenContainer.getNotificationChildren(); 504 for (int i = 0; i < notificationChildren.size(); i++) { 505 ExpandableNotificationRow child = notificationChildren.get(i); 506 child.setChronometerRunning(running); 507 } 508 } 509 } 510 511 private void setChronometerRunning(boolean running, NotificationContentView layout) { 512 if (layout != null) { 513 running = running || isPinned(); 514 View contractedChild = layout.getContractedChild(); 515 View expandedChild = layout.getExpandedChild(); 516 View headsUpChild = layout.getHeadsUpChild(); 517 setChronometerRunningForChild(running, contractedChild); 518 setChronometerRunningForChild(running, expandedChild); 519 setChronometerRunningForChild(running, headsUpChild); 520 } 521 } 522 523 private void setChronometerRunningForChild(boolean running, View child) { 524 if (child != null) { 525 View chronometer = child.findViewById(com.android.internal.R.id.chronometer); 526 if (chronometer instanceof Chronometer) { 527 ((Chronometer) chronometer).setStarted(running); 528 } 529 } 530 } 531 532 public NotificationHeaderView getNotificationHeader() { 533 if (mNotificationHeader != null) { 534 return mNotificationHeader; 535 } 536 return mPrivateLayout.getNotificationHeader(); 537 } 538 539 private NotificationHeaderView getVisibleNotificationHeader() { 540 if (mNotificationHeader != null) { 541 return mNotificationHeader; 542 } 543 return getShowingLayout().getVisibleNotificationHeader(); 544 } 545 546 public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) { 547 mOnExpandClickListener = onExpandClickListener; 548 } 549 550 public void setHeadsUpManager(HeadsUpManager headsUpManager) { 551 mHeadsUpManager = headsUpManager; 552 } 553 554 public void reInflateViews() { 555 initDimens(); 556 if (mIsSummaryWithChildren) { 557 removeView(mNotificationHeader); 558 mNotificationHeader = null; 559 recreateNotificationHeader(); 560 if (mChildrenContainer != null) { 561 mChildrenContainer.reInflateViews(); 562 } 563 } 564 if (mGuts != null) { 565 View oldGuts = mGuts; 566 int index = indexOfChild(oldGuts); 567 removeView(oldGuts); 568 mGuts = (NotificationGuts) LayoutInflater.from(mContext).inflate( 569 R.layout.notification_guts, this, false); 570 mGuts.setVisibility(oldGuts.getVisibility()); 571 addView(mGuts, index); 572 } 573 if (mSettingsIconRow != null) { 574 View oldSettings = mSettingsIconRow; 575 int settingsIndex = indexOfChild(oldSettings); 576 removeView(oldSettings); 577 mSettingsIconRow = (NotificationSettingsIconRow) LayoutInflater.from(mContext).inflate( 578 R.layout.notification_settings_icon_row, this, false); 579 mSettingsIconRow.setNotificationRowParent(ExpandableNotificationRow.this); 580 mSettingsIconRow.setAppName(mAppName); 581 mSettingsIconRow.setVisibility(oldSettings.getVisibility()); 582 addView(mSettingsIconRow, settingsIndex); 583 584 } 585 mPrivateLayout.reInflateViews(); 586 mPublicLayout.reInflateViews(); 587 } 588 589 public void setContentBackground(int customBackgroundColor, boolean animate, 590 NotificationContentView notificationContentView) { 591 if (getShowingLayout() == notificationContentView) { 592 setTintColor(customBackgroundColor, animate); 593 } 594 } 595 596 public void closeRemoteInput() { 597 mPrivateLayout.closeRemoteInput(); 598 mPublicLayout.closeRemoteInput(); 599 } 600 601 /** 602 * Set by how much the single line view should be indented. 603 */ 604 public void setSingleLineWidthIndention(int indention) { 605 mPrivateLayout.setSingleLineWidthIndention(indention); 606 } 607 608 public int getNotificationColor() { 609 return mNotificationColor; 610 } 611 612 private void updateNotificationColor() { 613 mNotificationColor = NotificationColorUtil.resolveContrastColor(mContext, 614 getStatusBarNotification().getNotification().color); 615 } 616 617 public HybridNotificationView getSingleLineView() { 618 return mPrivateLayout.getSingleLineView(); 619 } 620 621 public boolean isOnKeyguard() { 622 return mOnKeyguard; 623 } 624 625 public void removeAllChildren() { 626 List<ExpandableNotificationRow> notificationChildren 627 = mChildrenContainer.getNotificationChildren(); 628 ArrayList<ExpandableNotificationRow> clonedList = new ArrayList<>(notificationChildren); 629 for (int i = 0; i < clonedList.size(); i++) { 630 ExpandableNotificationRow row = clonedList.get(i); 631 mChildrenContainer.removeNotification(row); 632 mHeaderUtil.restoreNotificationHeader(row); 633 row.setIsChildInGroup(false, null); 634 } 635 onChildrenCountChanged(); 636 } 637 638 public interface ExpansionLogger { 639 public void logNotificationExpansion(String key, boolean userAction, boolean expanded); 640 } 641 642 public ExpandableNotificationRow(Context context, AttributeSet attrs) { 643 super(context, attrs); 644 mFalsingManager = FalsingManager.getInstance(context); 645 initDimens(); 646 } 647 648 private void initDimens() { 649 mNotificationMinHeightLegacy = getFontScaledHeight(R.dimen.notification_min_height_legacy); 650 mNotificationMinHeight = getFontScaledHeight(R.dimen.notification_min_height); 651 mNotificationMaxHeight = getFontScaledHeight(R.dimen.notification_max_height); 652 mMaxHeadsUpHeightLegacy = getFontScaledHeight( 653 R.dimen.notification_max_heads_up_height_legacy); 654 mMaxHeadsUpHeight = getFontScaledHeight(R.dimen.notification_max_heads_up_height); 655 } 656 657 /** 658 * @param dimenId the dimen to look up 659 * @return the font scaled dimen as if it were in sp but doesn't shrink sizes below dp 660 */ 661 private int getFontScaledHeight(int dimenId) { 662 int dimensionPixelSize = getResources().getDimensionPixelSize(dimenId); 663 float factor = Math.max(1.0f, getResources().getDisplayMetrics().scaledDensity / 664 getResources().getDisplayMetrics().density); 665 return (int) (dimensionPixelSize * factor); 666 } 667 668 /** 669 * Resets this view so it can be re-used for an updated notification. 670 */ 671 @Override 672 public void reset() { 673 super.reset(); 674 final boolean wasExpanded = isExpanded(); 675 mExpandable = false; 676 mHasUserChangedExpansion = false; 677 mUserLocked = false; 678 mShowingPublic = false; 679 mSensitive = false; 680 mShowingPublicInitialized = false; 681 mIsSystemExpanded = false; 682 mOnKeyguard = false; 683 mPublicLayout.reset(); 684 mPrivateLayout.reset(); 685 resetHeight(); 686 resetTranslation(); 687 logExpansionEvent(false, wasExpanded); 688 } 689 690 public void resetHeight() { 691 mMaxExpandHeight = 0; 692 mHeadsUpHeight = 0; 693 onHeightReset(); 694 requestLayout(); 695 } 696 697 @Override 698 protected void onFinishInflate() { 699 super.onFinishInflate(); 700 mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic); 701 mPublicLayout.setContainingNotification(this); 702 mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded); 703 mPrivateLayout.setExpandClickListener(mExpandClickListener); 704 mPrivateLayout.setContainingNotification(this); 705 mPublicLayout.setExpandClickListener(mExpandClickListener); 706 mSettingsIconRowStub = (ViewStub) findViewById(R.id.settings_icon_row_stub); 707 mSettingsIconRowStub.setOnInflateListener(new ViewStub.OnInflateListener() { 708 @Override 709 public void onInflate(ViewStub stub, View inflated) { 710 mSettingsIconRow = (NotificationSettingsIconRow) inflated; 711 mSettingsIconRow.setNotificationRowParent(ExpandableNotificationRow.this); 712 mSettingsIconRow.setAppName(mAppName); 713 } 714 }); 715 mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub); 716 mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() { 717 @Override 718 public void onInflate(ViewStub stub, View inflated) { 719 mGuts = (NotificationGuts) inflated; 720 mGuts.setClipTopAmount(getClipTopAmount()); 721 mGuts.setActualHeight(getActualHeight()); 722 mGutsStub = null; 723 } 724 }); 725 mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub); 726 mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() { 727 728 @Override 729 public void onInflate(ViewStub stub, View inflated) { 730 mChildrenContainer = (NotificationChildrenContainer) inflated; 731 mChildrenContainer.setNotificationParent(ExpandableNotificationRow.this); 732 mChildrenContainer.onNotificationUpdated(); 733 mTranslateableViews.add(mChildrenContainer); 734 } 735 }); 736 mVetoButton = findViewById(R.id.veto); 737 738 // Add the views that we translate to reveal the gear 739 mTranslateableViews = new ArrayList<View>(); 740 for (int i = 0; i < getChildCount(); i++) { 741 mTranslateableViews.add(getChildAt(i)); 742 } 743 // Remove views that don't translate 744 mTranslateableViews.remove(mVetoButton); 745 mTranslateableViews.remove(mSettingsIconRowStub); 746 mTranslateableViews.remove(mChildrenContainerStub); 747 mTranslateableViews.remove(mGutsStub); 748 } 749 750 private void setTranslationForOutline(float translationX) { 751 setOutlineRect(false, translationX, getTop(), getRight() + translationX, getBottom()); 752 } 753 754 public void resetTranslation() { 755 if (mTranslateableViews != null) { 756 for (int i = 0; i < mTranslateableViews.size(); i++) { 757 mTranslateableViews.get(i).setTranslationX(0); 758 } 759 setTranslationForOutline(0); 760 } 761 if (mSettingsIconRow != null) { 762 mSettingsIconRow.resetState(); 763 } 764 } 765 766 public void animateTranslateNotification(final float leftTarget) { 767 if (mTranslateAnim != null) { 768 mTranslateAnim.cancel(); 769 } 770 mTranslateAnim = (AnimatorSet) getTranslateViewAnimator(leftTarget, 771 null /* updateListener */); 772 if (mTranslateAnim != null) { 773 mTranslateAnim.start(); 774 } 775 } 776 777 @Override 778 public void setTranslation(float translationX) { 779 if (areGutsExposed()) { 780 // Don't translate if guts are showing. 781 return; 782 } 783 // Translate the group of views 784 for (int i = 0; i < mTranslateableViews.size(); i++) { 785 if (mTranslateableViews.get(i) != null) { 786 mTranslateableViews.get(i).setTranslationX(translationX); 787 } 788 } 789 setTranslationForOutline(translationX); 790 if (mSettingsIconRow != null) { 791 mSettingsIconRow.updateSettingsIcons(translationX, getMeasuredWidth()); 792 } 793 } 794 795 @Override 796 public float getTranslation() { 797 if (mTranslateableViews != null && mTranslateableViews.size() > 0) { 798 // All of the views in the list should have same translation, just use first one. 799 return mTranslateableViews.get(0).getTranslationX(); 800 } 801 return 0; 802 } 803 804 public Animator getTranslateViewAnimator(final float leftTarget, 805 AnimatorUpdateListener listener) { 806 if (mTranslateAnim != null) { 807 mTranslateAnim.cancel(); 808 } 809 if (areGutsExposed()) { 810 // No translation if guts are exposed. 811 return null; 812 } 813 AnimatorSet set = new AnimatorSet(); 814 if (mTranslateableViews != null) { 815 for (int i = 0; i < mTranslateableViews.size(); i++) { 816 final View animView = mTranslateableViews.get(i); 817 final ObjectAnimator translateAnim = ObjectAnimator.ofFloat( 818 animView, "translationX", leftTarget); 819 if (i == 0) { 820 translateAnim.addUpdateListener(new AnimatorUpdateListener() { 821 @Override 822 public void onAnimationUpdate(ValueAnimator animation) { 823 setTranslationForOutline((float) animation.getAnimatedValue()); 824 if (mSettingsIconRow != null) { 825 mSettingsIconRow.updateSettingsIcons( 826 (float) animation.getAnimatedValue(), getMeasuredWidth()); 827 } 828 } 829 }); 830 if (listener != null) { 831 translateAnim.addUpdateListener(listener); 832 } 833 translateAnim.addListener(new AnimatorListenerAdapter() { 834 boolean cancelled = false; 835 836 @Override 837 public void onAnimationCancel(Animator anim) { 838 cancelled = true; 839 } 840 841 @Override 842 public void onAnimationEnd(Animator anim) { 843 if (!cancelled && mSettingsIconRow != null && leftTarget == 0) { 844 mSettingsIconRow.resetState(); 845 mTranslateAnim = null; 846 } 847 } 848 }); 849 } 850 set.play(translateAnim); 851 } 852 } 853 mTranslateAnim = set; 854 return set; 855 } 856 857 public float getSpaceForGear() { 858 if (mSettingsIconRow != null) { 859 return mSettingsIconRow.getSpaceForGear(); 860 } 861 return 0; 862 } 863 864 public NotificationSettingsIconRow getSettingsRow() { 865 if (mSettingsIconRow == null) { 866 mSettingsIconRowStub.inflate(); 867 } 868 return mSettingsIconRow; 869 } 870 871 public void inflateGuts() { 872 if (mGuts == null) { 873 mGutsStub.inflate(); 874 } 875 } 876 877 private void updateChildrenVisibility() { 878 mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren ? VISIBLE 879 : INVISIBLE); 880 if (mChildrenContainer != null) { 881 mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE 882 : INVISIBLE); 883 } 884 if (mNotificationHeader != null) { 885 mNotificationHeader.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE 886 : INVISIBLE); 887 } 888 // The limits might have changed if the view suddenly became a group or vice versa 889 updateLimits(); 890 } 891 892 @Override 893 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) { 894 if (super.onRequestSendAccessibilityEventInternal(child, event)) { 895 // Add a record for the entire layout since its content is somehow small. 896 // The event comes from a leaf view that is interacted with. 897 AccessibilityEvent record = AccessibilityEvent.obtain(); 898 onInitializeAccessibilityEvent(record); 899 dispatchPopulateAccessibilityEvent(record); 900 event.appendRecord(record); 901 return true; 902 } 903 return false; 904 } 905 906 @Override 907 public void setDark(boolean dark, boolean fade, long delay) { 908 super.setDark(dark, fade, delay); 909 final NotificationContentView showing = getShowingLayout(); 910 if (showing != null) { 911 showing.setDark(dark, fade, delay); 912 } 913 if (mIsSummaryWithChildren) { 914 mChildrenContainer.setDark(dark, fade, delay); 915 mNotificationHeaderWrapper.setDark(dark, fade, delay); 916 } 917 } 918 919 public boolean isExpandable() { 920 if (mIsSummaryWithChildren && !mShowingPublic) { 921 return !mChildrenExpanded; 922 } 923 return mExpandable; 924 } 925 926 public void setExpandable(boolean expandable) { 927 mExpandable = expandable; 928 mPrivateLayout.updateExpandButtons(isExpandable()); 929 } 930 931 @Override 932 public void setClipToActualHeight(boolean clipToActualHeight) { 933 super.setClipToActualHeight(clipToActualHeight || isUserLocked()); 934 getShowingLayout().setClipToActualHeight(clipToActualHeight || isUserLocked()); 935 } 936 937 /** 938 * @return whether the user has changed the expansion state 939 */ 940 public boolean hasUserChangedExpansion() { 941 return mHasUserChangedExpansion; 942 } 943 944 public boolean isUserExpanded() { 945 return mUserExpanded; 946 } 947 948 /** 949 * Set this notification to be expanded by the user 950 * 951 * @param userExpanded whether the user wants this notification to be expanded 952 */ 953 public void setUserExpanded(boolean userExpanded) { 954 setUserExpanded(userExpanded, false /* allowChildExpansion */); 955 } 956 957 /** 958 * Set this notification to be expanded by the user 959 * 960 * @param userExpanded whether the user wants this notification to be expanded 961 * @param allowChildExpansion whether a call to this method allows expanding children 962 */ 963 public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) { 964 mFalsingManager.setNotificationExpanded(); 965 if (mIsSummaryWithChildren && !mShowingPublic && allowChildExpansion) { 966 mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded); 967 return; 968 } 969 if (userExpanded && !mExpandable) return; 970 final boolean wasExpanded = isExpanded(); 971 mHasUserChangedExpansion = true; 972 mUserExpanded = userExpanded; 973 logExpansionEvent(true, wasExpanded); 974 } 975 976 public void resetUserExpansion() { 977 mHasUserChangedExpansion = false; 978 mUserExpanded = false; 979 } 980 981 public boolean isUserLocked() { 982 return mUserLocked; 983 } 984 985 public void setUserLocked(boolean userLocked) { 986 mUserLocked = userLocked; 987 mPrivateLayout.setUserExpanding(userLocked); 988 if (mIsSummaryWithChildren) { 989 mChildrenContainer.setUserLocked(userLocked); 990 } 991 } 992 993 /** 994 * @return has the system set this notification to be expanded 995 */ 996 public boolean isSystemExpanded() { 997 return mIsSystemExpanded; 998 } 999 1000 /** 1001 * Set this notification to be expanded by the system. 1002 * 1003 * @param expand whether the system wants this notification to be expanded. 1004 */ 1005 public void setSystemExpanded(boolean expand) { 1006 if (expand != mIsSystemExpanded) { 1007 final boolean wasExpanded = isExpanded(); 1008 mIsSystemExpanded = expand; 1009 notifyHeightChanged(false /* needsAnimation */); 1010 logExpansionEvent(false, wasExpanded); 1011 if (mChildrenContainer != null) { 1012 mChildrenContainer.updateGroupOverflow(); 1013 } 1014 } 1015 } 1016 1017 /** 1018 * @param onKeyguard whether to prevent notification expansion 1019 */ 1020 public void setOnKeyguard(boolean onKeyguard) { 1021 if (onKeyguard != mOnKeyguard) { 1022 final boolean wasExpanded = isExpanded(); 1023 mOnKeyguard = onKeyguard; 1024 logExpansionEvent(false, wasExpanded); 1025 if (wasExpanded != isExpanded()) { 1026 if (mIsSummaryWithChildren) { 1027 mChildrenContainer.updateGroupOverflow(); 1028 } 1029 notifyHeightChanged(false /* needsAnimation */); 1030 } 1031 } 1032 } 1033 1034 /** 1035 * @return Can the underlying notification be cleared? 1036 */ 1037 public boolean isClearable() { 1038 return mStatusBarNotification != null && mStatusBarNotification.isClearable(); 1039 } 1040 1041 @Override 1042 public int getIntrinsicHeight() { 1043 if (isUserLocked()) { 1044 return getActualHeight(); 1045 } 1046 if (mGuts != null && mGuts.areGutsExposed()) { 1047 return mGuts.getHeight(); 1048 } else if ((isChildInGroup() && !isGroupExpanded())) { 1049 return mPrivateLayout.getMinHeight(); 1050 } else if (mSensitive && mHideSensitiveForIntrinsicHeight) { 1051 return getMinHeight(); 1052 } else if (mIsSummaryWithChildren && !mOnKeyguard) { 1053 return mChildrenContainer.getIntrinsicHeight(); 1054 } else if (mIsHeadsUp) { 1055 if (isPinned()) { 1056 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */); 1057 } else if (isExpanded()) { 1058 return Math.max(getMaxExpandHeight(), mHeadsUpHeight); 1059 } else { 1060 return Math.max(getCollapsedHeight(), mHeadsUpHeight); 1061 } 1062 } else if (isExpanded()) { 1063 return getMaxExpandHeight(); 1064 } else { 1065 return getCollapsedHeight(); 1066 } 1067 } 1068 1069 private boolean isGroupExpanded() { 1070 return mGroupManager.isGroupExpanded(mStatusBarNotification); 1071 } 1072 1073 /** 1074 * @return whether this view has a header on the top of the content 1075 */ 1076 private boolean hasNotificationHeader() { 1077 return mIsSummaryWithChildren; 1078 } 1079 1080 private void onChildrenCountChanged() { 1081 mIsSummaryWithChildren = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS 1082 && mChildrenContainer != null && mChildrenContainer.getChildCount() > 0; 1083 if (mIsSummaryWithChildren) { 1084 if (mNotificationHeader == null) { 1085 recreateNotificationHeader(); 1086 } 1087 } 1088 mPrivateLayout.updateExpandButtons(isExpandable()); 1089 updateChildrenHeaderAppearance(); 1090 updateChildrenVisibility(); 1091 } 1092 1093 /** 1094 * Check whether the view state is currently expanded. This is given by the system in {@link 1095 * #setSystemExpanded(boolean)} and can be overridden by user expansion or 1096 * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this 1097 * view can differ from this state, if layout params are modified from outside. 1098 * 1099 * @return whether the view state is currently expanded. 1100 */ 1101 public boolean isExpanded() { 1102 return isExpanded(false /* allowOnKeyguard */); 1103 } 1104 1105 public boolean isExpanded(boolean allowOnKeyguard) { 1106 return (!mOnKeyguard || allowOnKeyguard) 1107 && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded()) 1108 || isUserExpanded()); 1109 } 1110 1111 private boolean isSystemChildExpanded() { 1112 return mIsSystemChildExpanded; 1113 } 1114 1115 public void setSystemChildExpanded(boolean expanded) { 1116 mIsSystemChildExpanded = expanded; 1117 } 1118 1119 @Override 1120 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 1121 super.onLayout(changed, left, top, right, bottom); 1122 updateMaxHeights(); 1123 } 1124 1125 private void updateMaxHeights() { 1126 int intrinsicBefore = getIntrinsicHeight(); 1127 View expandedChild = mPrivateLayout.getExpandedChild(); 1128 if (expandedChild == null) { 1129 expandedChild = mPrivateLayout.getContractedChild(); 1130 } 1131 mMaxExpandHeight = expandedChild.getHeight(); 1132 View headsUpChild = mPrivateLayout.getHeadsUpChild(); 1133 if (headsUpChild == null) { 1134 headsUpChild = mPrivateLayout.getContractedChild(); 1135 } 1136 mHeadsUpHeight = headsUpChild.getHeight(); 1137 if (intrinsicBefore != getIntrinsicHeight()) { 1138 notifyHeightChanged(false /* needsAnimation */); 1139 } 1140 } 1141 1142 @Override 1143 public void notifyHeightChanged(boolean needsAnimation) { 1144 super.notifyHeightChanged(needsAnimation); 1145 getShowingLayout().requestSelectLayout(needsAnimation || isUserLocked()); 1146 } 1147 1148 public void setSensitive(boolean sensitive, boolean hideSensitive) { 1149 mSensitive = sensitive; 1150 mSensitiveHiddenInGeneral = hideSensitive; 1151 } 1152 1153 public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) { 1154 mHideSensitiveForIntrinsicHeight = hideSensitive; 1155 if (mIsSummaryWithChildren) { 1156 List<ExpandableNotificationRow> notificationChildren = 1157 mChildrenContainer.getNotificationChildren(); 1158 for (int i = 0; i < notificationChildren.size(); i++) { 1159 ExpandableNotificationRow child = notificationChildren.get(i); 1160 child.setHideSensitiveForIntrinsicHeight(hideSensitive); 1161 } 1162 } 1163 } 1164 1165 public void setHideSensitive(boolean hideSensitive, boolean animated, long delay, 1166 long duration) { 1167 boolean oldShowingPublic = mShowingPublic; 1168 mShowingPublic = mSensitive && hideSensitive; 1169 if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) { 1170 return; 1171 } 1172 1173 // bail out if no public version 1174 if (mPublicLayout.getChildCount() == 0) return; 1175 1176 if (!animated) { 1177 mPublicLayout.animate().cancel(); 1178 mPrivateLayout.animate().cancel(); 1179 mPublicLayout.setAlpha(1f); 1180 mPrivateLayout.setAlpha(1f); 1181 mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE); 1182 updateChildrenVisibility(); 1183 } else { 1184 animateShowingPublic(delay, duration); 1185 } 1186 NotificationContentView showingLayout = getShowingLayout(); 1187 showingLayout.updateBackgroundColor(animated); 1188 mPrivateLayout.updateExpandButtons(isExpandable()); 1189 updateClearability(); 1190 mShowingPublicInitialized = true; 1191 } 1192 1193 private void animateShowingPublic(long delay, long duration) { 1194 View[] privateViews = mIsSummaryWithChildren ? 1195 new View[] {mChildrenContainer, mNotificationHeader} 1196 : new View[] {mPrivateLayout}; 1197 View[] publicViews = new View[] {mPublicLayout}; 1198 View[] hiddenChildren = mShowingPublic ? privateViews : publicViews; 1199 View[] shownChildren = mShowingPublic ? publicViews : privateViews; 1200 for (final View hiddenView : hiddenChildren) { 1201 hiddenView.setVisibility(View.VISIBLE); 1202 hiddenView.animate().cancel(); 1203 hiddenView.animate() 1204 .alpha(0f) 1205 .setStartDelay(delay) 1206 .setDuration(duration) 1207 .withEndAction(new Runnable() { 1208 @Override 1209 public void run() { 1210 hiddenView.setVisibility(View.INVISIBLE); 1211 } 1212 }); 1213 } 1214 for (View showView : shownChildren) { 1215 showView.setVisibility(View.VISIBLE); 1216 showView.setAlpha(0f); 1217 showView.animate().cancel(); 1218 showView.animate() 1219 .alpha(1f) 1220 .setStartDelay(delay) 1221 .setDuration(duration); 1222 } 1223 } 1224 1225 public boolean mustStayOnScreen() { 1226 return mIsHeadsUp; 1227 } 1228 1229 private void updateClearability() { 1230 // public versions cannot be dismissed 1231 mVetoButton.setVisibility(isClearable() && (!mShowingPublic 1232 || !mSensitiveHiddenInGeneral) ? View.VISIBLE : View.GONE); 1233 } 1234 1235 public void setChildrenExpanded(boolean expanded, boolean animate) { 1236 mChildrenExpanded = expanded; 1237 if (mNotificationHeader != null) { 1238 mNotificationHeader.setExpanded(expanded); 1239 } 1240 if (mChildrenContainer != null) { 1241 mChildrenContainer.setChildrenExpanded(expanded); 1242 } 1243 } 1244 1245 public static void applyTint(View v, int color) { 1246 int alpha; 1247 if (color != 0) { 1248 alpha = COLORED_DIVIDER_ALPHA; 1249 } else { 1250 color = 0xff000000; 1251 alpha = DEFAULT_DIVIDER_ALPHA; 1252 } 1253 if (v.getBackground() instanceof ColorDrawable) { 1254 ColorDrawable background = (ColorDrawable) v.getBackground(); 1255 background.mutate(); 1256 background.setColor(color); 1257 background.setAlpha(alpha); 1258 } 1259 } 1260 1261 public int getMaxExpandHeight() { 1262 return mMaxExpandHeight; 1263 } 1264 1265 public boolean areGutsExposed() { 1266 return (mGuts != null && mGuts.areGutsExposed()); 1267 } 1268 1269 @Override 1270 public boolean isContentExpandable() { 1271 NotificationContentView showingLayout = getShowingLayout(); 1272 return showingLayout.isContentExpandable(); 1273 } 1274 1275 @Override 1276 protected View getContentView() { 1277 return getShowingLayout(); 1278 } 1279 1280 @Override 1281 public void setActualHeight(int height, boolean notifyListeners) { 1282 super.setActualHeight(height, notifyListeners); 1283 if (mGuts != null && mGuts.areGutsExposed()) { 1284 mGuts.setActualHeight(height); 1285 return; 1286 } 1287 int contentHeight = Math.max(getMinHeight(), height); 1288 mPrivateLayout.setContentHeight(contentHeight); 1289 mPublicLayout.setContentHeight(contentHeight); 1290 if (mIsSummaryWithChildren) { 1291 mChildrenContainer.setActualHeight(height); 1292 } 1293 if (mGuts != null) { 1294 mGuts.setActualHeight(height); 1295 } 1296 } 1297 1298 @Override 1299 public int getMaxContentHeight() { 1300 if (mIsSummaryWithChildren && !mShowingPublic) { 1301 return mChildrenContainer.getMaxContentHeight(); 1302 } 1303 NotificationContentView showingLayout = getShowingLayout(); 1304 return showingLayout.getMaxHeight(); 1305 } 1306 1307 @Override 1308 public int getMinHeight() { 1309 if (mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) { 1310 return getPinnedHeadsUpHeight(false /* atLeastMinHeight */); 1311 } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) { 1312 return mChildrenContainer.getMinHeight(); 1313 } else if (mIsHeadsUp) { 1314 return mHeadsUpHeight; 1315 } 1316 NotificationContentView showingLayout = getShowingLayout(); 1317 return showingLayout.getMinHeight(); 1318 } 1319 1320 @Override 1321 public int getCollapsedHeight() { 1322 if (mIsSummaryWithChildren && !mShowingPublic) { 1323 return mChildrenContainer.getCollapsedHeight(); 1324 } 1325 return getMinHeight(); 1326 } 1327 1328 @Override 1329 public void setClipTopAmount(int clipTopAmount) { 1330 super.setClipTopAmount(clipTopAmount); 1331 mPrivateLayout.setClipTopAmount(clipTopAmount); 1332 mPublicLayout.setClipTopAmount(clipTopAmount); 1333 if (mGuts != null) { 1334 mGuts.setClipTopAmount(clipTopAmount); 1335 } 1336 } 1337 1338 private void recreateNotificationHeader() { 1339 final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(), 1340 getStatusBarNotification().getNotification()); 1341 final RemoteViews header = builder.makeNotificationHeader(); 1342 if (mNotificationHeader == null) { 1343 mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this); 1344 final View expandButton = mNotificationHeader.findViewById( 1345 com.android.internal.R.id.expand_button); 1346 expandButton.setVisibility(VISIBLE); 1347 mNotificationHeader.setOnClickListener(mExpandClickListener); 1348 mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(), 1349 mNotificationHeader); 1350 addView(mNotificationHeader, indexOfChild(mChildrenContainer) + 1); 1351 mTranslateableViews.add(mNotificationHeader); 1352 } else { 1353 header.reapply(getContext(), mNotificationHeader); 1354 mNotificationHeaderWrapper.notifyContentUpdated(mEntry.notification); 1355 } 1356 updateChildrenHeaderAppearance(); 1357 } 1358 1359 public void updateChildrenHeaderAppearance() { 1360 if (mIsSummaryWithChildren) { 1361 mHeaderUtil.updateChildrenHeaderAppearance(); 1362 } 1363 } 1364 1365 public boolean isMaxExpandHeightInitialized() { 1366 return mMaxExpandHeight != 0; 1367 } 1368 1369 public NotificationContentView getShowingLayout() { 1370 return mShowingPublic ? mPublicLayout : mPrivateLayout; 1371 } 1372 1373 @Override 1374 public void setShowingLegacyBackground(boolean showing) { 1375 super.setShowingLegacyBackground(showing); 1376 mPrivateLayout.setShowingLegacyBackground(showing); 1377 mPublicLayout.setShowingLegacyBackground(showing); 1378 } 1379 1380 @Override 1381 protected void updateBackgroundTint() { 1382 super.updateBackgroundTint(); 1383 updateNoBackgroundState(); 1384 if (mIsSummaryWithChildren) { 1385 List<ExpandableNotificationRow> notificationChildren = 1386 mChildrenContainer.getNotificationChildren(); 1387 for (int i = 0; i < notificationChildren.size(); i++) { 1388 ExpandableNotificationRow child = notificationChildren.get(i); 1389 child.updateNoBackgroundState(); 1390 } 1391 } 1392 } 1393 1394 private void updateNoBackgroundState() { 1395 mShowNoBackground = isChildInGroup() && hasSameBgColor(mNotificationParent); 1396 updateBackground(); 1397 } 1398 1399 public void setExpansionLogger(ExpansionLogger logger, String key) { 1400 mLogger = logger; 1401 mLoggingKey = key; 1402 } 1403 1404 @Override 1405 public float getIncreasedPaddingAmount() { 1406 if (mIsSummaryWithChildren) { 1407 if (isGroupExpanded()) { 1408 return 1.0f; 1409 } else if (isUserLocked()) { 1410 return mChildrenContainer.getGroupExpandFraction(); 1411 } 1412 } 1413 return 0.0f; 1414 } 1415 1416 @Override 1417 protected boolean disallowSingleClick(MotionEvent event) { 1418 float x = event.getX(); 1419 float y = event.getY(); 1420 NotificationHeaderView header = getVisibleNotificationHeader(); 1421 if (header != null) { 1422 return header.isInTouchRect(x - getTranslation(), y); 1423 } 1424 return super.disallowSingleClick(event); 1425 } 1426 1427 private void logExpansionEvent(boolean userAction, boolean wasExpanded) { 1428 final boolean nowExpanded = isExpanded(); 1429 if (wasExpanded != nowExpanded && mLogger != null) { 1430 mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded) ; 1431 } 1432 } 1433 1434 public interface OnExpandClickListener { 1435 void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded); 1436 } 1437} 1438