NotificationChildrenContainer.java revision c897bd3e91ed442b0f32c3a5b9918e9e580b62b1
1/*
2 * Copyright (C) 2015 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.stack;
18
19import android.content.Context;
20import android.content.res.Configuration;
21import android.util.AttributeSet;
22import android.view.LayoutInflater;
23import android.view.View;
24import android.view.ViewGroup;
25import android.widget.TextView;
26
27import com.android.systemui.R;
28import com.android.systemui.ViewInvertHelper;
29import com.android.systemui.statusbar.CrossFadeHelper;
30import com.android.systemui.statusbar.ExpandableNotificationRow;
31import com.android.systemui.statusbar.notification.HybridGroupManager;
32import com.android.systemui.statusbar.notification.HybridNotificationView;
33import com.android.systemui.statusbar.notification.NotificationUtils;
34import com.android.systemui.statusbar.phone.NotificationPanelView;
35
36import java.util.ArrayList;
37import java.util.List;
38
39/**
40 * A container containing child notifications
41 */
42public class NotificationChildrenContainer extends ViewGroup {
43
44    private static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
45    private static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
46    private static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
47
48    private final List<View> mDividers = new ArrayList<>();
49    private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
50    private final HybridGroupManager mHybridGroupManager;
51    private int mChildPadding;
52    private int mDividerHeight;
53    private int mMaxNotificationHeight;
54    private int mNotificationHeaderHeight;
55    private int mNotificatonTopPadding;
56    private float mCollapsedBottompadding;
57    private ViewInvertHelper mOverflowInvertHelper;
58    private boolean mChildrenExpanded;
59    private ExpandableNotificationRow mNotificationParent;
60    private TextView mOverflowNumber;
61    private ViewState mGroupOverFlowState;
62    private int mRealHeight;
63    private boolean mUserLocked;
64    private int mActualHeight;
65    private boolean mNeverAppliedGroupState;
66
67    public NotificationChildrenContainer(Context context) {
68        this(context, null);
69    }
70
71    public NotificationChildrenContainer(Context context, AttributeSet attrs) {
72        this(context, attrs, 0);
73    }
74
75    public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr) {
76        this(context, attrs, defStyleAttr, 0);
77    }
78
79    public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr,
80            int defStyleRes) {
81        super(context, attrs, defStyleAttr, defStyleRes);
82        initDimens();
83        mHybridGroupManager = new HybridGroupManager(getContext(), this);
84    }
85
86    private void initDimens() {
87        mChildPadding = getResources().getDimensionPixelSize(
88                R.dimen.notification_children_padding);
89        mDividerHeight = Math.max(1, getResources().getDimensionPixelSize(
90                R.dimen.notification_divider_height));
91        mMaxNotificationHeight = getResources().getDimensionPixelSize(
92                R.dimen.notification_max_height);
93        mNotificationHeaderHeight = getResources().getDimensionPixelSize(
94                com.android.internal.R.dimen.notification_content_margin_top);
95        mNotificatonTopPadding = getResources().getDimensionPixelSize(
96                R.dimen.notification_children_container_top_padding);
97        mCollapsedBottompadding = getResources().getDimensionPixelSize(
98                com.android.internal.R.dimen.notification_content_margin_bottom);
99    }
100
101    @Override
102    protected void onLayout(boolean changed, int l, int t, int r, int b) {
103        int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
104        for (int i = 0; i < childCount; i++) {
105            View child = mChildren.get(i);
106            if (child.getVisibility() == View.GONE) {
107                continue;
108            }
109            child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
110            mDividers.get(i).layout(0, 0, getWidth(), mDividerHeight);
111        }
112        if (mOverflowNumber != null) {
113            mOverflowNumber.layout(getWidth() - mOverflowNumber.getMeasuredWidth(), 0, getWidth(),
114                    mOverflowNumber.getMeasuredHeight());
115        }
116    }
117
118    @Override
119    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
120        int ownMaxHeight = mMaxNotificationHeight;
121        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
122        boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
123        boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
124        int size = MeasureSpec.getSize(heightMeasureSpec);
125        if (hasFixedHeight || isHeightLimited) {
126            ownMaxHeight = Math.min(ownMaxHeight, size);
127        }
128        int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
129        int width = MeasureSpec.getSize(widthMeasureSpec);
130        if (mOverflowNumber != null) {
131            mOverflowNumber.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
132                    newHeightSpec);
133        }
134        int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
135        int height = mNotificationHeaderHeight + mNotificatonTopPadding;
136        int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
137        int collapsedChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
138        int overflowIndex = childCount > collapsedChildren ? collapsedChildren - 1 : -1;
139        for (int i = 0; i < childCount; i++) {
140            ExpandableNotificationRow child = mChildren.get(i);
141            boolean isOverflow = i == overflowIndex;
142            child.setSingleLineWidthIndention(isOverflow ? mOverflowNumber.getMeasuredWidth() : 0);
143            child.measure(widthMeasureSpec, newHeightSpec);
144            height += child.getMeasuredHeight();
145
146            // layout the divider
147            View divider = mDividers.get(i);
148            divider.measure(widthMeasureSpec, dividerHeightSpec);
149            height += mDividerHeight;
150        }
151        mRealHeight = height;
152        if (heightMode != MeasureSpec.UNSPECIFIED) {
153            height = Math.min(height, size);
154        }
155        setMeasuredDimension(width, height);
156    }
157
158    @Override
159    public boolean pointInView(float localX, float localY, float slop) {
160        return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
161                localY < (mRealHeight + slop);
162    }
163
164    /**
165     * Add a child notification to this view.
166     *
167     * @param row the row to add
168     * @param childIndex the index to add it at, if -1 it will be added at the end
169     */
170    public void addNotification(ExpandableNotificationRow row, int childIndex) {
171        int newIndex = childIndex < 0 ? mChildren.size() : childIndex;
172        mChildren.add(newIndex, row);
173        addView(row);
174        row.setUserLocked(mUserLocked);
175
176        View divider = inflateDivider();
177        addView(divider);
178        mDividers.add(newIndex, divider);
179
180        updateGroupOverflow();
181    }
182
183    public void removeNotification(ExpandableNotificationRow row) {
184        int childIndex = mChildren.indexOf(row);
185        mChildren.remove(row);
186        removeView(row);
187
188        final View divider = mDividers.remove(childIndex);
189        removeView(divider);
190        getOverlay().add(divider);
191        CrossFadeHelper.fadeOut(divider, new Runnable() {
192            @Override
193            public void run() {
194                getOverlay().remove(divider);
195            }
196        });
197
198        row.setSystemChildExpanded(false);
199        row.setUserLocked(false);
200        updateGroupOverflow();
201    }
202
203    public void updateGroupOverflow() {
204        int childCount = mChildren.size();
205        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
206        if (childCount > maxAllowedVisibleChildren) {
207            mOverflowNumber = mHybridGroupManager.bindOverflowNumber(
208                    mOverflowNumber, childCount - maxAllowedVisibleChildren);
209            if (mOverflowInvertHelper == null) {
210                mOverflowInvertHelper= new ViewInvertHelper(mOverflowNumber,
211                        NotificationPanelView.DOZE_ANIMATION_DURATION);
212            }
213            if (mGroupOverFlowState == null) {
214                mGroupOverFlowState = new ViewState();
215                mNeverAppliedGroupState = true;
216            }
217        } else if (mOverflowNumber != null) {
218            removeView(mOverflowNumber);
219            if (isShown()) {
220                final View removedOverflowNumber = mOverflowNumber;
221                addTransientView(removedOverflowNumber, getTransientViewCount());
222                CrossFadeHelper.fadeOut(removedOverflowNumber, new Runnable() {
223                    @Override
224                    public void run() {
225                        removeTransientView(removedOverflowNumber);
226                    }
227                });
228            }
229            mOverflowNumber = null;
230            mOverflowInvertHelper = null;
231            mGroupOverFlowState = null;
232        }
233    }
234
235    @Override
236    protected void onConfigurationChanged(Configuration newConfig) {
237        super.onConfigurationChanged(newConfig);
238        updateGroupOverflow();
239    }
240
241    private View inflateDivider() {
242        return LayoutInflater.from(mContext).inflate(
243                R.layout.notification_children_divider, this, false);
244    }
245
246    public List<ExpandableNotificationRow> getNotificationChildren() {
247        return mChildren;
248    }
249
250    /**
251     * Apply the order given in the list to the children.
252     *
253     * @param childOrder the new list order
254     * @return whether the list order has changed
255     */
256    public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder) {
257        if (childOrder == null) {
258            return false;
259        }
260        boolean result = false;
261        for (int i = 0; i < mChildren.size() && i < childOrder.size(); i++) {
262            ExpandableNotificationRow child = mChildren.get(i);
263            ExpandableNotificationRow desiredChild = childOrder.get(i);
264            if (child != desiredChild) {
265                mChildren.remove(desiredChild);
266                mChildren.add(i, desiredChild);
267                result = true;
268            }
269        }
270        updateExpansionStates();
271        return result;
272    }
273
274    private void updateExpansionStates() {
275        if (mChildrenExpanded || mUserLocked) {
276            // we don't modify it the group is expanded or if we are expanding it
277            return;
278        }
279        int size = mChildren.size();
280        for (int i = 0; i < size; i++) {
281            ExpandableNotificationRow child = mChildren.get(i);
282            child.setSystemChildExpanded(i == 0 && size == 1);
283        }
284    }
285
286    /**
287     *
288     * @return the intrinsic size of this children container, i.e the natural fully expanded state
289     */
290    public int getIntrinsicHeight() {
291        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
292        return getIntrinsicHeight(maxAllowedVisibleChildren);
293    }
294
295    /**
296     * @return the intrinsic height with a number of children given
297     *         in @param maxAllowedVisibleChildren
298     */
299    private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
300        int intrinsicHeight = mNotificationHeaderHeight;
301        int visibleChildren = 0;
302        int childCount = mChildren.size();
303        boolean firstChild = true;
304        float expandFactor = 0;
305        if (mUserLocked) {
306            expandFactor = getChildExpandFraction();
307        }
308        for (int i = 0; i < childCount; i++) {
309            if (visibleChildren >= maxAllowedVisibleChildren) {
310                break;
311            }
312            if (!firstChild) {
313                if (mUserLocked) {
314                    intrinsicHeight += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
315                            expandFactor);
316                } else {
317                    intrinsicHeight += mChildrenExpanded ? mDividerHeight : mChildPadding;
318                }
319            } else {
320                if (mUserLocked) {
321                    intrinsicHeight += NotificationUtils.interpolate(
322                            0,
323                            mNotificatonTopPadding + mDividerHeight,
324                            expandFactor);
325                } else {
326                    intrinsicHeight += mChildrenExpanded
327                            ? mNotificatonTopPadding + mDividerHeight
328                            : 0;
329                }
330                firstChild = false;
331            }
332            ExpandableNotificationRow child = mChildren.get(i);
333            intrinsicHeight += child.getIntrinsicHeight();
334            visibleChildren++;
335        }
336        if (mUserLocked) {
337            intrinsicHeight += NotificationUtils.interpolate(mCollapsedBottompadding, 0.0f,
338                    expandFactor);
339        } else if (!mChildrenExpanded) {
340            intrinsicHeight += mCollapsedBottompadding;
341        }
342        return intrinsicHeight;
343    }
344
345    /**
346     * Update the state of all its children based on a linear layout algorithm.
347     *
348     * @param resultState the state to update
349     * @param parentState the state of the parent
350     */
351    public void getState(StackScrollState resultState, StackViewState parentState) {
352        int childCount = mChildren.size();
353        int yPosition = mNotificationHeaderHeight;
354        boolean firstChild = true;
355        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
356        boolean hasOverflow = !mChildrenExpanded && childCount > maxAllowedVisibleChildren
357                && maxAllowedVisibleChildren != NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
358        int lastVisibleIndex = maxAllowedVisibleChildren - 1;
359        float expandFactor = 0;
360        if (mUserLocked) {
361            expandFactor = getChildExpandFraction();
362        }
363        for (int i = 0; i < childCount; i++) {
364            ExpandableNotificationRow child = mChildren.get(i);
365            if (!firstChild) {
366                if (mUserLocked) {
367                     yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
368                             expandFactor);
369                } else {
370                    yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
371                }
372            } else {
373                if (mUserLocked) {
374                    yPosition += NotificationUtils.interpolate(
375                            0,
376                            mNotificatonTopPadding + mDividerHeight,
377                            expandFactor);
378                } else {
379                    yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
380                }
381                firstChild = false;
382            }
383            StackViewState childState = resultState.getViewStateForView(child);
384            int intrinsicHeight = child.getIntrinsicHeight();
385            childState.yTranslation = yPosition;
386            childState.zTranslation = 0;
387            childState.height = intrinsicHeight;
388            childState.dimmed = parentState.dimmed;
389            childState.dark = parentState.dark;
390            childState.hideSensitive = parentState.hideSensitive;
391            childState.belowSpeedBump = parentState.belowSpeedBump;
392            childState.clipTopAmount = 0;
393            childState.topOverLap = 0;
394            boolean visible = i <= lastVisibleIndex;
395            childState.alpha = visible ? 1 : 0;
396            childState.location = parentState.location;
397            yPosition += intrinsicHeight;
398        }
399        if (mOverflowNumber != null) {
400            ExpandableNotificationRow overflowView = mChildren.get(Math.min(
401                    getMaxAllowedVisibleChildren(true /* likeCollpased */), childCount) - 1);
402            mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
403            if (!mChildrenExpanded) {
404                if (mUserLocked) {
405                    HybridNotificationView singleLineView = overflowView.getSingleLineView();
406                    View mirrorView = singleLineView.getTextView();
407                    if (mirrorView.getVisibility() == GONE) {
408                        mirrorView = singleLineView.getTitleView();
409                    }
410                    if (mirrorView.getVisibility() == GONE) {
411                        mirrorView = singleLineView;
412                    }
413                    mGroupOverFlowState.yTranslation += NotificationUtils.getRelativeYOffset(
414                            mirrorView, overflowView);
415                    mGroupOverFlowState.alpha = mirrorView.getAlpha();
416                }
417            } else {
418                mGroupOverFlowState.yTranslation += mNotificationHeaderHeight;
419                mGroupOverFlowState.alpha = 0.0f;
420            }
421        }
422    }
423
424    private int getMaxAllowedVisibleChildren() {
425        return getMaxAllowedVisibleChildren(false /* likeCollapsed */);
426    }
427
428    private int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
429        if (!likeCollapsed && (mChildrenExpanded || mNotificationParent.isUserLocked())) {
430            return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
431        }
432        if (mNotificationParent.isExpanded() || mNotificationParent.isHeadsUp()) {
433            return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
434        }
435        return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
436    }
437
438    public void applyState(StackScrollState state) {
439        int childCount = mChildren.size();
440        ViewState tmpState = new ViewState();
441        float expandFraction = 0.0f;
442        if (mUserLocked) {
443            expandFraction = getChildExpandFraction();
444        }
445        for (int i = 0; i < childCount; i++) {
446            ExpandableNotificationRow child = mChildren.get(i);
447            StackViewState viewState = state.getViewStateForView(child);
448            state.applyState(child, viewState);
449
450            // layout the divider
451            View divider = mDividers.get(i);
452            tmpState.initFrom(divider);
453            tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
454            float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
455            if (mUserLocked && viewState.alpha != 0) {
456                alpha = NotificationUtils.interpolate(0, 0.5f, expandFraction);
457            }
458            tmpState.alpha = alpha;
459            state.applyViewState(divider, tmpState);
460            // There is no fake shadow to be drawn on the children
461            child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
462        }
463        if (mOverflowNumber != null) {
464            state.applyViewState(mOverflowNumber, mGroupOverFlowState);
465            mNeverAppliedGroupState = false;
466        }
467    }
468
469    /**
470     * This is called when the children expansion has changed and positions the children properly
471     * for an appear animation.
472     *
473     * @param state the new state we animate to
474     */
475    public void prepareExpansionChanged(StackScrollState state) {
476        // TODO: do something that makes sense, like placing the invisible views correctly
477        return;
478    }
479
480    public void startAnimationToState(StackScrollState state, StackStateAnimator stateAnimator,
481            long baseDelay, long duration) {
482        int childCount = mChildren.size();
483        ViewState tmpState = new ViewState();
484        float expandFraction = getChildExpandFraction();
485        for (int i = childCount - 1; i >= 0; i--) {
486            ExpandableNotificationRow child = mChildren.get(i);
487            StackViewState viewState = state.getViewStateForView(child);
488            stateAnimator.startStackAnimations(child, viewState, state, -1, baseDelay);
489
490            // layout the divider
491            View divider = mDividers.get(i);
492            tmpState.initFrom(divider);
493            tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
494            float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
495            if (mUserLocked && viewState.alpha != 0) {
496                alpha = NotificationUtils.interpolate(0, 0.5f, expandFraction);
497            }
498            tmpState.alpha = alpha;
499            stateAnimator.startViewAnimations(divider, tmpState, baseDelay, duration);
500            // There is no fake shadow to be drawn on the children
501            child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
502        }
503        if (mOverflowNumber != null) {
504            if (mNeverAppliedGroupState) {
505                float alpha = mGroupOverFlowState.alpha;
506                mGroupOverFlowState.alpha = 0;
507                state.applyViewState(mOverflowNumber, mGroupOverFlowState);
508                mGroupOverFlowState.alpha = alpha;
509                mNeverAppliedGroupState = false;
510            }
511            stateAnimator.startViewAnimations(mOverflowNumber, mGroupOverFlowState,
512                    baseDelay, duration);
513        }
514    }
515
516    public ExpandableNotificationRow getViewAtPosition(float y) {
517        // find the view under the pointer, accounting for GONE views
518        final int count = mChildren.size();
519        for (int childIdx = 0; childIdx < count; childIdx++) {
520            ExpandableNotificationRow slidingChild = mChildren.get(childIdx);
521            float childTop = slidingChild.getTranslationY();
522            float top = childTop + slidingChild.getClipTopAmount();
523            float bottom = childTop + slidingChild.getActualHeight();
524            if (y >= top && y <= bottom) {
525                return slidingChild;
526            }
527        }
528        return null;
529    }
530
531    public void setChildrenExpanded(boolean childrenExpanded) {
532        mChildrenExpanded = childrenExpanded;
533        updateExpansionStates();
534    }
535
536    public void setNotificationParent(ExpandableNotificationRow parent) {
537        mNotificationParent = parent;
538    }
539
540    public int getMaxContentHeight() {
541        int maxContentHeight = mNotificationHeaderHeight + mNotificatonTopPadding;
542        int visibleChildren = 0;
543        int childCount = mChildren.size();
544        for (int i = 0; i < childCount; i++) {
545            if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
546                break;
547            }
548            ExpandableNotificationRow child = mChildren.get(i);
549            float childHeight = child.isExpanded(true /* allowOnKeyguard */)
550                    ? child.getMaxExpandHeight()
551                    : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
552            maxContentHeight += childHeight;
553            visibleChildren++;
554        }
555        if (visibleChildren > 0) {
556            maxContentHeight += visibleChildren * mDividerHeight;
557        }
558        return maxContentHeight;
559    }
560
561    public void setActualHeight(int actualHeight) {
562        if (!mUserLocked) {
563            return;
564        }
565        mActualHeight = actualHeight;
566        float fraction = getChildExpandFraction();
567        int childCount = mChildren.size();
568        for (int i = 0; i < childCount; i++) {
569            ExpandableNotificationRow child = mChildren.get(i);
570            float childHeight = child.isExpanded(true /* allowOnKeyguard */)
571                    ? child.getMaxExpandHeight()
572                    : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
573            float singleLineHeight = child.getShowingLayout().getMinHeight(
574                    false /* likeGroupExpanded */);
575            child.setActualHeight((int) NotificationUtils.interpolate(singleLineHeight, childHeight,
576                    fraction), false);
577        }
578    }
579
580    public float getChildExpandFraction() {
581        int allChildrenVisibleHeight = getChildrenExpandStartHeight();
582        int maxContentHeight = getMaxContentHeight();
583        float factor = (mActualHeight - allChildrenVisibleHeight)
584                / (float) (maxContentHeight - allChildrenVisibleHeight);
585        return Math.max(0.0f, Math.min(1.0f, factor));
586    }
587
588    private int getChildrenExpandStartHeight() {
589        int intrinsicHeight = mNotificationHeaderHeight;
590        int visibleChildren = 0;
591        int childCount = mChildren.size();
592        for (int i = 0; i < childCount; i++) {
593            if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
594                break;
595            }
596            ExpandableNotificationRow child = mChildren.get(i);
597            intrinsicHeight += child.getMinHeight();
598            visibleChildren++;
599        }
600        if (visibleChildren > 0) {
601            intrinsicHeight += (visibleChildren - 1) * mChildPadding;
602        }
603        intrinsicHeight += mCollapsedBottompadding;
604        return intrinsicHeight;
605    }
606
607    public int getMinHeight() {
608        return getIntrinsicHeight(NUMBER_OF_CHILDREN_WHEN_COLLAPSED);
609    }
610
611    public int getMinExpandHeight(boolean onKeyguard) {
612        int maxAllowedVisibleChildren = onKeyguard ? NUMBER_OF_CHILDREN_WHEN_COLLAPSED
613                : getMaxAllowedVisibleChildren(true /* forceCollapsed */);
614        int minExpandHeight = mNotificationHeaderHeight;
615        int visibleChildren = 0;
616        boolean firstChild = true;
617        int childCount = mChildren.size();
618        for (int i = 0; i < childCount; i++) {
619            if (visibleChildren >= maxAllowedVisibleChildren) {
620                break;
621            }
622            if (!firstChild) {
623                minExpandHeight += mChildPadding;
624            } else {
625                firstChild = false;
626            }
627            ExpandableNotificationRow child = mChildren.get(i);
628            minExpandHeight += child.getMinHeight();
629            visibleChildren++;
630        }
631        minExpandHeight += mCollapsedBottompadding;
632        return minExpandHeight;
633    }
634
635    public void setDark(boolean dark, boolean fade, long delay) {
636        if (mOverflowNumber != null) {
637            mOverflowInvertHelper.setInverted(dark, fade, delay);
638        }
639    }
640
641    public void reInflateViews() {
642        initDimens();
643        for (int i = 0; i < mDividers.size(); i++) {
644            View prevDivider = mDividers.get(i);
645            int index = indexOfChild(prevDivider);
646            removeView(prevDivider);
647            View divider = inflateDivider();
648            addView(divider, index);
649            mDividers.set(i, divider);
650        }
651    }
652
653    public void setUserLocked(boolean userLocked) {
654        mUserLocked = userLocked;
655        int childCount = mChildren.size();
656        for (int i = 0; i < childCount; i++) {
657            ExpandableNotificationRow child = mChildren.get(i);
658            child.setUserLocked(userLocked);
659        }
660    }
661
662    public void onNotificationUpdated() {
663        mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
664                mNotificationParent.getNotificationColor());
665    }
666}
667