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

Commit f7025bf4 authored by Johannes Gallmann's avatar Johannes Gallmann Committed by Automerger Merge Worker
Browse files

Merge "Polish RemoteInputView defocus animation" into tm-qpr-dev am: ba935418 am: e39bf513

parents 1a82e746 e39bf513
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -65,8 +65,6 @@ import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.util.DumpUtilsKt;
import com.android.systemui.util.ListenerSet;

import dagger.Lazy;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -74,6 +72,8 @@ import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;

import dagger.Lazy;

/**
 * Class for handling remote input state over a set of notifications. This class handles things
 * like keeping notifications temporarily that were cancelled as a response to a remote input
@@ -465,8 +465,7 @@ public class NotificationRemoteInputManager implements Dumpable {
        riv.getController().setRemoteInput(input);
        riv.getController().setRemoteInputs(inputs);
        riv.getController().setEditedSuggestionInfo(editedSuggestionInfo);
        ViewGroup parent = view.getParent() != null ? (ViewGroup) view.getParent() : null;
        riv.focusAnimated(parent);
        riv.focusAnimated();
        if (userMessageContent != null) {
            riv.setEditTextContent(userMessageContent);
        }
+77 −38
Original line number Diff line number Diff line
@@ -109,6 +109,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
    private static final long FOCUS_ANIMATION_FADE_IN_DELAY = 33;
    private static final long FOCUS_ANIMATION_FADE_IN_DURATION = 83;
    private static final float FOCUS_ANIMATION_MIN_SCALE = 0.5f;
    private static final long DEFOCUS_ANIMATION_FADE_OUT_DELAY = 120;
    private static final long DEFOCUS_ANIMATION_CROSSFADE_DELAY = 180;

    public final Object mToken = new Object();

@@ -421,7 +423,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
    }

    @VisibleForTesting
    void onDefocus(boolean animate, boolean logClose) {
    void onDefocus(boolean animate, boolean logClose, @Nullable Runnable doAfterDefocus) {
        mController.removeRemoteInput(mEntry, mToken);
        mEntry.remoteInputText = mEditText.getText();

@@ -431,18 +433,20 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
            ViewGroup parent = (ViewGroup) getParent();
            if (animate && parent != null && mIsFocusAnimationFlagActive) {


                ViewGroup grandParent = (ViewGroup) parent.getParent();
                ViewGroupOverlay overlay = parent.getOverlay();
                View actionsContainer = getActionsContainerLayout();
                int actionsContainerHeight =
                        actionsContainer != null ? actionsContainer.getHeight() : 0;

                // After adding this RemoteInputView to the overlay of the parent (and thus removing
                // it from the parent itself), the parent will shrink in height. This causes the
                // overlay to be moved. To correct the position of the overlay we need to offset it.
                int overlayOffsetY = getMaxSiblingHeight() - getHeight();
                int overlayOffsetY = actionsContainerHeight - getHeight();
                overlay.add(this);
                if (grandParent != null) grandParent.setClipChildren(false);

                Animator animator = getDefocusAnimator(overlayOffsetY);
                Animator animator = getDefocusAnimator(actionsContainer, overlayOffsetY);
                View self = this;
                animator.addListener(new AnimatorListenerAdapter() {
                    @Override
@@ -454,8 +458,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
                        if (mWrapper != null) {
                            mWrapper.setRemoteInputVisible(false);
                        }
                        if (doAfterDefocus != null) {
                            doAfterDefocus.run();
                        }
                    }
                });
                if (actionsContainer != null) actionsContainer.setAlpha(0f);
                animator.start();

            } else if (animate && mRevealParams != null && mRevealParams.radius > 0) {
@@ -474,6 +482,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
                reveal.start();
            } else {
                setVisibility(GONE);
                if (doAfterDefocus != null) doAfterDefocus.run();
                if (mWrapper != null) {
                    mWrapper.setRemoteInputVisible(false);
                }
@@ -596,10 +605,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene

    /**
     * Focuses the RemoteInputView and animates its appearance
     *
     * @param crossFadeView view that will be crossfaded during the appearance animation
     */
    public void focusAnimated(View crossFadeView) {
    public void focusAnimated() {
        if (!mIsFocusAnimationFlagActive && getVisibility() != VISIBLE
                && mRevealParams != null) {
            android.animation.Animator animator = mRevealParams.createCircularRevealAnimator(this);
@@ -609,7 +616,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
        } else if (mIsFocusAnimationFlagActive && getVisibility() != VISIBLE) {
            mIsAnimatingAppearance = true;
            setAlpha(0f);
            Animator focusAnimator = getFocusAnimator(crossFadeView);
            Animator focusAnimator = getFocusAnimator(getActionsContainerLayout());
            focusAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation, boolean isReverse) {
@@ -661,6 +668,23 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
    }

    private void reset() {
        if (mIsFocusAnimationFlagActive) {
            mProgressBar.setVisibility(INVISIBLE);
            mResetting = true;
            mSending = false;
            onDefocus(true /* animate */, false /* logClose */, () -> {
                mEntry.remoteInputTextWhenReset = SpannedString.valueOf(mEditText.getText());
                mEditText.getText().clear();
                mEditText.setEnabled(isAggregatedVisible());
                mSendButton.setVisibility(VISIBLE);
                mController.removeSpinning(mEntry.getKey(), mToken);
                updateSendButton();
                setAttachment(null);
                mResetting = false;
            });
            return;
        }

        mResetting = true;
        mSending = false;
        mEntry.remoteInputTextWhenReset = SpannedString.valueOf(mEditText.getText());
@@ -671,7 +695,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
        mProgressBar.setVisibility(INVISIBLE);
        mController.removeSpinning(mEntry.getKey(), mToken);
        updateSendButton();
        onDefocus(false /* animate */, false /* logClose */);
        onDefocus(false /* animate */, false /* logClose */, null /* doAfterDefocus */);
        setAttachment(null);

        mResetting = false;
@@ -825,23 +849,22 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
    }

    /**
     * @return max sibling height (0 in case of no siblings)
     * @return action button container view (i.e. ViewGroup containing Reply button etc.)
     */
    public int getMaxSiblingHeight() {
    public View getActionsContainerLayout() {
        ViewGroup parentView = (ViewGroup) getParent();
        int maxHeight = 0;
        if (parentView == null) return 0;
        for (int i = 0; i < parentView.getChildCount(); i++) {
            View siblingView = parentView.getChildAt(i);
            if (siblingView != this) maxHeight = Math.max(maxHeight, siblingView.getHeight());
        }
        return maxHeight;
        if (parentView == null) return null;
        return parentView.findViewById(com.android.internal.R.id.actions_container_layout);
    }

    /**
     * Creates an animator for the focus animation.
     *
     * @param fadeOutView View that will be faded out during the focus animation.
     */
    private Animator getFocusAnimator(View crossFadeView) {
    private Animator getFocusAnimator(@Nullable View fadeOutView) {
        final AnimatorSet animatorSet = new AnimatorSet();

        final Animator alphaAnimator = ObjectAnimator.ofFloat(this, View.ALPHA, 0f, 1f);
        alphaAnimator.setStartDelay(FOCUS_ANIMATION_FADE_IN_DELAY);
        alphaAnimator.setDuration(FOCUS_ANIMATION_FADE_IN_DURATION);
@@ -854,30 +877,36 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
        scaleAnimator.setDuration(FOCUS_ANIMATION_TOTAL_DURATION);
        scaleAnimator.setInterpolator(InterpolatorsAndroidX.FAST_OUT_SLOW_IN);

        final Animator crossFadeViewAlphaAnimator =
                ObjectAnimator.ofFloat(crossFadeView, View.ALPHA, 1f, 0f);
        crossFadeViewAlphaAnimator.setDuration(FOCUS_ANIMATION_CROSSFADE_DURATION);
        crossFadeViewAlphaAnimator.setInterpolator(InterpolatorsAndroidX.LINEAR);
        alphaAnimator.addListener(new AnimatorListenerAdapter() {
        if (fadeOutView == null) {
            animatorSet.playTogether(alphaAnimator, scaleAnimator);
        } else {
            final Animator fadeOutViewAlphaAnimator =
                    ObjectAnimator.ofFloat(fadeOutView, View.ALPHA, 1f, 0f);
            fadeOutViewAlphaAnimator.setDuration(FOCUS_ANIMATION_CROSSFADE_DURATION);
            fadeOutViewAlphaAnimator.setInterpolator(InterpolatorsAndroidX.LINEAR);
            animatorSet.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation, boolean isReverse) {
                crossFadeView.setAlpha(1f);
                    fadeOutView.setAlpha(1f);
                }
            });

        final AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(alphaAnimator, scaleAnimator, crossFadeViewAlphaAnimator);
            animatorSet.playTogether(alphaAnimator, scaleAnimator, fadeOutViewAlphaAnimator);
        }
        return animatorSet;
    }

    /**
     * Creates an animator for the defocus animation.
     *
     * @param fadeInView View that will be faded in during the defocus animation.
     * @param offsetY    The RemoteInputView will be offset by offsetY during the animation
     */
    private Animator getDefocusAnimator(int offsetY) {
    private Animator getDefocusAnimator(@Nullable View fadeInView, int offsetY) {
        final AnimatorSet animatorSet = new AnimatorSet();

        final Animator alphaAnimator = ObjectAnimator.ofFloat(this, View.ALPHA, 1f, 0f);
        alphaAnimator.setDuration(FOCUS_ANIMATION_CROSSFADE_DURATION);
        alphaAnimator.setDuration(FOCUS_ANIMATION_FADE_IN_DURATION);
        alphaAnimator.setStartDelay(DEFOCUS_ANIMATION_FADE_OUT_DELAY);
        alphaAnimator.setInterpolator(InterpolatorsAndroidX.LINEAR);

        ValueAnimator scaleAnimator = ValueAnimator.ofFloat(1f, FOCUS_ANIMATION_MIN_SCALE);
@@ -893,8 +922,17 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
            }
        });

        final AnimatorSet animatorSet = new AnimatorSet();
        if (fadeInView == null) {
            animatorSet.playTogether(alphaAnimator, scaleAnimator);
        } else {
            fadeInView.forceHasOverlappingRendering(false);
            Animator fadeInViewAlphaAnimator =
                    ObjectAnimator.ofFloat(fadeInView, View.ALPHA, 0f, 1f);
            fadeInViewAlphaAnimator.setDuration(FOCUS_ANIMATION_FADE_IN_DURATION);
            fadeInViewAlphaAnimator.setInterpolator(InterpolatorsAndroidX.LINEAR);
            fadeInViewAlphaAnimator.setStartDelay(DEFOCUS_ANIMATION_CROSSFADE_DELAY);
            animatorSet.playTogether(alphaAnimator, scaleAnimator, fadeInViewAlphaAnimator);
        }
        return animatorSet;
    }

@@ -1011,7 +1049,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
            if (isFocusable() && isEnabled()) {
                setInnerFocusable(false);
                if (mRemoteInputView != null) {
                    mRemoteInputView.onDefocus(animate, true /* logClose */);
                    mRemoteInputView
                            .onDefocus(animate, true /* logClose */, null /* doAfterDefocus */);
                }
                mShowImeOnInputConnection = false;
            }
+24 −9
Original line number Diff line number Diff line
@@ -390,19 +390,27 @@ public class RemoteInputViewTest extends SysuiTestCase {
        bindController(view, row.getEntry());
        view.setVisibility(View.GONE);

        View crossFadeView = new View(mContext);
        View fadeOutView = new View(mContext);
        fadeOutView.setId(com.android.internal.R.id.actions_container_layout);

        // Start focus animation
        view.focusAnimated(crossFadeView);
        FrameLayout parent = new FrameLayout(mContext);
        parent.addView(view);
        parent.addView(fadeOutView);

        // Start focus animation
        view.focusAnimated();
        assertTrue(view.isAnimatingAppearance());

        // fast forward to 1 ms before end of animation and verify fadeOutView has alpha set to 0f
        mAnimatorTestRule.advanceTimeBy(ANIMATION_DURATION_STANDARD - 1);
        assertEquals(0f, fadeOutView.getAlpha());

        // fast forward to end of animation
        mAnimatorTestRule.advanceTimeBy(ANIMATION_DURATION_STANDARD);
        mAnimatorTestRule.advanceTimeBy(1);

        // assert that crossFadeView's alpha is reset to 1f after the animation (hidden behind
        // assert that fadeOutView's alpha is reset to 1f after the animation (hidden behind
        // RemoteInputView)
        assertEquals(1f, crossFadeView.getAlpha());
        assertEquals(1f, fadeOutView.getAlpha());
        assertFalse(view.isAnimatingAppearance());
        assertEquals(View.VISIBLE, view.getVisibility());
        assertEquals(1f, view.getAlpha());
@@ -415,20 +423,27 @@ public class RemoteInputViewTest extends SysuiTestCase {
                mDependency,
                TestableLooper.get(this));
        ExpandableNotificationRow row = helper.createRow();
        FrameLayout remoteInputViewParent = new FrameLayout(mContext);
        RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
        remoteInputViewParent.addView(view);
        bindController(view, row.getEntry());

        View fadeInView = new View(mContext);
        fadeInView.setId(com.android.internal.R.id.actions_container_layout);

        FrameLayout parent = new FrameLayout(mContext);
        parent.addView(view);
        parent.addView(fadeInView);

        // Start defocus animation
        view.onDefocus(true, false);
        view.onDefocus(true /* animate */, false /* logClose */, null /* doAfterDefocus */);
        assertEquals(View.VISIBLE, view.getVisibility());
        assertEquals(0f, fadeInView.getAlpha());

        // fast forward to end of animation
        mAnimatorTestRule.advanceTimeBy(ANIMATION_DURATION_STANDARD);

        // assert that RemoteInputView is no longer visible
        assertEquals(View.GONE, view.getVisibility());
        assertEquals(1f, fadeInView.getAlpha());
    }

    // NOTE: because we're refactoring the RemoteInputView and moving logic into the