Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 14af8e97 authored by Lyn Han's avatar Lyn Han Committed by Android (Google) Code Review
Browse files

Merge "Expand notifications like accordian" into sc-dev

parents 5c3e1245 99c69253
Loading
Loading
Loading
Loading
+91 −34
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.statusbar.phone.NotificationIconContainer;

@@ -82,6 +83,9 @@ public class NotificationShelf extends ActivatableNotificationView implements
    private Rect mClipRect = new Rect();
    private int mCutoutHeight;
    private int mGapHeight;
    private int mIndexOfFirstViewInShelf = -1;
    private int mIndexOfFirstViewInOverflowingSection = -1;

    private NotificationShelfController mController;

    public NotificationShelf(Context context, AttributeSet attrs) {
@@ -159,30 +163,49 @@ public class NotificationShelf extends ActivatableNotificationView implements
    }

    /** Update the state of the shelf. */
    public void updateState(AmbientState ambientState) {
    public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState,
            AmbientState ambientState) {
        ExpandableView lastView = ambientState.getLastVisibleBackgroundChild();
        ShelfState viewState = (ShelfState) getViewState();
        if (mShowNotificationShelf && lastView != null) {
            float maxShelfEnd = ambientState.getInnerHeight() + ambientState.getTopPadding()
                    + ambientState.getStackTranslation();
            ExpandableViewState lastViewState = lastView.getViewState();
            float viewEnd = lastViewState.yTranslation + lastViewState.height;
            viewState.copyFrom(lastViewState);

            viewState.height = getIntrinsicHeight();
            viewState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - viewState.height,
                    getFullyClosedTranslation());
            viewState.zTranslation = ambientState.getBaseZHeight();
            viewState.clipTopAmount = 0;
            viewState.alpha = 1f - ambientState.getHideAmount();
            viewState.belowSpeedBump = mHostLayoutController.getSpeedBumpIndex() == 0;
            viewState.hideSensitive = false;
            viewState.xTranslation = getTranslationX();
            viewState.hasItemsInStableShelf = lastViewState.inShelf;
            viewState.firstViewInShelf = algorithmState.firstViewInShelf;
            viewState.firstViewInOverflowSection = algorithmState.firstViewInOverflowSection;
            if (mNotGoneIndex != -1) {
                viewState.notGoneIndex = Math.min(viewState.notGoneIndex, mNotGoneIndex);
            }
            viewState.hasItemsInStableShelf = lastViewState.inShelf;

            viewState.hidden = !mAmbientState.isShadeExpanded()
                    || mAmbientState.isQsCustomizerShowing();
                    || mAmbientState.isQsCustomizerShowing()
                    || algorithmState.firstViewInShelf == null;

            final int indexOfFirstViewInShelf = algorithmState.visibleChildren.indexOf(
                    algorithmState.firstViewInShelf);

            if (mAmbientState.isExpansionChanging()
                    && algorithmState.firstViewInShelf != null
                    && indexOfFirstViewInShelf > 0) {

                // Show shelf if section before it is showing.
                final ExpandableView viewBeforeShelf = algorithmState.visibleChildren.get(
                        indexOfFirstViewInShelf - 1);
                if (viewBeforeShelf.getViewState().hidden) {
                    viewState.hidden = true;
                }
            }

            final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
            viewState.yTranslation = stackEnd - viewState.height;
        } else {
            viewState.hidden = true;
            viewState.location = ExpandableViewState.LOCATION_GONE;
@@ -199,13 +222,11 @@ public class NotificationShelf extends ActivatableNotificationView implements
        if (!mShowNotificationShelf) {
            return;
        }

        mShelfIcons.resetViewStates();
        float shelfStart = getTranslationY();
        float numViewsInShelf = 0.0f;
        View lastChild = mAmbientState.getLastVisibleBackgroundChild();
        mNotGoneIndex = -1;
        float interpolationStart = mMaxLayoutHeight - getIntrinsicHeight() * 2;
        //  find the first view that doesn't overlap with the shelf
        int notGoneIndex = 0;
        int colorOfViewBeforeLast = NO_COLOR;
@@ -233,22 +254,37 @@ public class NotificationShelf extends ActivatableNotificationView implements
            if (!child.needsClippingToShelf() || child.getVisibility() == GONE) {
                continue;
            }

            float notificationClipEnd;
            boolean aboveShelf = ViewState.getFinalTranslationZ(child) > baseZHeight
                    || child.isPinned();
            boolean isLastChild = child == lastChild;
            float rowTranslationY = child.getTranslationY();

            final float inShelfAmount = updateShelfTransformation(i, child, scrollingFast,
                    expandingAnimated, isLastChild);

            final float stackEnd = mAmbientState.getStackY()
                    + mAmbientState.getStackHeight();
            // TODO(b/172289889) scale mPaddingBetweenElements with expansion amount
            if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) {
                notificationClipEnd = shelfStart + getIntrinsicHeight();
                notificationClipEnd = stackEnd;
            } else if (mAmbientState.isExpansionChanging()) {
                if (mIndexOfFirstViewInOverflowingSection != -1
                    && i >= mIndexOfFirstViewInOverflowingSection) {
                    // Clip notifications in (section overflowing into shelf) to shelf start.
                    notificationClipEnd = shelfStart - mPaddingBetweenElements;
                } else {
                    // Clip notifications before the section overflowing into shelf
                    // to stackEnd because we do not show the shelf if the section right before the
                    // shelf is still hidden.
                    notificationClipEnd = stackEnd;
                }
            } else {
                notificationClipEnd = shelfStart - mPaddingBetweenElements;
            }
            int clipTop = updateNotificationClipHeight(child, notificationClipEnd, notGoneIndex);
            clipTopAmount = Math.max(clipTop, clipTopAmount);

            final float inShelfAmount = updateShelfTransformation(child, scrollingFast,
                    expandingAnimated, isLastChild);
            // If the current row is an ExpandableNotificationRow, update its color, roundedness,
            // and icon state.
            if (child instanceof ExpandableNotificationRow) {
@@ -314,19 +350,23 @@ public class NotificationShelf extends ActivatableNotificationView implements
                                distanceToGapTop / mGapHeight);
                        previousAnv.setBottomRoundness(firstElementRoundness,
                                false /* don't animate */);
                        backgroundTop = (int) distanceToGapBottom;
                    }
                }
                previousAnv = anv;
            }
        }

        clipTransientViews();

        setClipTopAmount(clipTopAmount);
        boolean isHidden = getViewState().hidden || clipTopAmount >= getIntrinsicHeight();
        if (mShowNotificationShelf) {

        boolean isHidden = getViewState().hidden
                || clipTopAmount >= getIntrinsicHeight()
                || !mShowNotificationShelf
                || numViewsInShelf < 1f;

        // TODO(b/172289889) transition last icon in shelf to notification icon and vice versa.
        setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE);
        }
        setBackgroundTop(backgroundTop);
        setFirstElementRoundness(firstElementRoundness);
        mShelfIcons.setSpeedBumpIndex(mHostLayoutController.getSpeedBumpIndex());
