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

Commit 446cb95a authored by Omar Miatello's avatar Omar Miatello Committed by Android (Google) Code Review
Browse files

Merge "Define clear ownership for roundness SourceType" into tm-qpr-dev

parents 49480248 5022740b
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -83,8 +83,7 @@ object Flags {
    val SEMI_STABLE_SORT = unreleasedFlag(115, "semi_stable_sort", teamfood = true)

    @JvmField
    val NOTIFICATION_GROUP_CORNER =
        unreleasedFlag(116, "notification_group_corner", teamfood = true)
    val USE_ROUNDNESS_SOURCETYPES = unreleasedFlag(116, "use_roundness_sourcetype", teamfood = true)

    // TODO(b/259217907)
    @JvmField
+86 −64
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -66,6 +67,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
    // the next icon has translated out of the way, to avoid overlapping.
    private static final Interpolator ICON_ALPHA_INTERPOLATOR =
            new PathInterpolator(0.6f, 0f, 0.6f, 0f);
    private static final SourceType BASE_VALUE = SourceType.from("BaseValue");
    private static final SourceType SHELF_SCROLL = SourceType.from("ShelfScroll");

    private NotificationIconContainer mShelfIcons;
    private int[] mTmp = new int[2];
@@ -112,12 +115,14 @@ public class NotificationShelf extends ActivatableNotificationView implements
        setClipChildren(false);
        setClipToPadding(false);
        mShelfIcons.setIsStaticLayout(false);
        requestBottomRoundness(1.0f, /* animate = */ false, SourceType.DefaultValue);
        requestTopRoundness(1f, false, SourceType.DefaultValue);
        requestRoundness(/* top = */ 1f, /* bottom = */ 1f, BASE_VALUE, /* animate = */ false);

        // Setting this to first in section to get the clipping to the top roundness correct. This
        // value determines the way we are clipping to the top roundness of the overall shade
        if (!mUseRoundnessSourceTypes) {
            // Setting this to first in section to get the clipping to the top roundness correct.
            // This value determines the way we are clipping to the top roundness of the overall
            // shade
            setFirstInSection(true);
        }
        updateResources();
    }

