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

Commit 2b549f4b authored by Selim Cinek's avatar Selim Cinek
Browse files

Added possibility to use canned animation for icons

Icons now move in a canned animation from the shelf position
to the icon.

Test: adb shell setprop debug.icon_opening_animations true && adb shell killall com.android.systemui
Bug: 32437839
Change-Id: I82b6de37ac7a179aeb5d16bd663d566c2f338b1a
parent 7e7c6e2c
Loading
Loading
Loading
Loading
+21 −15
Original line number Diff line number Diff line
@@ -193,7 +193,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
    private View mChildAfterViewWhenDismissed;
    private View mGroupParentWhenDismissed;
    private boolean mRefocusOnDismiss;
    private float mIconTransformationAmount;
    private float mContentTransformationAmount;
    private boolean mIconsVisible = true;
    private boolean mAboveShelf;
    private boolean mIsLastChild;
@@ -837,25 +837,31 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
    /**
     * Set how much this notification is transformed into an icon.
     *
     * @param iconTransformationAmount A value from 0 to 1 indicating how much we are transformed
     *                                 to an icon
     * @param contentTransformationAmount A value from 0 to 1 indicating how much we are transformed
     *                                 to the content away
     * @param isLastChild is this the last child in the list. If true, then the transformation is
     *                    different since it's content fades out.
     */
    public void setIconTransformationAmount(float iconTransformationAmount, boolean isLastChild) {
    public void setContentTransformationAmount(float contentTransformationAmount,
            boolean isLastChild) {
        boolean changeTransformation = isLastChild != mIsLastChild;
        changeTransformation |= mIconTransformationAmount != iconTransformationAmount;
        changeTransformation |= mContentTransformationAmount != contentTransformationAmount;
        mIsLastChild = isLastChild;
        mIconTransformationAmount = iconTransformationAmount;
        mContentTransformationAmount = contentTransformationAmount;
        if (changeTransformation) {
            updateContentTransformation();
            boolean iconsVisible = mIconTransformationAmount == 0.0f;
        }
    }

    /**
     * Set the icons to be visible of this notification.
     */
    public void setIconsVisible(boolean iconsVisible) {
        if (iconsVisible != mIconsVisible) {
            mIconsVisible = iconsVisible;
            updateIconVisibilities();
        }
    }
    }

    @Override
    protected void onBelowSpeedBumpChanged() {
@@ -864,9 +870,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {

    private void updateContentTransformation() {
        float contentAlpha;
        float translationY = - mIconTransformationAmount * mIconTransformContentShift;
        float translationY = -mContentTransformationAmount * mIconTransformContentShift;
        if (mIsLastChild) {
            contentAlpha = 1.0f - mIconTransformationAmount;
            contentAlpha = 1.0f - mContentTransformationAmount;
            contentAlpha = Math.min(contentAlpha / 0.5f, 1.0f);
            contentAlpha = Interpolators.ALPHA_OUT.getInterpolation(contentAlpha);
            translationY *= 0.4f;
@@ -1871,8 +1877,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
        }

        @Override
        protected void onYTranslationAnimationFinished() {
            super.onYTranslationAnimationFinished();
        protected void onYTranslationAnimationFinished(View view) {
            super.onYTranslationAnimationFinished(view);
            if (mHeadsupDisappearRunning) {
                setHeadsUpAnimatingAway(false);
            }
+6 −0
Original line number Diff line number Diff line
@@ -208,6 +208,12 @@ public class NotificationData {
                expandedIcon = null;
                throw new IconException("Couldn't create icon: " + ic);
            }
            expandedIcon.setOnVisibilityChangedListener(
                    newVisibility -> {
                        if (row != null) {
                            row.setIconsVisible(newVisibility != View.VISIBLE);
                        }
                    });
        }

        public void setIconTag(int key, Object tag) {
+101 −49
Original line number Diff line number Diff line
@@ -18,7 +18,10 @@ package com.android.systemui.statusbar;

import android.content.Context;
import android.content.res.Configuration;
import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Property;
import android.view.View;
import android.view.ViewGroup;

@@ -34,7 +37,6 @@ import com.android.systemui.statusbar.stack.ExpandableViewState;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackScrollState;

import java.util.ArrayList;
import java.util.WeakHashMap;

/**
@@ -43,10 +45,11 @@ import java.util.WeakHashMap;
 */
public class NotificationShelf extends ActivatableNotificationView {

    private static final boolean USE_ANIMATIONS_WHEN_OPENING =
            SystemProperties.getBoolean("debug.icon_opening_animations", true);
    private ViewInvertHelper mViewInvertHelper;
    private boolean mDark;
    private NotificationIconContainer mShelfIcons;
    private ArrayList<StatusBarIconView> mIcons = new ArrayList<>();
    private ShelfState mShelfState;
    private int[] mTmp = new int[2];
    private boolean mHideBackground;
@@ -204,8 +207,6 @@ public class NotificationShelf extends ActivatableNotificationView {
                continue;
            }
            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
            StatusBarIconView icon = row.getEntry().expandedIcon;
            NotificationIconContainer.IconState iconState = iconStates.get(icon);
            float notificationClipEnd;
            float shelfStart = getTranslationY();
            boolean aboveShelf = row.getTranslationZ() > mAmbientState.getBaseZHeight();
@@ -223,8 +224,7 @@ public class NotificationShelf extends ActivatableNotificationView {
                }
            }
            updateNotificationClipHeight(row, notificationClipEnd);
            float inShelfAmount = updateIconAppearance(row, iconState, icon, expandAmount,
                    isLastChild);
            float inShelfAmount = updateIconAppearance(row, expandAmount, isLastChild);
            numViewsInShelf += inShelfAmount;
            int ownColorUntinted = row.getBackgroundColorWithoutTint();
            if (row.getTranslationY() >= getTranslationY() && mNotGoneIndex == -1) {
@@ -250,7 +250,7 @@ public class NotificationShelf extends ActivatableNotificationView {
        }
        mShelfIcons.calculateIconTranslations();
        mShelfIcons.applyIconStates();
        setVisibility(numViewsInShelf != 0.0f && mAmbientState.isShadeExpanded()
        setVisibility(mAmbientState.isShadeExpanded()
                ? VISIBLE
                : INVISIBLE);
        boolean hideBackground = numViewsInShelf < 1.0f;
@@ -275,41 +275,103 @@ public class NotificationShelf extends ActivatableNotificationView {
    /**
     * @return the icon amount how much this notification is in the shelf;
     */
    private float updateIconAppearance(ExpandableNotificationRow row,
            NotificationIconContainer.IconState iconState, StatusBarIconView icon,
            float expandAmount, boolean isLastChild) {
    private float updateIconAppearance(ExpandableNotificationRow row, float expandAmount,
            boolean isLastChild) {
        // Let calculate how much the view is in the shelf
        float viewStart = row.getTranslationY();
        int transformHeight = row.getActualHeight() + mPaddingBetweenElements;
        int fullHeight = row.getActualHeight() + mPaddingBetweenElements;
        float iconTransformDistance = getIntrinsicHeight() * 1.5f;
        if (isLastChild) {
            transformHeight =
                    Math.min(transformHeight, row.getMinHeight() - getIntrinsicHeight());
            fullHeight = Math.min(fullHeight, row.getMinHeight() - getIntrinsicHeight());
            iconTransformDistance = Math.min(iconTransformDistance, row.getMinHeight()
                    - getIntrinsicHeight());
        }
        float viewEnd = viewStart + transformHeight;
        float iconAppearAmount;
        float yTranslation;
        float alpha = 1.0f;
        float viewEnd = viewStart + fullHeight;
        float fullTransitionAmount;
        float iconTransitonAmount;
        if (viewEnd >= getTranslationY() && (mAmbientState.isShadeExpanded()
                || (!row.isPinned() && !row.isHeadsUpAnimatingAway()))) {
            if (viewStart < getTranslationY()) {
                float linearAmount = (getTranslationY() - viewStart) / transformHeight;

                float fullAmount = (getTranslationY() - viewStart) / fullHeight;
                float interpolatedAmount =  Interpolators.ACCELERATE_DECELERATE.getInterpolation(
                        linearAmount);
                        fullAmount);
                interpolatedAmount = NotificationUtils.interpolate(
                        interpolatedAmount, linearAmount, expandAmount);
                iconAppearAmount = 1.0f - interpolatedAmount;
                        interpolatedAmount, fullAmount, expandAmount);
                fullTransitionAmount = 1.0f - interpolatedAmount;

                iconTransitonAmount = (getTranslationY() - viewStart) / iconTransformDistance;
                iconTransitonAmount = Math.min(1.0f, iconTransitonAmount);
                iconTransitonAmount = 1.0f - iconTransitonAmount;

            } else {
                iconAppearAmount = 1.0f;
                fullTransitionAmount = 1.0f;
                iconTransitonAmount = 1.0f;
            }
        } else {
            iconAppearAmount = 0.0f;
            fullTransitionAmount = 0.0f;
            iconTransitonAmount = 0.0f;
        }
        row.setContentTransformationAmount(iconTransitonAmount, isLastChild);
        updateIconPositioning(row, iconTransitonAmount, fullTransitionAmount);
        return fullTransitionAmount;
    }

    private void updateIconPositioning(ExpandableNotificationRow row, float iconTransitionAmount,
            float fullTransitionAmount) {
        StatusBarIconView icon = row.getEntry().expandedIcon;
        NotificationIconContainer.IconState iconState = getIconState(icon);
        if (iconState == null) {
            return;
        }
        float clampedAmount = iconTransitionAmount > 0.5f ? 1.0f : 0.0f;
        boolean isLastChild = isLastChild(row);
        if (clampedAmount == iconTransitionAmount) {
            iconState.keepClampedPosition = false;
        }
        if (clampedAmount == fullTransitionAmount) {
            iconState.useFullTransitionAmount = fullTransitionAmount == 0.0f;
        }
        float transitionAmount;
        boolean needCannedAnimation = iconState.clampedAppearAmount == 1.0f
                && clampedAmount == 0.0f;
        if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount) {
            transitionAmount = iconTransitionAmount;
        } else if (iconState.keepClampedPosition
                && iconState.clampedAppearAmount != clampedAmount) {
            // We animated to the clamped amount but then decided to go the other way. Let's
            // animate it to the new position
            transitionAmount = iconTransitionAmount;
            iconState.needsCannedAnimation = true;
            iconState.keepClampedPosition = false;
        } else if (needCannedAnimation || iconState.keepClampedPosition
                || iconState.iconAppearAmount == 1.0f) {
            // We need to perform a canned animation since we crossed the treshhold
            transitionAmount = clampedAmount;
            iconState.keepClampedPosition = iconState.keepClampedPosition || needCannedAnimation;
            iconState.needsCannedAnimation = needCannedAnimation;
        } else {
            transitionAmount = iconTransitionAmount;
        }
        iconState.iconAppearAmount = !USE_ANIMATIONS_WHEN_OPENING
                    || iconState.useFullTransitionAmount
                ? fullTransitionAmount
                : transitionAmount;
        iconState.clampedAppearAmount = clampedAmount;
        setIconTransformationAmount(row, transitionAmount);
    }

        // Lets now calculate how much of the transformation has already happened. This is different
        // from the above, since we only start transforming when the view is already quite a bit
        // pushed in.
    private boolean isLastChild(ExpandableNotificationRow row) {
        return row == mAmbientState.getLastVisibleBackgroundChild();
    }

    private void setIconTransformationAmount(ExpandableNotificationRow row,
            float transitionAmount) {
        StatusBarIconView icon = row.getEntry().expandedIcon;
        NotificationIconContainer.IconState iconState = getIconState(icon);

        View rowIcon = row.getNotificationIcon();
        float notificationIconPosition = viewStart;
        float notificationIconPosition = row.getTranslationY();
        float notificationIconSize = 0.0f;
        int iconTopPadding;
        if (rowIcon != null) {
@@ -322,28 +384,18 @@ public class NotificationShelf extends ActivatableNotificationView {
        float shelfIconPosition = getTranslationY() + icon.getTop();
        shelfIconPosition += ((1.0f - icon.getIconScale()) * icon.getHeight()) / 2.0f;
        float transitionDistance = getIntrinsicHeight() * 1.5f;
        if (isLastChild) {
        if (row == mAmbientState.getLastVisibleBackgroundChild()) {
            transitionDistance = Math.min(transitionDistance, row.getMinHeight()
                    - getIntrinsicHeight());
        }
        float transformationStartPosition = getTranslationY() - transitionDistance;
        float transitionAmount = 0.0f;
        if (viewStart < transformationStartPosition
                || (!mAmbientState.isShadeExpanded()
                        && (row.isPinned() || row.isHeadsUpAnimatingAway()))) {
            // We simply place it on the icon of the notification
            yTranslation = notificationIconPosition - shelfIconPosition;
        } else {
            transitionAmount = (viewStart - transformationStartPosition)
                    / transitionDistance;
            float startPosition = transformationStartPosition + iconTopPadding;
            yTranslation = NotificationUtils.interpolate(
                    startPosition - shelfIconPosition, 0, transitionAmount);
            // If we are merging into the shelf, lets make sure the shelf is at least on our height,
            // otherwise the icons won't be visible.
            setTranslationZ(Math.max(getTranslationZ(), row.getTranslationZ()));
        }
        float iconYTranslation = NotificationUtils.interpolate(
                Math.min(notificationIconPosition, transformationStartPosition + iconTopPadding)
                        - shelfIconPosition,
                0,
                transitionAmount);
        float shelfIconSize = icon.getHeight() * icon.getIconScale();
        float alpha = 1.0f;
        if (!row.isShowingIcon()) {
            // The view currently doesn't have an icon, lets transform it in!
            alpha = transitionAmount;
@@ -352,15 +404,12 @@ public class NotificationShelf extends ActivatableNotificationView {
        // The notification size is different from the size in the shelf / statusbar
        float newSize = NotificationUtils.interpolate(notificationIconSize, shelfIconSize,
                transitionAmount);
        row.setIconTransformationAmount(transitionAmount, isLastChild);
        if (iconState != null) {
            iconState.scaleX = newSize / icon.getHeight() / icon.getIconScale();
            iconState.scaleY = iconState.scaleX;
            iconState.hidden = transitionAmount == 0.0f;
            iconState.iconAppearAmount = iconAppearAmount;
            iconState.alpha = alpha;
            iconState.yTranslation = yTranslation;
            icon.setVisibility(transitionAmount == 0.0f ? INVISIBLE : VISIBLE);
            iconState.yTranslation = iconYTranslation;
            if (row.isInShelf() && !row.isTransformingIntoShelf()) {
                iconState.iconAppearAmount = 1.0f;
                iconState.alpha = 1.0f;
@@ -369,7 +418,10 @@ public class NotificationShelf extends ActivatableNotificationView {
                iconState.hidden = false;
            }
        }
        return iconAppearAmount;
    }

    private NotificationIconContainer.IconState getIconState(StatusBarIconView icon) {
        return mShelfIcons.getIconState(icon);
    }

    private float getFullyClosedTranslation() {
+18 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.util.FloatProperty;
import android.util.Log;
import android.util.Property;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Interpolator;
@@ -102,6 +103,7 @@ public class StatusBarIconView extends AnimatedImageView {
    private ObjectAnimator mIconAppearAnimator;
    private ObjectAnimator mDotAnimator;
    private float mDotAppearAmount;
    private OnVisibilityChangedListener mOnVisibilityChangedListener;

    public StatusBarIconView(Context context, String slot, Notification notification) {
        this(context, slot, notification, false);
@@ -525,7 +527,23 @@ public class StatusBarIconView extends AnimatedImageView {
        invalidate();
    }

    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
        if (mOnVisibilityChangedListener != null) {
            mOnVisibilityChangedListener.onVisibilityChanged(visibility);
        }
    }

    public float getDotAppearAmount() {
        return mDotAppearAmount;
    }

    public void setOnVisibilityChangedListener(OnVisibilityChangedListener listener) {
        mOnVisibilityChangedListener = listener;
    }

    public interface OnVisibilityChangedListener {
        void onVisibilityChanged(int newVisibility);
    }
}
+79 −13
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import java.util.WeakHashMap;
public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
    private static final String TAG = "NotificationIconContainer";
    private static final boolean DEBUG = false;
    private static final int CANNED_ANIMATION_DURATION = 100;
    private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() {
        private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();

@@ -49,6 +50,26 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
        }
    }.setDuration(200);

    private static final AnimationProperties ICON_ANIMATION_PROPERTIES = new AnimationProperties() {
        private AnimationFilter mAnimationFilter = new AnimationFilter().animateY().animateAlpha();
        // TODO: add scale

        @Override
        public AnimationFilter getAnimationFilter() {
            return mAnimationFilter;
        }
    }.setDuration(CANNED_ANIMATION_DURATION);

    private static final AnimationProperties mTempProperties = new AnimationProperties() {
        private AnimationFilter mAnimationFilter = new AnimationFilter();
        // TODO: add scale

        @Override
        public AnimationFilter getAnimationFilter() {
            return mAnimationFilter;
        }
    }.setDuration(CANNED_ANIMATION_DURATION);

    private static final AnimationProperties ADD_ICON_PROPERTIES = new AnimationProperties() {
        private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();

@@ -66,7 +87,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
    private float mActualPaddingEnd = -1;
    private float mActualPaddingStart = -1;
    private boolean mChangingViewPositions;
    private int mAnimationStartIndex = -1;
    private int mAddAnimationStartIndex = -1;
    private int mCannedAnimationStartIndex = -1;

    public NotificationIconContainer(Context context, AttributeSet attrs) {
        super(context, attrs);
@@ -121,7 +143,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
                childState.applyToView(child);
            }
        }
        mAnimationStartIndex = -1;
        mAddAnimationStartIndex = -1;
        mCannedAnimationStartIndex = -1;
    }

    @Override
@@ -133,10 +156,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
        int childIndex = indexOfChild(child);
        if (childIndex < getChildCount() - 1
            && mIconStates.get(getChildAt(childIndex + 1)).iconAppearAmount > 0.0f) {
            if (mAnimationStartIndex < 0) {
                mAnimationStartIndex = childIndex;
            if (mAddAnimationStartIndex < 0) {
                mAddAnimationStartIndex = childIndex;
            } else {
                mAnimationStartIndex = Math.min(mAnimationStartIndex, childIndex);
                mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, childIndex);
            }
        }
    }
@@ -149,10 +172,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
            if (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
                    && child.getVisibility() == VISIBLE) {
                int animationStartIndex = findFirstViewIndexAfter(icon.getTranslationX());
                if (mAnimationStartIndex < 0) {
                    mAnimationStartIndex = animationStartIndex;
                if (mAddAnimationStartIndex < 0) {
                    mAddAnimationStartIndex = animationStartIndex;
                } else {
                    mAnimationStartIndex = Math.min(mAnimationStartIndex, animationStartIndex);
                    mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, animationStartIndex);
                }
            }
            if (!mChangingViewPositions) {
@@ -305,28 +328,64 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
        mChangingViewPositions = changingViewPositions;
    }

    public IconState getIconState(StatusBarIconView icon) {
        return mIconStates.get(icon);
    }

    public class IconState extends ViewState {
        public float iconAppearAmount = 1.0f;
        public float clampedAppearAmount = 1.0f;
        public int visibleState;
        public boolean justAdded = true;
        public boolean needsCannedAnimation;
        public boolean keepClampedPosition;
        public boolean useFullTransitionAmount;

        @Override
        public void applyToView(View view) {
            if (view instanceof StatusBarIconView) {
                StatusBarIconView icon = (StatusBarIconView) view;
                AnimationProperties animationProperties = DOT_ANIMATION_PROPERTIES;
                boolean animate = false;
                AnimationProperties animationProperties = null;
                if (justAdded) {
                    super.applyToView(icon);
                    icon.setAlpha(0.0f);
                    icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, false /* animate */);
                    animationProperties = ADD_ICON_PROPERTIES;
                    animate = true;
                } else if (visibleState != icon.getVisibleState()) {
                    animationProperties = DOT_ANIMATION_PROPERTIES;
                    animate = true;
                }
                boolean animate = visibleState != icon.getVisibleState() || justAdded;
                if (!animate && mAnimationStartIndex >= 0
                if (!animate && mAddAnimationStartIndex >= 0
                        && indexOfChild(view) >= mAddAnimationStartIndex
                        && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
                            || visibleState != StatusBarIconView.STATE_HIDDEN)) {
                    int viewIndex = indexOfChild(view);
                    animate = viewIndex >= mAnimationStartIndex;
                    animationProperties = DOT_ANIMATION_PROPERTIES;
                    animate = true;
                }
                if (needsCannedAnimation) {
                    AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
                    animationFilter.reset();
                    animationFilter.combineFilter(ICON_ANIMATION_PROPERTIES.getAnimationFilter());
                    if (animationProperties != null) {
                        animationFilter.combineFilter(animationProperties.getAnimationFilter());
                    }
                    animationProperties = mTempProperties;
                    animationProperties.setDuration(CANNED_ANIMATION_DURATION);
                    animate = true;
                    mCannedAnimationStartIndex = indexOfChild(view);
                }
                if (!animate && mCannedAnimationStartIndex >= 0
                        && indexOfChild(view) > mCannedAnimationStartIndex
                        && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
                        || visibleState != StatusBarIconView.STATE_HIDDEN)) {
                    AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
                    animationFilter.reset();
                    animationFilter.animateX();
                    animationProperties = mTempProperties;
                    animationProperties.setDuration(CANNED_ANIMATION_DURATION);
                    animate = true;
                }
                icon.setVisibleState(visibleState);
                if (animate) {
@@ -336,6 +395,13 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
                }
            }
            justAdded = false;
            needsCannedAnimation = false;
        }

        protected void onYTranslationAnimationFinished(View view) {
            if (hidden) {
                view.setVisibility(INVISIBLE);
            }
        }
    }
}
Loading