@@ -339,11 +379,10 @@ public class NotificationShelf extends ActivatableNotificationView implements
                continue;
            }
            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
            updateIconClipAmount(row);
            updateContinuousClipping(row);
        }
        boolean hideBackground = numViewsInShelf < 1.0f;
        setHideBackground(hideBackground || backgroundForceHidden);
        boolean hideBackground = isHidden;
        setHideBackground(hideBackground);
        if (mNotGoneIndex == -1) {
            mNotGoneIndex = notGoneIndex;
        }
@@ -476,10 +515,10 @@ public class NotificationShelf extends ActivatableNotificationView implements
    /**
     * @return the amount how much this notification is in the shelf
     */
    private float updateShelfTransformation(ExpandableView view, boolean scrollingFast,
    private float updateShelfTransformation(int i, ExpandableView view, boolean scrollingFast,
            boolean expandingAnimated, boolean isLastChild) {

        // Let calculate how much the view is in the shelf
        // Let's calculate how much the view is in the shelf
        float viewStart = view.getTranslationY();
        int fullHeight = view.getActualHeight() + mPaddingBetweenElements;
        float iconTransformStart = calculateIconTransformationStart(view);
@@ -496,12 +535,18 @@ public class NotificationShelf extends ActivatableNotificationView implements
                    transformDistance,
                    view.getMinHeight() - getIntrinsicHeight());
        }

        float viewEnd = viewStart + fullHeight;
        float fullTransitionAmount = 0.0f;
        float iconTransitionAmount = 0.0f;
        float shelfStart = getTranslationY();

        if (viewEnd >= shelfStart
        if (mAmbientState.isExpansionChanging() && !mAmbientState.isOnKeyguard()) {
            // TODO(b/172289889) handle icon placement for notification that is clipped by the shelf
            if (mIndexOfFirstViewInShelf != -1 && i >= mIndexOfFirstViewInShelf) {
                fullTransitionAmount = 1f;
                iconTransitionAmount = 1f;
            }
        } else if (viewEnd >= shelfStart
                && (!mAmbientState.isUnlockHintRunning() || view.isInShelf())
                && (mAmbientState.isShadeExpanded()
                || (!view.isPinned() && !view.isHeadsUpAnimatingAway()))) {
@@ -572,7 +617,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
                    && !mNoAnimationsInThisFrame;
        }
        iconState.clampedAppearAmount = clampedAmount;
        setIconTransformationAmount(view, transitionAmount, isLastChild);
        setIconTransformationAmount(view, transitionAmount);
    }

    private boolean isTargetClipped(ExpandableView view) {
@@ -585,12 +630,10 @@ public class NotificationShelf extends ActivatableNotificationView implements
                + view.getContentTranslation()
                + view.getRelativeTopPadding(target)
                + target.getHeight();

        return endOfTarget >= getTranslationY() - mPaddingBetweenElements;
    }

    private void setIconTransformationAmount(ExpandableView view, float transitionAmount,
            boolean isLastChild) {
    private void setIconTransformationAmount(ExpandableView view, float transitionAmount) {
        if (!(view instanceof ExpandableNotificationRow)) {
            return;
        }
@@ -601,7 +644,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
            return;
        }
        iconState.alpha = transitionAmount;

        boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
        iconState.hidden = isAppearing
                || (view instanceof ExpandableNotificationRow
@@ -610,8 +652,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
                || (transitionAmount == 0.0f && !iconState.isAnimating(icon))
                || row.isAboveShelf()
                || row.showingPulsing()
                || (!row.isInShelf() && isLastChild)
                || row.getTranslationZ() > mAmbientState.getBaseZHeight();

        iconState.iconAppearAmount = iconState.hidden? 0f : transitionAmount;

        // Fade in icons at shelf start
@@ -790,8 +832,19 @@ public class NotificationShelf extends ActivatableNotificationView implements
        mController = notificationShelfController;
    }

    public void setIndexOfFirstViewInShelf(ExpandableView firstViewInShelf) {
        mIndexOfFirstViewInShelf = mHostLayoutController.indexOfChild(firstViewInShelf);
    }

    public void setFirstViewInOverflowingSection(ExpandableView firstViewInOverflowingSection) {
        mIndexOfFirstViewInOverflowingSection =
                mHostLayoutController.indexOfChild(firstViewInOverflowingSection);
    }

    private class ShelfState extends ExpandableViewState {
        private boolean hasItemsInStableShelf;
        private ExpandableView firstViewInShelf;
        private ExpandableView firstViewInOverflowSection;

        @Override
        public void applyToView(View view) {
@@ -800,6 +853,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
            }

            super.applyToView(view);
            setIndexOfFirstViewInShelf(firstViewInShelf);
            setFirstViewInOverflowingSection(firstViewInOverflowSection);
            updateAppearance();
            setHasItemsInStableShelf(hasItemsInStableShelf);
            mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
@@ -812,6 +867,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
            }

            super.animateTo(child, properties);
            setIndexOfFirstViewInShelf(firstViewInShelf);
            setFirstViewInOverflowingSection(firstViewInOverflowSection);
            updateAppearance();
            setHasItemsInStableShelf(hasItemsInStableShelf);
            mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
+4 −2
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.phone.StatusBarNotificationPresenter;
@@ -103,9 +104,10 @@ public class NotificationShelfController {
        return mView.getHeight();
    }

    public void updateState(AmbientState ambientState) {
    public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState,
            AmbientState ambientState) {
        mAmbientState = ambientState;
        mView.updateState(ambientState);
        mView.updateState(algorithmState, ambientState);
    }

    public int getIntrinsicHeight() {
+78 −5
Original line number Diff line number Diff line
@@ -60,7 +60,7 @@ public class AmbientState {
    private NotificationShelf mShelf;
    private int mZDistanceBetweenElements;
    private int mBaseZHeight;
    private int mMaxLayoutHeight;
    private int mContentHeight;
    private ExpandableView mLastVisibleBackgroundChild;
    private float mCurrentScrollVelocity;
    private int mStatusBarState;
@@ -84,6 +84,75 @@ public class AmbientState {
    private boolean mIsShadeOpening;
    private float mSectionPadding;

    /** Distance of top of notifications panel from top of screen. */
    private float mStackY = 0;

    /** Height of notifications panel. */
    private float mStackHeight = 0;

    /** Fraction of shade expansion. */
    private float mExpansionFraction;

    /** Height of the notifications panel without top padding when expansion completes. */
    private float mStackEndHeight;

    /**
     * @return Height of the notifications panel without top padding when expansion completes.
     */
    public float getStackEndHeight() {
        return mStackEndHeight;
    }

    /**
     * @param stackEndHeight Height of the notifications panel without top padding
     *                       when expansion completes.
     */
    public void setStackEndHeight(float stackEndHeight) {
        mStackEndHeight = stackEndHeight;
    }

    /**
     * @param stackY Distance of top of notifications panel from top of screen.
     */
    public void setStackY(float stackY) {
        mStackY = stackY;
    }

    /**
     * @return Distance of top of notifications panel from top of screen.
     */
    public float getStackY() {
        return mStackY;
    }

    /**
     * @param expansionFraction Fraction of shade expansion.
     */
    public void setExpansionFraction(float expansionFraction) {
        mExpansionFraction = expansionFraction;
    }

    /**
     * @return Fraction of shade expansion.
     */
    public float getExpansionFraction() {
        return mExpansionFraction;
    }

    /**
     * @param stackHeight Height of notifications panel.
     */
    public void setStackHeight(float stackHeight) {
        mStackHeight = stackHeight;
    }

    /**
     * @return Height of notifications panel.
     */
    public float getStackHeight() {
        return mStackHeight;
    }

    /** Tracks the state from AlertingNotificationManager#hasNotifications() */
    private boolean mHasAlertEntries;

@@ -263,8 +332,8 @@ public class AmbientState {
        if (mDozeAmount == 1.0f && !isPulseExpanding()) {
            return mShelf.getHeight();
        }
        int height = Math.max(mLayoutMinHeight,
                Math.min(mLayoutHeight, mMaxLayoutHeight) - mTopPadding);
        int height = (int) Math.max(mLayoutMinHeight,
                Math.min(mLayoutHeight, mContentHeight) - mTopPadding);
        if (ignorePulseHeight) {
            return height;
        }
@@ -313,8 +382,12 @@ public class AmbientState {
        return mShelf;
    }

    public void setLayoutMaxHeight(int maxLayoutHeight) {
        mMaxLayoutHeight = maxLayoutHeight;
    public void setContentHeight(int contentHeight) {
        mContentHeight = contentHeight;
    }

    public float getContentHeight() {
        return mContentHeight;
    }

    /**
+29 −1
Original line number Diff line number Diff line
@@ -658,6 +658,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
            y = getHeight() - getEmptyBottomMargin();
            mDebugPaint.setColor(Color.GREEN);
            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);

            y = (int) (mAmbientState.getStackY());
            mDebugPaint.setColor(Color.CYAN);
            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);

            y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
            mDebugPaint.setColor(Color.BLUE);
            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
        }
    }

@@ -1123,11 +1131,25 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
                mTopPaddingNeedsAnimation = true;
                mNeedsAnimation = true;
            }
            updateStackPosition();
            requestChildrenUpdate();
            notifyHeightChangeListener(null, animate);
        }
    }

    /**
     * Apply expansion fraction to the y position and height of the notifications panel.
     */
    private void updateStackPosition() {
        // Consider interpolating from an mExpansionStartY for use on lockscreen and AOD
        mAmbientState.setStackY(
                MathUtils.lerp(0, mTopPadding, mAmbientState.getExpansionFraction()));
        final float shadeBottom = getHeight() - getEmptyBottomMargin();
        mAmbientState.setStackEndHeight(shadeBottom - mTopPadding);
        mAmbientState.setStackHeight(
                MathUtils.lerp(0, shadeBottom - mTopPadding, mAmbientState.getExpansionFraction()));
    }

    /**
     * Update the height of the panel.
     *
@@ -1135,6 +1157,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
     */
    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
    public void setExpandedHeight(float height) {
        final float shadeBottom = getHeight() - getEmptyBottomMargin();
        final float expansionFraction = MathUtils.constrain(height / shadeBottom, 0f, 1f);
        mAmbientState.setExpansionFraction(expansionFraction);
        updateStackPosition();

        mExpandedHeight = height;
        setIsExpanded(height > 0);
        int minExpansionHeight = getMinExpansionHeight();
@@ -2067,7 +2094,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomMargin;
        updateScrollability();
        clampScrollPosition();
        mAmbientState.setLayoutMaxHeight(mContentHeight);
        updateStackPosition();
        mAmbientState.setContentHeight(mContentHeight);
    }

    /**
+4 −0
Original line number Diff line number Diff line
@@ -788,6 +788,10 @@ public class NotificationStackScrollLayoutController {
        return mView.getTranslationX();
    }

    public int indexOfChild(View view) {
        return mView.indexOfChild(view);
    }

    public void setOnHeightChangedListener(
            ExpandableView.OnHeightChangedListener listener) {
        mView.setOnHeightChangedListener(listener);
Loading