Home | History | Annotate | Download | only in statusbar
      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 
     17 package com.android.systemui.statusbar;
     18 
     19 import android.app.Notification;
     20 import android.app.PendingIntent;
     21 import android.app.RemoteInput;
     22 import android.content.Context;
     23 import android.graphics.Rect;
     24 import android.os.Build;
     25 import android.service.notification.StatusBarNotification;
     26 import android.util.AttributeSet;
     27 import android.view.NotificationHeaderView;
     28 import android.view.View;
     29 import android.view.ViewGroup;
     30 import android.view.ViewTreeObserver;
     31 import android.widget.FrameLayout;
     32 import android.widget.ImageView;
     33 
     34 import com.android.internal.util.NotificationColorUtil;
     35 import com.android.systemui.R;
     36 import com.android.systemui.statusbar.notification.HybridNotificationView;
     37 import com.android.systemui.statusbar.notification.HybridGroupManager;
     38 import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
     39 import com.android.systemui.statusbar.notification.NotificationUtils;
     40 import com.android.systemui.statusbar.notification.NotificationViewWrapper;
     41 import com.android.systemui.statusbar.phone.NotificationGroupManager;
     42 import com.android.systemui.statusbar.policy.RemoteInputView;
     43 
     44 /**
     45  * A frame layout containing the actual payload of the notification, including the contracted,
     46  * expanded and heads up layout. This class is responsible for clipping the content and and
     47  * switching between the expanded, contracted and the heads up view depending on its clipped size.
     48  */
     49 public class NotificationContentView extends FrameLayout {
     50 
     51     private static final int VISIBLE_TYPE_CONTRACTED = 0;
     52     private static final int VISIBLE_TYPE_EXPANDED = 1;
     53     private static final int VISIBLE_TYPE_HEADSUP = 2;
     54     private static final int VISIBLE_TYPE_SINGLELINE = 3;
     55     public static final int UNDEFINED = -1;
     56 
     57     private final Rect mClipBounds = new Rect();
     58     private final int mMinContractedHeight;
     59     private final int mNotificationContentMarginEnd;
     60 
     61     private View mContractedChild;
     62     private View mExpandedChild;
     63     private View mHeadsUpChild;
     64     private HybridNotificationView mSingleLineView;
     65 
     66     private RemoteInputView mExpandedRemoteInput;
     67     private RemoteInputView mHeadsUpRemoteInput;
     68 
     69     private NotificationViewWrapper mContractedWrapper;
     70     private NotificationViewWrapper mExpandedWrapper;
     71     private NotificationViewWrapper mHeadsUpWrapper;
     72     private HybridGroupManager mHybridGroupManager;
     73     private int mClipTopAmount;
     74     private int mContentHeight;
     75     private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
     76     private boolean mDark;
     77     private boolean mAnimate;
     78     private boolean mIsHeadsUp;
     79     private boolean mShowingLegacyBackground;
     80     private boolean mIsChildInGroup;
     81     private int mSmallHeight;
     82     private int mHeadsUpHeight;
     83     private int mNotificationMaxHeight;
     84     private StatusBarNotification mStatusBarNotification;
     85     private NotificationGroupManager mGroupManager;
     86     private RemoteInputController mRemoteInputController;
     87 
     88     private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
     89             = new ViewTreeObserver.OnPreDrawListener() {
     90         @Override
     91         public boolean onPreDraw() {
     92             // We need to post since we don't want the notification to animate on the very first
     93             // frame
     94             post(new Runnable() {
     95                 @Override
     96                 public void run() {
     97                     mAnimate = true;
     98                 }
     99             });
    100             getViewTreeObserver().removeOnPreDrawListener(this);
    101             return true;
    102         }
    103     };
    104 
    105     private OnClickListener mExpandClickListener;
    106     private boolean mBeforeN;
    107     private boolean mExpandable;
    108     private boolean mClipToActualHeight = true;
    109     private ExpandableNotificationRow mContainingNotification;
    110     /** The visible type at the start of a touch driven transformation */
    111     private int mTransformationStartVisibleType;
    112     /** The visible type at the start of an animation driven transformation */
    113     private int mAnimationStartVisibleType = UNDEFINED;
    114     private boolean mUserExpanding;
    115     private int mSingleLineWidthIndention;
    116     private boolean mForceSelectNextLayout = true;
    117     private PendingIntent mPreviousExpandedRemoteInputIntent;
    118     private PendingIntent mPreviousHeadsUpRemoteInputIntent;
    119 
    120     private int mContentHeightAtAnimationStart = UNDEFINED;
    121     private boolean mFocusOnVisibilityChange;
    122     private boolean mHeadsupDisappearRunning;
    123 
    124 
    125     public NotificationContentView(Context context, AttributeSet attrs) {
    126         super(context, attrs);
    127         mHybridGroupManager = new HybridGroupManager(getContext(), this);
    128         mMinContractedHeight = getResources().getDimensionPixelSize(
    129                 R.dimen.min_notification_layout_height);
    130         mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
    131                 com.android.internal.R.dimen.notification_content_margin_end);
    132         reset();
    133     }
    134 
    135     public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
    136         mSmallHeight = smallHeight;
    137         mHeadsUpHeight = headsUpMaxHeight;
    138         mNotificationMaxHeight = maxHeight;
    139     }
    140 
    141     @Override
    142     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    143         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    144         boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
    145         boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
    146         int maxSize = Integer.MAX_VALUE;
    147         int width = MeasureSpec.getSize(widthMeasureSpec);
    148         if (hasFixedHeight || isHeightLimited) {
    149             maxSize = MeasureSpec.getSize(heightMeasureSpec);
    150         }
    151         int maxChildHeight = 0;
    152         if (mExpandedChild != null) {
    153             int size = Math.min(maxSize, mNotificationMaxHeight);
    154             ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
    155             if (layoutParams.height >= 0) {
    156                 // An actual height is set
    157                 size = Math.min(maxSize, layoutParams.height);
    158             }
    159             int spec = size == Integer.MAX_VALUE
    160                     ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
    161                     : MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
    162             mExpandedChild.measure(widthMeasureSpec, spec);
    163             maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
    164         }
    165         if (mContractedChild != null) {
    166             int heightSpec;
    167             int size = Math.min(maxSize, mSmallHeight);
    168             if (shouldContractedBeFixedSize()) {
    169                 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
    170             } else {
    171                 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
    172             }
    173             mContractedChild.measure(widthMeasureSpec, heightSpec);
    174             int measuredHeight = mContractedChild.getMeasuredHeight();
    175             if (measuredHeight < mMinContractedHeight) {
    176                 heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
    177                 mContractedChild.measure(widthMeasureSpec, heightSpec);
    178             }
    179             maxChildHeight = Math.max(maxChildHeight, measuredHeight);
    180             if (updateContractedHeaderWidth()) {
    181                 mContractedChild.measure(widthMeasureSpec, heightSpec);
    182             }
    183             if (mExpandedChild != null
    184                     && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
    185                 // the Expanded child is smaller then the collapsed. Let's remeasure it.
    186                 heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
    187                         MeasureSpec.EXACTLY);
    188                 mExpandedChild.measure(widthMeasureSpec, heightSpec);
    189             }
    190         }
    191         if (mHeadsUpChild != null) {
    192             int size = Math.min(maxSize, mHeadsUpHeight);
    193             ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
    194             if (layoutParams.height >= 0) {
    195                 // An actual height is set
    196                 size = Math.min(size, layoutParams.height);
    197             }
    198             mHeadsUpChild.measure(widthMeasureSpec,
    199                     MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
    200             maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
    201         }
    202         if (mSingleLineView != null) {
    203             int singleLineWidthSpec = widthMeasureSpec;
    204             if (mSingleLineWidthIndention != 0
    205                     && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
    206                 singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
    207                         width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
    208                         MeasureSpec.AT_MOST);
    209             }
    210             mSingleLineView.measure(singleLineWidthSpec,
    211                     MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
    212             maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
    213         }
    214         int ownHeight = Math.min(maxChildHeight, maxSize);
    215         setMeasuredDimension(width, ownHeight);
    216     }
    217 
    218     private boolean updateContractedHeaderWidth() {
    219         // We need to update the expanded and the collapsed header to have exactly the same with to
    220         // have the expand buttons laid out at the same location.
    221         NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
    222         if (contractedHeader != null) {
    223             if (mExpandedChild != null
    224                     && mExpandedWrapper.getNotificationHeader() != null) {
    225                 NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
    226                 int expandedSize = expandedHeader.getMeasuredWidth()
    227                         - expandedHeader.getPaddingEnd();
    228                 int collapsedSize = contractedHeader.getMeasuredWidth()
    229                         - expandedHeader.getPaddingEnd();
    230                 if (expandedSize != collapsedSize) {
    231                     int paddingEnd = contractedHeader.getMeasuredWidth() - expandedSize;
    232                     contractedHeader.setPadding(
    233                             contractedHeader.isLayoutRtl()
    234                                     ? paddingEnd
    235                                     : contractedHeader.getPaddingLeft(),
    236                             contractedHeader.getPaddingTop(),
    237                             contractedHeader.isLayoutRtl()
    238                                     ? contractedHeader.getPaddingLeft()
    239                                     : paddingEnd,
    240                             contractedHeader.getPaddingBottom());
    241                     contractedHeader.setShowWorkBadgeAtEnd(true);
    242                     return true;
    243                 }
    244             } else {
    245                 int paddingEnd = mNotificationContentMarginEnd;
    246                 if (contractedHeader.getPaddingEnd() != paddingEnd) {
    247                     contractedHeader.setPadding(
    248                             contractedHeader.isLayoutRtl()
    249                                     ? paddingEnd
    250                                     : contractedHeader.getPaddingLeft(),
    251                             contractedHeader.getPaddingTop(),
    252                             contractedHeader.isLayoutRtl()
    253                                     ? contractedHeader.getPaddingLeft()
    254                                     : paddingEnd,
    255                             contractedHeader.getPaddingBottom());
    256                     contractedHeader.setShowWorkBadgeAtEnd(false);
    257                     return true;
    258                 }
    259             }
    260         }
    261         return false;
    262     }
    263 
    264     private boolean shouldContractedBeFixedSize() {
    265         return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
    266     }
    267 
    268     @Override
    269     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    270         int previousHeight = 0;
    271         if (mExpandedChild != null) {
    272             previousHeight = mExpandedChild.getHeight();
    273         }
    274         super.onLayout(changed, left, top, right, bottom);
    275         if (previousHeight != 0 && mExpandedChild.getHeight() != previousHeight) {
    276             mContentHeightAtAnimationStart = previousHeight;
    277         }
    278         updateClipping();
    279         invalidateOutline();
    280         selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
    281         mForceSelectNextLayout = false;
    282         updateExpandButtons(mExpandable);
    283     }
    284 
    285     @Override
    286     protected void onAttachedToWindow() {
    287         super.onAttachedToWindow();
    288         updateVisibility();
    289     }
    290 
    291     public void reset() {
    292         if (mContractedChild != null) {
    293             mContractedChild.animate().cancel();
    294             removeView(mContractedChild);
    295         }
    296         mPreviousExpandedRemoteInputIntent = null;
    297         if (mExpandedRemoteInput != null) {
    298             mExpandedRemoteInput.onNotificationUpdateOrReset();
    299             if (mExpandedRemoteInput.isActive()) {
    300                 mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
    301             }
    302         }
    303         if (mExpandedChild != null) {
    304             mExpandedChild.animate().cancel();
    305             removeView(mExpandedChild);
    306             mExpandedRemoteInput = null;
    307         }
    308         mPreviousHeadsUpRemoteInputIntent = null;
    309         if (mHeadsUpRemoteInput != null) {
    310             mHeadsUpRemoteInput.onNotificationUpdateOrReset();
    311             if (mHeadsUpRemoteInput.isActive()) {
    312                 mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
    313             }
    314         }
    315         if (mHeadsUpChild != null) {
    316             mHeadsUpChild.animate().cancel();
    317             removeView(mHeadsUpChild);
    318             mHeadsUpRemoteInput = null;
    319         }
    320         mContractedChild = null;
    321         mExpandedChild = null;
    322         mHeadsUpChild = null;
    323     }
    324 
    325     public View getContractedChild() {
    326         return mContractedChild;
    327     }
    328 
    329     public View getExpandedChild() {
    330         return mExpandedChild;
    331     }
    332 
    333     public View getHeadsUpChild() {
    334         return mHeadsUpChild;
    335     }
    336 
    337     public void setContractedChild(View child) {
    338         if (mContractedChild != null) {
    339             mContractedChild.animate().cancel();
    340             removeView(mContractedChild);
    341         }
    342         addView(child);
    343         mContractedChild = child;
    344         mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
    345                 mContainingNotification);
    346         mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
    347     }
    348 
    349     public void setExpandedChild(View child) {
    350         if (mExpandedChild != null) {
    351             mExpandedChild.animate().cancel();
    352             removeView(mExpandedChild);
    353         }
    354         addView(child);
    355         mExpandedChild = child;
    356         mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
    357                 mContainingNotification);
    358     }
    359 
    360     public void setHeadsUpChild(View child) {
    361         if (mHeadsUpChild != null) {
    362             mHeadsUpChild.animate().cancel();
    363             removeView(mHeadsUpChild);
    364         }
    365         addView(child);
    366         mHeadsUpChild = child;
    367         mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
    368                 mContainingNotification);
    369     }
    370 
    371     @Override
    372     protected void onVisibilityChanged(View changedView, int visibility) {
    373         super.onVisibilityChanged(changedView, visibility);
    374         updateVisibility();
    375     }
    376 
    377     private void updateVisibility() {
    378         setVisible(isShown());
    379     }
    380 
    381     @Override
    382     protected void onDetachedFromWindow() {
    383         super.onDetachedFromWindow();
    384         getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
    385     }
    386 
    387     private void setVisible(final boolean isVisible) {
    388         if (isVisible) {
    389             // This call can happen multiple times, but removing only removes a single one.
    390             // We therefore need to remove the old one.
    391             getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
    392             // We only animate if we are drawn at least once, otherwise the view might animate when
    393             // it's shown the first time
    394             getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
    395         } else {
    396             getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
    397             mAnimate = false;
    398         }
    399     }
    400 
    401     private void focusExpandButtonIfNecessary() {
    402         if (mFocusOnVisibilityChange) {
    403             NotificationHeaderView header = getVisibleNotificationHeader();
    404             if (header != null) {
    405                 ImageView expandButton = header.getExpandButton();
    406                 if (expandButton != null) {
    407                     expandButton.requestAccessibilityFocus();
    408                 }
    409             }
    410             mFocusOnVisibilityChange = false;
    411         }
    412     }
    413 
    414     public void setContentHeight(int contentHeight) {
    415         mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());
    416         selectLayout(mAnimate /* animate */, false /* force */);
    417 
    418         int minHeightHint = getMinContentHeightHint();
    419 
    420         NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
    421         if (wrapper != null) {
    422             wrapper.setContentHeight(mContentHeight, minHeightHint);
    423         }
    424 
    425         wrapper = getVisibleWrapper(mTransformationStartVisibleType);
    426         if (wrapper != null) {
    427             wrapper.setContentHeight(mContentHeight, minHeightHint);
    428         }
    429 
    430         updateClipping();
    431         invalidateOutline();
    432     }
    433 
    434     /**
    435      * @return the minimum apparent height that the wrapper should allow for the purpose
    436      *         of aligning elements at the bottom edge. If this is larger than the content
    437      *         height, the notification is clipped instead of being further shrunk.
    438      */
    439     private int getMinContentHeightHint() {
    440         if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
    441             return mContext.getResources().getDimensionPixelSize(
    442                         com.android.internal.R.dimen.notification_action_list_height);
    443         }
    444 
    445         // Transition between heads-up & expanded, or pinned.
    446         if (mHeadsUpChild != null && mExpandedChild != null) {
    447             boolean transitioningBetweenHunAndExpanded =
    448                     isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
    449                     isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
    450             boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
    451                     && (mIsHeadsUp || mHeadsupDisappearRunning);
    452             if (transitioningBetweenHunAndExpanded || pinned) {
    453                 return Math.min(mHeadsUpChild.getHeight(), mExpandedChild.getHeight());
    454             }
    455         }
    456 
    457         // Size change of the expanded version
    458         if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
    459                 && mExpandedChild != null) {
    460             return Math.min(mContentHeightAtAnimationStart, mExpandedChild.getHeight());
    461         }
    462 
    463         int hint;
    464         if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
    465             hint = mHeadsUpChild.getHeight();
    466         } else if (mExpandedChild != null) {
    467             hint = mExpandedChild.getHeight();
    468         } else {
    469             hint = mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
    470                     com.android.internal.R.dimen.notification_action_list_height);
    471         }
    472 
    473         if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
    474             hint = Math.min(hint, mExpandedChild.getHeight());
    475         }
    476         return hint;
    477     }
    478 
    479     private boolean isTransitioningFromTo(int from, int to) {
    480         return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
    481                 && mVisibleType == to;
    482     }
    483 
    484     private boolean isVisibleOrTransitioning(int type) {
    485         return mVisibleType == type || mTransformationStartVisibleType == type
    486                 || mAnimationStartVisibleType == type;
    487     }
    488 
    489     private void updateContentTransformation() {
    490         int visibleType = calculateVisibleType();
    491         if (visibleType != mVisibleType) {
    492             // A new transformation starts
    493             mTransformationStartVisibleType = mVisibleType;
    494             final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
    495             final TransformableView hiddenView = getTransformableViewForVisibleType(
    496                     mTransformationStartVisibleType);
    497             shownView.transformFrom(hiddenView, 0.0f);
    498             getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
    499             hiddenView.transformTo(shownView, 0.0f);
    500             mVisibleType = visibleType;
    501             updateBackgroundColor(true /* animate */);
    502         }
    503         if (mForceSelectNextLayout) {
    504             forceUpdateVisibilities();
    505         }
    506         if (mTransformationStartVisibleType != UNDEFINED
    507                 && mVisibleType != mTransformationStartVisibleType
    508                 && getViewForVisibleType(mTransformationStartVisibleType) != null) {
    509             final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
    510             final TransformableView hiddenView = getTransformableViewForVisibleType(
    511                     mTransformationStartVisibleType);
    512             float transformationAmount = calculateTransformationAmount();
    513             shownView.transformFrom(hiddenView, transformationAmount);
    514             hiddenView.transformTo(shownView, transformationAmount);
    515             updateBackgroundTransformation(transformationAmount);
    516         } else {
    517             updateViewVisibilities(visibleType);
    518             updateBackgroundColor(false);
    519         }
    520     }
    521 
    522     private void updateBackgroundTransformation(float transformationAmount) {
    523         int endColor = getBackgroundColor(mVisibleType);
    524         int startColor = getBackgroundColor(mTransformationStartVisibleType);
    525         if (endColor != startColor) {
    526             if (startColor == 0) {
    527                 startColor = mContainingNotification.getBackgroundColorWithoutTint();
    528             }
    529             if (endColor == 0) {
    530                 endColor = mContainingNotification.getBackgroundColorWithoutTint();
    531             }
    532             endColor = NotificationUtils.interpolateColors(startColor, endColor,
    533                     transformationAmount);
    534         }
    535         mContainingNotification.updateBackgroundAlpha(transformationAmount);
    536         mContainingNotification.setContentBackground(endColor, false, this);
    537     }
    538 
    539     private float calculateTransformationAmount() {
    540         int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
    541         int endHeight = getViewForVisibleType(mVisibleType).getHeight();
    542         int progress = Math.abs(mContentHeight - startHeight);
    543         int totalDistance = Math.abs(endHeight - startHeight);
    544         float amount = (float) progress / (float) totalDistance;
    545         return Math.min(1.0f, amount);
    546     }
    547 
    548     public int getContentHeight() {
    549         return mContentHeight;
    550     }
    551 
    552     public int getMaxHeight() {
    553         if (mExpandedChild != null) {
    554             return mExpandedChild.getHeight();
    555         } else if (mIsHeadsUp && mHeadsUpChild != null) {
    556             return mHeadsUpChild.getHeight();
    557         }
    558         return mContractedChild.getHeight();
    559     }
    560 
    561     public int getMinHeight() {
    562         return getMinHeight(false /* likeGroupExpanded */);
    563     }
    564 
    565     public int getMinHeight(boolean likeGroupExpanded) {
    566         if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
    567             return mContractedChild.getHeight();
    568         } else {
    569             return mSingleLineView.getHeight();
    570         }
    571     }
    572 
    573     private boolean isGroupExpanded() {
    574         return mGroupManager.isGroupExpanded(mStatusBarNotification);
    575     }
    576 
    577     public void setClipTopAmount(int clipTopAmount) {
    578         mClipTopAmount = clipTopAmount;
    579         updateClipping();
    580     }
    581 
    582     private void updateClipping() {
    583         if (mClipToActualHeight) {
    584             mClipBounds.set(0, mClipTopAmount, getWidth(), mContentHeight);
    585             setClipBounds(mClipBounds);
    586         } else {
    587             setClipBounds(null);
    588         }
    589     }
    590 
    591     public void setClipToActualHeight(boolean clipToActualHeight) {
    592         mClipToActualHeight = clipToActualHeight;
    593         updateClipping();
    594     }
    595 
    596     private void selectLayout(boolean animate, boolean force) {
    597         if (mContractedChild == null) {
    598             return;
    599         }
    600         if (mUserExpanding) {
    601             updateContentTransformation();
    602         } else {
    603             int visibleType = calculateVisibleType();
    604             boolean changedType = visibleType != mVisibleType;
    605             if (changedType || force) {
    606                 View visibleView = getViewForVisibleType(visibleType);
    607                 if (visibleView != null) {
    608                     visibleView.setVisibility(VISIBLE);
    609                     transferRemoteInputFocus(visibleType);
    610                 }
    611                 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
    612                 if (visibleWrapper != null) {
    613                     visibleWrapper.setContentHeight(mContentHeight, getMinContentHeightHint());
    614                 }
    615 
    616                 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
    617                         || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
    618                         || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
    619                         || visibleType == VISIBLE_TYPE_CONTRACTED)) {
    620                     animateToVisibleType(visibleType);
    621                 } else {
    622                     updateViewVisibilities(visibleType);
    623                 }
    624                 mVisibleType = visibleType;
    625                 if (changedType) {
    626                     focusExpandButtonIfNecessary();
    627                 }
    628                 updateBackgroundColor(animate);
    629             }
    630         }
    631     }
    632 
    633     private void forceUpdateVisibilities() {
    634         boolean contractedVisible = mVisibleType == VISIBLE_TYPE_CONTRACTED
    635                 || mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED;
    636         boolean expandedVisible = mVisibleType == VISIBLE_TYPE_EXPANDED
    637                 || mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED;
    638         boolean headsUpVisible = mVisibleType == VISIBLE_TYPE_HEADSUP
    639                 || mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP;
    640         boolean singleLineVisible = mVisibleType == VISIBLE_TYPE_SINGLELINE
    641                 || mTransformationStartVisibleType == VISIBLE_TYPE_SINGLELINE;
    642         if (!contractedVisible) {
    643             mContractedChild.setVisibility(View.INVISIBLE);
    644         } else {
    645             mContractedWrapper.setVisible(true);
    646         }
    647         if (mExpandedChild != null) {
    648             if (!expandedVisible) {
    649                 mExpandedChild.setVisibility(View.INVISIBLE);
    650             } else {
    651                 mExpandedWrapper.setVisible(true);
    652             }
    653         }
    654         if (mHeadsUpChild != null) {
    655             if (!headsUpVisible) {
    656                 mHeadsUpChild.setVisibility(View.INVISIBLE);
    657             } else {
    658                 mHeadsUpWrapper.setVisible(true);
    659             }
    660         }
    661         if (mSingleLineView != null) {
    662             if (!singleLineVisible) {
    663                 mSingleLineView.setVisibility(View.INVISIBLE);
    664             } else {
    665                 mSingleLineView.setVisible(true);
    666             }
    667         }
    668     }
    669 
    670     public void updateBackgroundColor(boolean animate) {
    671         int customBackgroundColor = getBackgroundColor(mVisibleType);
    672         mContainingNotification.resetBackgroundAlpha();
    673         mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
    674     }
    675 
    676     public int getVisibleType() {
    677         return mVisibleType;
    678     }
    679 
    680     public int getBackgroundColorForExpansionState() {
    681         // When expanding or user locked we want the new type, when collapsing we want
    682         // the original type
    683         final int visibleType = (mContainingNotification.isGroupExpanded()
    684                 || mContainingNotification.isUserLocked())
    685                         ? calculateVisibleType()
    686                         : getVisibleType();
    687         return getBackgroundColor(visibleType);
    688     }
    689 
    690     public int getBackgroundColor(int visibleType) {
    691         NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
    692         int customBackgroundColor = 0;
    693         if (currentVisibleWrapper != null) {
    694             customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
    695         }
    696         return customBackgroundColor;
    697     }
    698 
    699     private void updateViewVisibilities(int visibleType) {
    700         boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED;
    701         mContractedWrapper.setVisible(contractedVisible);
    702         if (mExpandedChild != null) {
    703             boolean expandedVisible = visibleType == VISIBLE_TYPE_EXPANDED;
    704             mExpandedWrapper.setVisible(expandedVisible);
    705         }
    706         if (mHeadsUpChild != null) {
    707             boolean headsUpVisible = visibleType == VISIBLE_TYPE_HEADSUP;
    708             mHeadsUpWrapper.setVisible(headsUpVisible);
    709         }
    710         if (mSingleLineView != null) {
    711             boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
    712             mSingleLineView.setVisible(singleLineVisible);
    713         }
    714     }
    715 
    716     private void animateToVisibleType(int visibleType) {
    717         final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
    718         final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
    719         if (shownView == hiddenView || hiddenView == null) {
    720             shownView.setVisible(true);
    721             return;
    722         }
    723         mAnimationStartVisibleType = mVisibleType;
    724         shownView.transformFrom(hiddenView);
    725         getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
    726         hiddenView.transformTo(shownView, new Runnable() {
    727             @Override
    728             public void run() {
    729                 if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
    730                     hiddenView.setVisible(false);
    731                 }
    732                 mAnimationStartVisibleType = UNDEFINED;
    733             }
    734         });
    735     }
    736 
    737     private void transferRemoteInputFocus(int visibleType) {
    738         if (visibleType == VISIBLE_TYPE_HEADSUP
    739                 && mHeadsUpRemoteInput != null
    740                 && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
    741             mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
    742         }
    743         if (visibleType == VISIBLE_TYPE_EXPANDED
    744                 && mExpandedRemoteInput != null
    745                 && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
    746             mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
    747         }
    748     }
    749 
    750     /**
    751      * @param visibleType one of the static enum types in this view
    752      * @return the corresponding transformable view according to the given visible type
    753      */
    754     private TransformableView getTransformableViewForVisibleType(int visibleType) {
    755         switch (visibleType) {
    756             case VISIBLE_TYPE_EXPANDED:
    757                 return mExpandedWrapper;
    758             case VISIBLE_TYPE_HEADSUP:
    759                 return mHeadsUpWrapper;
    760             case VISIBLE_TYPE_SINGLELINE:
    761                 return mSingleLineView;
    762             default:
    763                 return mContractedWrapper;
    764         }
    765     }
    766 
    767     /**
    768      * @param visibleType one of the static enum types in this view
    769      * @return the corresponding view according to the given visible type
    770      */
    771     private View getViewForVisibleType(int visibleType) {
    772         switch (visibleType) {
    773             case VISIBLE_TYPE_EXPANDED:
    774                 return mExpandedChild;
    775             case VISIBLE_TYPE_HEADSUP:
    776                 return mHeadsUpChild;
    777             case VISIBLE_TYPE_SINGLELINE:
    778                 return mSingleLineView;
    779             default:
    780                 return mContractedChild;
    781         }
    782     }
    783 
    784     private NotificationViewWrapper getVisibleWrapper(int visibleType) {
    785         switch (visibleType) {
    786             case VISIBLE_TYPE_EXPANDED:
    787                 return mExpandedWrapper;
    788             case VISIBLE_TYPE_HEADSUP:
    789                 return mHeadsUpWrapper;
    790             case VISIBLE_TYPE_CONTRACTED:
    791                 return mContractedWrapper;
    792             default:
    793                 return null;
    794         }
    795     }
    796 
    797     /**
    798      * @return one of the static enum types in this view, calculated form the current state
    799      */
    800     public int calculateVisibleType() {
    801         if (mUserExpanding) {
    802             int height = !mIsChildInGroup || isGroupExpanded()
    803                     || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
    804                     ? mContainingNotification.getMaxContentHeight()
    805                     : mContainingNotification.getShowingLayout().getMinHeight();
    806             if (height == 0) {
    807                 height = mContentHeight;
    808             }
    809             int expandedVisualType = getVisualTypeForHeight(height);
    810             int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
    811                     ? VISIBLE_TYPE_SINGLELINE
    812                     : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
    813             return mTransformationStartVisibleType == collapsedVisualType
    814                     ? expandedVisualType
    815                     : collapsedVisualType;
    816         }
    817         int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
    818         int viewHeight = mContentHeight;
    819         if (intrinsicHeight != 0) {
    820             // the intrinsicHeight might be 0 because it was just reset.
    821             viewHeight = Math.min(mContentHeight, intrinsicHeight);
    822         }
    823         return getVisualTypeForHeight(viewHeight);
    824     }
    825 
    826     private int getVisualTypeForHeight(float viewHeight) {
    827         boolean noExpandedChild = mExpandedChild == null;
    828         if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
    829             return VISIBLE_TYPE_EXPANDED;
    830         }
    831         if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
    832             return VISIBLE_TYPE_SINGLELINE;
    833         }
    834 
    835         if ((mIsHeadsUp || mHeadsupDisappearRunning) && mHeadsUpChild != null) {
    836             if (viewHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
    837                 return VISIBLE_TYPE_HEADSUP;
    838             } else {
    839                 return VISIBLE_TYPE_EXPANDED;
    840             }
    841         } else {
    842             if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
    843                     && (!mIsChildInGroup || isGroupExpanded()
    844                             || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
    845                 return VISIBLE_TYPE_CONTRACTED;
    846             } else {
    847                 return VISIBLE_TYPE_EXPANDED;
    848             }
    849         }
    850     }
    851 
    852     public boolean isContentExpandable() {
    853         return mExpandedChild != null;
    854     }
    855 
    856     public void setDark(boolean dark, boolean fade, long delay) {
    857         if (mContractedChild == null) {
    858             return;
    859         }
    860         mDark = dark;
    861         if (mVisibleType == VISIBLE_TYPE_CONTRACTED || !dark) {
    862             mContractedWrapper.setDark(dark, fade, delay);
    863         }
    864         if (mVisibleType == VISIBLE_TYPE_EXPANDED || (mExpandedChild != null && !dark)) {
    865             mExpandedWrapper.setDark(dark, fade, delay);
    866         }
    867         if (mVisibleType == VISIBLE_TYPE_HEADSUP || (mHeadsUpChild != null && !dark)) {
    868             mHeadsUpWrapper.setDark(dark, fade, delay);
    869         }
    870         if (mSingleLineView != null && (mVisibleType == VISIBLE_TYPE_SINGLELINE || !dark)) {
    871             mSingleLineView.setDark(dark, fade, delay);
    872         }
    873     }
    874 
    875     public void setHeadsUp(boolean headsUp) {
    876         mIsHeadsUp = headsUp;
    877         selectLayout(false /* animate */, true /* force */);
    878         updateExpandButtons(mExpandable);
    879     }
    880 
    881     @Override
    882     public boolean hasOverlappingRendering() {
    883 
    884         // This is not really true, but good enough when fading from the contracted to the expanded
    885         // layout, and saves us some layers.
    886         return false;
    887     }
    888 
    889     public void setShowingLegacyBackground(boolean showing) {
    890         mShowingLegacyBackground = showing;
    891         updateShowingLegacyBackground();
    892     }
    893 
    894     private void updateShowingLegacyBackground() {
    895         if (mContractedChild != null) {
    896             mContractedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
    897         }
    898         if (mExpandedChild != null) {
    899             mExpandedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
    900         }
    901         if (mHeadsUpChild != null) {
    902             mHeadsUpWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
    903         }
    904     }
    905 
    906     public void setIsChildInGroup(boolean isChildInGroup) {
    907         mIsChildInGroup = isChildInGroup;
    908         updateSingleLineView();
    909     }
    910 
    911     public void onNotificationUpdated(NotificationData.Entry entry) {
    912         mStatusBarNotification = entry.notification;
    913         mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
    914         updateSingleLineView();
    915         applyRemoteInput(entry);
    916         if (mContractedChild != null) {
    917             mContractedWrapper.notifyContentUpdated(entry.notification);
    918         }
    919         if (mExpandedChild != null) {
    920             mExpandedWrapper.notifyContentUpdated(entry.notification);
    921         }
    922         if (mHeadsUpChild != null) {
    923             mHeadsUpWrapper.notifyContentUpdated(entry.notification);
    924         }
    925         updateShowingLegacyBackground();
    926         mForceSelectNextLayout = true;
    927         setDark(mDark, false /* animate */, 0 /* delay */);
    928         mPreviousExpandedRemoteInputIntent = null;
    929         mPreviousHeadsUpRemoteInputIntent = null;
    930     }
    931 
    932     private void updateSingleLineView() {
    933         if (mIsChildInGroup) {
    934             mSingleLineView = mHybridGroupManager.bindFromNotification(
    935                     mSingleLineView, mStatusBarNotification.getNotification());
    936         } else if (mSingleLineView != null) {
    937             removeView(mSingleLineView);
    938             mSingleLineView = null;
    939         }
    940     }
    941 
    942     private void applyRemoteInput(final NotificationData.Entry entry) {
    943         if (mRemoteInputController == null) {
    944             return;
    945         }
    946 
    947         boolean hasRemoteInput = false;
    948 
    949         Notification.Action[] actions = entry.notification.getNotification().actions;
    950         if (actions != null) {
    951             for (Notification.Action a : actions) {
    952                 if (a.getRemoteInputs() != null) {
    953                     for (RemoteInput ri : a.getRemoteInputs()) {
    954                         if (ri.getAllowFreeFormInput()) {
    955                             hasRemoteInput = true;
    956                             break;
    957                         }
    958                     }
    959                 }
    960             }
    961         }
    962 
    963         View bigContentView = mExpandedChild;
    964         if (bigContentView != null) {
    965             mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
    966                     mPreviousExpandedRemoteInputIntent);
    967         } else {
    968             mExpandedRemoteInput = null;
    969         }
    970 
    971         View headsUpContentView = mHeadsUpChild;
    972         if (headsUpContentView != null) {
    973             mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
    974                     mPreviousHeadsUpRemoteInputIntent);
    975         } else {
    976             mHeadsUpRemoteInput = null;
    977         }
    978     }
    979 
    980     private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
    981             boolean hasRemoteInput, PendingIntent existingPendingIntent) {
    982         View actionContainerCandidate = view.findViewById(
    983                 com.android.internal.R.id.actions_container);
    984         if (actionContainerCandidate instanceof FrameLayout) {
    985             RemoteInputView existing = (RemoteInputView)
    986                     view.findViewWithTag(RemoteInputView.VIEW_TAG);
    987 
    988             if (existing != null) {
    989                 existing.onNotificationUpdateOrReset();
    990             }
    991 
    992             if (existing == null && hasRemoteInput) {
    993                 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
    994                 RemoteInputView riv = RemoteInputView.inflate(
    995                         mContext, actionContainer, entry, mRemoteInputController);
    996 
    997                 riv.setVisibility(View.INVISIBLE);
    998                 actionContainer.addView(riv, new LayoutParams(
    999                         ViewGroup.LayoutParams.MATCH_PARENT,
   1000                         ViewGroup.LayoutParams.MATCH_PARENT)
   1001                 );
   1002                 existing = riv;
   1003             }
   1004             if (hasRemoteInput) {
   1005                 int color = entry.notification.getNotification().color;
   1006                 if (color == Notification.COLOR_DEFAULT) {
   1007                     color = mContext.getColor(R.color.default_remote_input_background);
   1008                 }
   1009                 existing.setBackgroundColor(NotificationColorUtil.ensureTextBackgroundColor(color,
   1010                         mContext.getColor(R.color.remote_input_text_enabled),
   1011                         mContext.getColor(R.color.remote_input_hint)));
   1012 
   1013                 if (existingPendingIntent != null || existing.isActive()) {
   1014                     // The current action could be gone, or the pending intent no longer valid.
   1015                     // If we find a matching action in the new notification, focus, otherwise close.
   1016                     Notification.Action[] actions = entry.notification.getNotification().actions;
   1017                     if (existingPendingIntent != null) {
   1018                         existing.setPendingIntent(existingPendingIntent);
   1019                     }
   1020                     if (existing.updatePendingIntentFromActions(actions)) {
   1021                         if (!existing.isActive()) {
   1022                             existing.focus();
   1023                         }
   1024                     } else {
   1025                         if (existing.isActive()) {
   1026                             existing.close();
   1027                         }
   1028                     }
   1029                 }
   1030             }
   1031             return existing;
   1032         }
   1033         return null;
   1034     }
   1035 
   1036     public void closeRemoteInput() {
   1037         if (mHeadsUpRemoteInput != null) {
   1038             mHeadsUpRemoteInput.close();
   1039         }
   1040         if (mExpandedRemoteInput != null) {
   1041             mExpandedRemoteInput.close();
   1042         }
   1043     }
   1044 
   1045     public void setGroupManager(NotificationGroupManager groupManager) {
   1046         mGroupManager = groupManager;
   1047     }
   1048 
   1049     public void setRemoteInputController(RemoteInputController r) {
   1050         mRemoteInputController = r;
   1051     }
   1052 
   1053     public void setExpandClickListener(OnClickListener expandClickListener) {
   1054         mExpandClickListener = expandClickListener;
   1055     }
   1056 
   1057     public void updateExpandButtons(boolean expandable) {
   1058         mExpandable = expandable;
   1059         // if the expanded child has the same height as the collapsed one we hide it.
   1060         if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
   1061             if ((!mIsHeadsUp || mHeadsUpChild == null)) {
   1062                 if (mExpandedChild.getHeight() == mContractedChild.getHeight()) {
   1063                     expandable = false;
   1064                 }
   1065             } else if (mExpandedChild.getHeight() == mHeadsUpChild.getHeight()) {
   1066                 expandable = false;
   1067             }
   1068         }
   1069         if (mExpandedChild != null) {
   1070             mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
   1071         }
   1072         if (mContractedChild != null) {
   1073             mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
   1074         }
   1075         if (mHeadsUpChild != null) {
   1076             mHeadsUpWrapper.updateExpandability(expandable,  mExpandClickListener);
   1077         }
   1078     }
   1079 
   1080     public NotificationHeaderView getNotificationHeader() {
   1081         NotificationHeaderView header = null;
   1082         if (mContractedChild != null) {
   1083             header = mContractedWrapper.getNotificationHeader();
   1084         }
   1085         if (header == null && mExpandedChild != null) {
   1086             header = mExpandedWrapper.getNotificationHeader();
   1087         }
   1088         if (header == null && mHeadsUpChild != null) {
   1089             header = mHeadsUpWrapper.getNotificationHeader();
   1090         }
   1091         return header;
   1092     }
   1093 
   1094     public NotificationHeaderView getVisibleNotificationHeader() {
   1095         NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
   1096         return wrapper == null ? null : wrapper.getNotificationHeader();
   1097     }
   1098 
   1099     public void setContainingNotification(ExpandableNotificationRow containingNotification) {
   1100         mContainingNotification = containingNotification;
   1101     }
   1102 
   1103     public void requestSelectLayout(boolean needsAnimation) {
   1104         selectLayout(needsAnimation, false);
   1105     }
   1106 
   1107     public void reInflateViews() {
   1108         if (mIsChildInGroup && mSingleLineView != null) {
   1109             removeView(mSingleLineView);
   1110             mSingleLineView = null;
   1111             updateSingleLineView();
   1112         }
   1113     }
   1114 
   1115     public void setUserExpanding(boolean userExpanding) {
   1116         mUserExpanding = userExpanding;
   1117         if (userExpanding) {
   1118             mTransformationStartVisibleType = mVisibleType;
   1119         } else {
   1120             mTransformationStartVisibleType = UNDEFINED;
   1121             mVisibleType = calculateVisibleType();
   1122             updateViewVisibilities(mVisibleType);
   1123             updateBackgroundColor(false);
   1124         }
   1125     }
   1126 
   1127     /**
   1128      * Set by how much the single line view should be indented. Used when a overflow indicator is
   1129      * present and only during measuring
   1130      */
   1131     public void setSingleLineWidthIndention(int singleLineWidthIndention) {
   1132         if (singleLineWidthIndention != mSingleLineWidthIndention) {
   1133             mSingleLineWidthIndention = singleLineWidthIndention;
   1134             mContainingNotification.forceLayout();
   1135             forceLayout();
   1136         }
   1137     }
   1138 
   1139     public HybridNotificationView getSingleLineView() {
   1140         return mSingleLineView;
   1141     }
   1142 
   1143     public void setRemoved() {
   1144         if (mExpandedRemoteInput != null) {
   1145             mExpandedRemoteInput.setRemoved();
   1146         }
   1147         if (mHeadsUpRemoteInput != null) {
   1148             mHeadsUpRemoteInput.setRemoved();
   1149         }
   1150     }
   1151 
   1152     public void setContentHeightAnimating(boolean animating) {
   1153         if (!animating) {
   1154             mContentHeightAtAnimationStart = UNDEFINED;
   1155         }
   1156     }
   1157 
   1158     public void setHeadsupDisappearRunning(boolean headsupDisappearRunning) {
   1159         mHeadsupDisappearRunning = headsupDisappearRunning;
   1160         selectLayout(false /* animate */, true /* force */);
   1161     }
   1162 
   1163     public void setFocusOnVisibilityChange() {
   1164         mFocusOnVisibilityChange = true;
   1165     }
   1166 }
   1167