@@ -125,6 +130,9 @@ public class NotificationShelf extends ActivatableNotificationView implements
                     NotificationStackScrollLayoutController hostLayoutController) {
        mAmbientState = ambientState;
        mHostLayoutController = hostLayoutController;
        hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> {
            child.requestRoundnessReset(SHELF_SCROLL);
        });
    }

    private void updateResources() {
@@ -185,7 +193,9 @@ public class NotificationShelf extends ActivatableNotificationView implements
                + " indexOfFirstViewInShelf=" + mIndexOfFirstViewInShelf + ')';
    }

    /** Update the state of the shelf. */
    /**
     * Update the state of the shelf.
     */
    public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState,
                            AmbientState ambientState) {
        ExpandableView lastView = ambientState.getLastVisibleBackgroundChild();
@@ -478,13 +488,15 @@ public class NotificationShelf extends ActivatableNotificationView implements
        }
    }

    private void updateCornerRoundnessOnScroll(ActivatableNotificationView anv, float viewStart,
    private void updateCornerRoundnessOnScroll(
            ActivatableNotificationView anv,
            float viewStart,
            float shelfStart) {

        final boolean isUnlockedHeadsUp = !mAmbientState.isOnKeyguard()
                && !mAmbientState.isShadeExpanded()
                && anv instanceof ExpandableNotificationRow
                && ((ExpandableNotificationRow) anv).isHeadsUp();
                && anv.isHeadsUp();

        final boolean isHunGoingToShade = mAmbientState.isShadeExpanded()
                && anv == mAmbientState.getTrackedHeadsUpRow();
@@ -506,41 +518,40 @@ public class NotificationShelf extends ActivatableNotificationView implements
                * mAmbientState.getExpansionFraction();
        final float cornerAnimationTop = shelfStart - cornerAnimationDistance;

        if (viewEnd >= cornerAnimationTop) {
            // Round bottom corners within animation bounds
            final float changeFraction = MathUtils.saturate(
                    (viewEnd - cornerAnimationTop) / cornerAnimationDistance);
            anv.requestBottomRoundness(
                    /* value = */ anv.isLastInSection() ? 1f : changeFraction,
                    /* animate = */ false,
                    SourceType.OnScroll);

        } else if (viewEnd < cornerAnimationTop) {
            // Fast scroll skips frames and leaves corners with unfinished rounding.
            // Reset top and bottom corners outside of animation bounds.
            anv.requestBottomRoundness(
                    /* value = */ anv.isLastInSection() ? 1f : 0f,
                    /* animate = */ false,
                    SourceType.OnScroll);
        final SourceType sourceType;
        if (mUseRoundnessSourceTypes) {
            sourceType = SHELF_SCROLL;
        } else {
            sourceType = LegacySourceType.OnScroll;
        }

        if (viewStart >= cornerAnimationTop) {
        final float topValue;
        if (!mUseRoundnessSourceTypes && anv.isFirstInSection()) {
            topValue = 1f;
        } else if (viewStart >= cornerAnimationTop) {
            // Round top corners within animation bounds
            final float changeFraction = MathUtils.saturate(
            topValue = MathUtils.saturate(
                    (viewStart - cornerAnimationTop) / cornerAnimationDistance);
            anv.requestTopRoundness(
                    /* value = */ anv.isFirstInSection() ? 1f : changeFraction,
                    /* animate = */ false,
                    SourceType.OnScroll);
        } else {
            // Fast scroll skips frames and leaves corners with unfinished rounding.
            // Reset top and bottom corners outside of animation bounds.
            topValue = 0f;
        }
        anv.requestTopRoundness(topValue, sourceType, /* animate = */ false);

        } else if (viewStart < cornerAnimationTop) {
        final float bottomValue;
        if (!mUseRoundnessSourceTypes && anv.isLastInSection()) {
            bottomValue = 1f;
        } else if (viewEnd >= cornerAnimationTop) {
            // Round bottom corners within animation bounds
            bottomValue = MathUtils.saturate(
                    (viewEnd - cornerAnimationTop) / cornerAnimationDistance);
        } else {
            // Fast scroll skips frames and leaves corners with unfinished rounding.
            // Reset top and bottom corners outside of animation bounds.
            anv.requestTopRoundness(
                    /* value = */ anv.isFirstInSection() ? 1f : 0f,
                    /* animate = */ false,
                    SourceType.OnScroll);
            bottomValue = 0f;
        }
        anv.requestBottomRoundness(bottomValue, sourceType, /* animate = */ false);
    }

    /**
@@ -626,6 +637,7 @@ public class NotificationShelf extends ActivatableNotificationView implements

    /**
     * Update the clipping of this view.
     *
     * @return the amount that our own top should be clipped
     */
    private int updateNotificationClipHeight(ExpandableView view,
@@ -675,8 +687,14 @@ public class NotificationShelf extends ActivatableNotificationView implements
     * 0f is not in shelf. 1f is completely in shelf.
     */
    @VisibleForTesting
    public float getAmountInShelf(int i, ExpandableView view, boolean scrollingFast,
            boolean expandingAnimated, boolean isLastChild, float shelfClipStart) {
    public float getAmountInShelf(
            int i,
            ExpandableView view,
            boolean scrollingFast,
            boolean expandingAnimated,
            boolean isLastChild,
            float shelfClipStart
    ) {

        // Let's calculate how much the view is in the shelf
        float viewStart = view.getTranslationY();
@@ -755,8 +773,13 @@ public class NotificationShelf extends ActivatableNotificationView implements
        return start;
    }

    private void updateIconPositioning(ExpandableView view, float iconTransitionAmount,
            boolean scrollingFast, boolean expandingAnimated, boolean isLastChild) {
    private void updateIconPositioning(
            ExpandableView view,
            float iconTransitionAmount,
            boolean scrollingFast,
            boolean expandingAnimated,
            boolean isLastChild
    ) {
        StatusBarIconView icon = view.getShelfIcon();
        NotificationIconContainer.IconState iconState = getIconState(icon);
        if (iconState == null) {
@@ -981,12 +1004,11 @@ public class NotificationShelf extends ActivatableNotificationView implements

    /**
     * This method resets the OnScroll roundness of a view to 0f
     *
     * <p>
     * Note: This should be the only class that handles roundness {@code SourceType.OnScroll}
     */
    public static void resetOnScrollRoundness(ExpandableView expandableView) {
        expandableView.requestTopRoundness(0f, false, SourceType.OnScroll);
        expandableView.requestBottomRoundness(0f, false, SourceType.OnScroll);
    public static void resetLegacyOnScrollRoundness(ExpandableView expandableView) {
        expandableView.requestRoundnessReset(LegacySourceType.OnScroll);
    }

    public class ShelfState extends ExpandableViewState {
+8 −3
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar;

import android.view.View;

import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
@@ -42,14 +44,17 @@ public class NotificationShelfController {
    private AmbientState mAmbientState;

    @Inject
    public NotificationShelfController(NotificationShelf notificationShelf,
    public NotificationShelfController(
            NotificationShelf notificationShelf,
            ActivatableNotificationViewController activatableNotificationViewController,
            KeyguardBypassController keyguardBypassController,
            SysuiStatusBarStateController statusBarStateController) {
            SysuiStatusBarStateController statusBarStateController,
            FeatureFlags featureFlags) {
        mView = notificationShelf;
        mActivatableNotificationViewController = activatableNotificationViewController;
        mKeyguardBypassController = keyguardBypassController;
        mStatusBarStateController = statusBarStateController;
        mView.useRoundnessSourceTypes(featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES));
        mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() {
            @Override
            public void onViewAttachedToWindow(View v) {
@@ -88,7 +93,7 @@ public class NotificationShelfController {

    public @View.Visibility int getVisibility() {
        return mView.getVisibility();
    };
    }

    public void setCollapsedIcons(NotificationIconContainer notificationIcons) {
        mView.setCollapsedIcons(notificationIcons);
+156 −8
Original line number Diff line number Diff line
@@ -74,8 +74,8 @@ interface Roundable {
    @JvmDefault
    fun requestTopRoundness(
        @FloatRange(from = 0.0, to = 1.0) value: Float,
        animate: Boolean,
        sourceType: SourceType,
        animate: Boolean,
    ): Boolean {
        val roundnessMap = roundableState.topRoundnessMap
        val lastValue = roundnessMap.values.maxOrNull() ?: 0f
@@ -104,6 +104,30 @@ interface Roundable {
        return false
    }

    /**
     * Request the top roundness [value] for a specific [sourceType]. Animate the roundness if the
     * view is shown.
     *
     * The top roundness of a [Roundable] can be defined by different [sourceType]. In case more
     * origins require different roundness, for the same property, the maximum value will always be
     * chosen.
     *
     * @param value a value between 0f and 1f.
     * @param sourceType the source from which the request for roundness comes.
     * @return Whether the roundness was changed.
     */
    @JvmDefault
    fun requestTopRoundness(
        @FloatRange(from = 0.0, to = 1.0) value: Float,
        sourceType: SourceType,
    ): Boolean {
        return requestTopRoundness(
            value = value,
            sourceType = sourceType,
            animate = roundableState.targetView.isShown
        )
    }

    /**
     * Request the bottom roundness [value] for a specific [sourceType].
     *
@@ -119,8 +143,8 @@ interface Roundable {
    @JvmDefault
    fun requestBottomRoundness(
        @FloatRange(from = 0.0, to = 1.0) value: Float,
        animate: Boolean,
        sourceType: SourceType,
        animate: Boolean,
    ): Boolean {
        val roundnessMap = roundableState.bottomRoundnessMap
        val lastValue = roundnessMap.values.maxOrNull() ?: 0f
@@ -149,9 +173,101 @@ interface Roundable {
        return false
    }

    /**
     * Request the bottom roundness [value] for a specific [sourceType]. Animate the roundness if
     * the view is shown.
     *
     * The bottom roundness of a [Roundable] can be defined by different [sourceType]. In case more
     * origins require different roundness, for the same property, the maximum value will always be
     * chosen.
     *
     * @param value value between 0f and 1f.
     * @param sourceType the source from which the request for roundness comes.
     * @return Whether the roundness was changed.
     */
    @JvmDefault
    fun requestBottomRoundness(
        @FloatRange(from = 0.0, to = 1.0) value: Float,
        sourceType: SourceType,
    ): Boolean {
        return requestBottomRoundness(
            value = value,
            sourceType = sourceType,
            animate = roundableState.targetView.isShown
        )
    }

    /**
     * Request the roundness [value] for a specific [sourceType].
     *
     * The top/bottom roundness of a [Roundable] can be defined by different [sourceType]. In case
     * more origins require different roundness, for the same property, the maximum value will
     * always be chosen.
     *
     * @param top top value between 0f and 1f.
     * @param bottom bottom value between 0f and 1f.
     * @param sourceType the source from which the request for roundness comes.
     * @param animate true if it should animate to that value.
     * @return Whether the roundness was changed.
     */
    @JvmDefault
    fun requestRoundness(
        @FloatRange(from = 0.0, to = 1.0) top: Float,
        @FloatRange(from = 0.0, to = 1.0) bottom: Float,
        sourceType: SourceType,
        animate: Boolean,
    ): Boolean {
        val hasTopChanged =
            requestTopRoundness(value = top, sourceType = sourceType, animate = animate)
        val hasBottomChanged =
            requestBottomRoundness(value = bottom, sourceType = sourceType, animate = animate)
        return hasTopChanged || hasBottomChanged
    }

    /**
     * Request the roundness [value] for a specific [sourceType]. Animate the roundness if the view
     * is shown.
     *
     * The top/bottom roundness of a [Roundable] can be defined by different [sourceType]. In case
     * more origins require different roundness, for the same property, the maximum value will
     * always be chosen.
     *
     * @param top top value between 0f and 1f.
     * @param bottom bottom value between 0f and 1f.
     * @param sourceType the source from which the request for roundness comes.
     * @return Whether the roundness was changed.
     */
    @JvmDefault
    fun requestRoundness(
        @FloatRange(from = 0.0, to = 1.0) top: Float,
        @FloatRange(from = 0.0, to = 1.0) bottom: Float,
        sourceType: SourceType,
    ): Boolean {
        return requestRoundness(
            top = top,
            bottom = bottom,
            sourceType = sourceType,
            animate = roundableState.targetView.isShown,
        )
    }

    /**
     * Request the roundness 0f for a [SourceType]. Animate the roundness if the view is shown.
     *
     * The top/bottom roundness of a [Roundable] can be defined by different [sourceType]. In case
     * more origins require different roundness, for the same property, the maximum value will
     * always be chosen.
     *
     * @param sourceType the source from which the request for roundness comes.
     */
    @JvmDefault
    fun requestRoundnessReset(sourceType: SourceType) {
        requestRoundness(top = 0f, bottom = 0f, sourceType = sourceType)
    }

    /** Apply the roundness changes, usually means invalidate the [RoundableState.targetView]. */
    @JvmDefault
    fun applyRoundness() {
    fun applyRoundnessAndInvalidate() {
        roundableState.targetView.invalidate()
    }

@@ -227,7 +343,7 @@ class RoundableState(
    /** Set the current top roundness */
    internal fun setTopRoundness(
        value: Float,
        animated: Boolean = targetView.isShown,
        animated: Boolean,
    ) {
        PropertyAnimator.setProperty(targetView, topAnimatable, value, DURATION, animated)
    }
@@ -235,11 +351,19 @@ class RoundableState(
    /** Set the current bottom roundness */
    internal fun setBottomRoundness(
        value: Float,
        animated: Boolean = targetView.isShown,
        animated: Boolean,
    ) {
        PropertyAnimator.setProperty(targetView, bottomAnimatable, value, DURATION, animated)
    }

    fun debugString() = buildString {
        append("TargetView: ${targetView.hashCode()} ")
        append("Top: $topRoundness ")
        append(topRoundnessMap.map { "${it.key} ${it.value}" })
        append(" Bottom: $bottomRoundness ")
        append(bottomRoundnessMap.map { "${it.key} ${it.value}" })
    }

    companion object {
        private val DURATION: AnimationProperties =
            AnimationProperties()
@@ -252,7 +376,7 @@ class RoundableState(

                    override fun setValue(view: View, value: Float) {
                        roundable.roundableState.topRoundness = value
                        roundable.applyRoundness()
                        roundable.applyRoundnessAndInvalidate()
                    }
                },
                R.id.top_roundess_animator_tag,
@@ -267,7 +391,7 @@ class RoundableState(

                    override fun setValue(view: View, value: Float) {
                        roundable.roundableState.bottomRoundness = value
                        roundable.applyRoundness()
                        roundable.applyRoundnessAndInvalidate()
                    }
                },
                R.id.bottom_roundess_animator_tag,
@@ -277,7 +401,31 @@ class RoundableState(
    }
}

enum class SourceType {
/**
 * Interface used to define the owner of a roundness. Usually the [SourceType] is defined as a
 * private property of a class.
 */
interface SourceType {
    companion object {
        /**
         * This is the most convenient way to define a new [SourceType].
         *
         * For example:
         *
         * ```kotlin
         *     private val SECTION = SourceType.from("Section")
         * ```
         */
        @JvmStatic
        fun from(name: String) =
            object : SourceType {
                override fun toString() = name
            }
    }
}

@Deprecated("Use SourceType.from() instead", ReplaceWith("SourceType.from()"))
enum class LegacySourceType : SourceType {
    DefaultValue,
    OnDismissAnimation,
    OnScroll,
+35 −2
Original line number Diff line number Diff line
@@ -39,9 +39,13 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;

import java.util.HashSet;
import java.util.Set;

/**
 * Base class for both {@link ExpandableNotificationRow} and {@link NotificationShelf}
 * to implement dimming/activating on Keyguard for the double-tap gesture
@@ -91,6 +95,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
            = new PathInterpolator(0.6f, 0, 0.5f, 1);
    private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
            = new PathInterpolator(0, 0, 0.5f, 1);
    private final Set<SourceType> mOnDetachResetRoundness = new HashSet<>();
    private int mTintedRippleColor;
    private int mNormalRippleColor;
    private Gefingerpoken mTouchHandler;
@@ -134,6 +139,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
    private boolean mDismissed;
    private boolean mRefocusOnDismiss;
    private AccessibilityManager mAccessibilityManager;
    protected boolean mUseRoundnessSourceTypes;

    public ActivatableNotificationView(Context context, AttributeSet attrs) {
        super(context, attrs);
@@ -613,9 +619,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
    protected void resetAllContentAlphas() {}

    @Override
    public void applyRoundness() {
        super.applyRoundness();
    public void applyRoundnessAndInvalidate() {
        applyBackgroundRoundness(getTopCornerRadius(), getBottomCornerRadius());
        super.applyRoundnessAndInvalidate();
    }

    @Override
@@ -775,6 +781,33 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
        mAccessibilityManager = accessibilityManager;
    }

    /**
     * Enable the support for rounded corner based on the SourceType
     * @param enabled true if is supported
     */
    public void useRoundnessSourceTypes(boolean enabled) {
        mUseRoundnessSourceTypes = enabled;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mUseRoundnessSourceTypes && !mOnDetachResetRoundness.isEmpty()) {
            for (SourceType sourceType : mOnDetachResetRoundness) {
                requestRoundnessReset(sourceType);
            }
            mOnDetachResetRoundness.clear();
        }
    }

    /**
     * SourceType which should be reset when this View is detached
     * @param sourceType will be reset on View detached
     */
    public void addOnDetachResetRoundness(SourceType sourceType) {
        mOnDetachResetRoundness.add(sourceType);
    }

    public interface OnActivatedListener {
        void onActivated(ActivatableNotificationView view);
        void onActivationReset(ActivatableNotificationView view);
Loading