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

Commit 05e46ca8 authored by Winson's avatar Winson
Browse files

Updating paging animation to spec.



- To handle the specific animation spec, we just animate the views
  manually instead of animating the stack scroll (like how we do when
  swiping to dismiss)
- Fixing a regression in settings the initial focused index when
  alt-tabbing
- Minor tweak to make the front most task smaller when in the initial
  non-paging mode

Change-Id: Ic5fd54500fd8ce8284c7aaeddb102b2291bcecac

Signed-off-by: default avatarWinson <winsonc@google.com>
parent 42329522
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -66,8 +66,9 @@ public class RecentsActivityLaunchState {
     */
    public int getInitialFocusTaskIndex(int numTasks) {
        RecentsDebugFlags debugFlags = Recents.getDebugFlags();
        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
        if (launchedFromAppWithThumbnail) {
            if (debugFlags.isFastToggleRecentsEnabled()) {
            if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled()) {
                // If fast toggling, focus the front most task so that the next tap will focus the
                // N-1 task
                return numTasks - 1;
@@ -76,7 +77,7 @@ public class RecentsActivityLaunchState {
            // If coming from another app, focus the next task
            return numTasks - 2;
        } else {
            if (debugFlags.isFastToggleRecentsEnabled()) {
            if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled()) {
                // If fast toggling, defer focusing until the next tap (which will automatically
                // focus the front most task)
                return -1;
+2 −0
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@ public class Utilities {
                }
            };

    public static final RectFEvaluator RECTF_EVALUATOR = new RectFEvaluator();

    /**
     * @return the first parent walking up the view hierarchy that has the given class type.
     *
+99 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.content.res.Resources;
import android.graphics.Path;
import android.graphics.RectF;
import android.view.View;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;

import com.android.systemui.Interpolators;
@@ -34,6 +35,7 @@ import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;

import java.util.ArrayList;
import java.util.List;

/**
@@ -81,9 +83,18 @@ public class TaskStackAnimationHelper {
    private static final PathInterpolator EXIT_TO_HOME_ALPHA_INTERPOLATOR =
            new PathInterpolator(0.4f, 0, 1f, 1f);

    private static final PathInterpolator FOCUS_NEXT_TASK_INTERPOLATOR =
            new PathInterpolator(0.4f, 0, 0, 1f);
    private static final PathInterpolator FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR =
            new PathInterpolator(0, 0, 0, 1f);
    private static final PathInterpolator FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR =
            new PathInterpolator(0.4f, 0, 0.2f, 1f);

    private TaskStackView mStackView;

    private TaskViewTransform mTmpTransform = new TaskViewTransform();
    private ArrayList<TaskViewTransform> mTmpCurrentTaskTransforms = new ArrayList<>();
    private ArrayList<TaskViewTransform> mTmpFinalTaskTransforms = new ArrayList<>();

    public TaskStackAnimationHelper(Context context, TaskStackView stackView) {
        mStackView = stackView;
@@ -418,4 +429,92 @@ public class TaskStackAnimationHelper {
            mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
        }
    }

    /**
     * Starts the animation to focus the next {@link TaskView} when paging through recents.
     *
     * @return whether or not this will trigger a scroll in the stack
     */
    public boolean startScrollToFocusedTaskAnimation(Task newFocusedTask,
            boolean requestViewFocus) {
        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
        TaskStackViewScroller stackScroller = mStackView.getScroller();
        TaskStack stack = mStackView.getStack();

        final float newScroll = stackLayout.getStackScrollForTask(newFocusedTask);
        boolean willScrollToFront = newScroll > stackScroller.getStackScroll();
        boolean willScroll = Float.compare(newScroll, stackScroller.getStackScroll()) != 0;

        // Get the current set of task transforms
        ArrayList<Task> stackTasks = stack.getStackTasks();
        mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);

        // Pick up the newly visible views after the scroll
        mStackView.bindVisibleTaskViews(newScroll);

        // Update the internal state
        stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_FOCUSED);
        stackScroller.setStackScroll(newScroll, null /* animation */);
        mStackView.cancelDeferredTaskViewLayoutAnimation();

        // Get the final set of task transforms
        mStackView.getLayoutTaskTransforms(newScroll, stackTasks, mTmpFinalTaskTransforms);

        // Focus the task view
        TaskView newFocusedTaskView = mStackView.getChildViewForTask(newFocusedTask);
        newFocusedTaskView.setFocusedState(true, requestViewFocus);

        // Setup the end listener to return all the hidden views to the view pool after the
        // focus animation
        AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mStackView.bindVisibleTaskViews(newScroll);
            }
        };

        List<TaskView> taskViews = mStackView.getTaskViews();
        int taskViewCount = taskViews.size();
        int newFocusTaskViewIndex = taskViews.indexOf(newFocusedTaskView);
        for (int i = 0; i < taskViewCount; i++) {
            TaskView tv = taskViews.get(i);
            Task task = tv.getTask();

            if (mStackView.isIgnoredTask(task)) {
                continue;
            }

            int taskIndex = stackTasks.indexOf(task);
            TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
            TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);

            // Update the task to the initial state (for the newly picked up tasks)
            mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);

            int duration;
            Interpolator interpolator;
            if (willScrollToFront) {
                duration = Math.max(100, 100 + ((i - 1) * 50));
                interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
            } else {
                if (i < newFocusTaskViewIndex) {
                    duration = 150 + ((newFocusTaskViewIndex - i - 1) * 50);
                    interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
                } else if (i > newFocusTaskViewIndex) {
                    duration = Math.max(100, 150 - ((i - newFocusTaskViewIndex - 1) * 50));
                    interpolator = FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR;
                } else {
                    duration = 200;
                    interpolator = FOCUS_NEXT_TASK_INTERPOLATOR;
                }
            }

            AnimationProps anim = new AnimationProps()
                    .setDuration(AnimationProps.BOUNDS, duration)
                    .setInterpolator(AnimationProps.BOUNDS, interpolator)
                    .setListener(endListener);
            mStackView.updateTaskViewToTransform(tv, toTransform, anim);
        }
        return willScroll;
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -457,7 +457,7 @@ public class TaskStackLayoutAlgorithm {
                            launchTaskIndex - 1));
                }
            } else {
                float offsetPct = (float) (mTaskRect.height() / 2) / mStackRect.height();
                float offsetPct = (float) (mTaskRect.height() / 3) / mStackRect.height();
                float normX = mUnfocusedCurveInterpolator.getX(offsetPct);
                mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP,
                        launchTaskIndex - mUnfocusedRange.getAbsoluteX(normX)));
+39 −40
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.ComponentName;
@@ -41,6 +43,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;

import com.android.internal.logging.MetricsLogger;
@@ -115,6 +119,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal

    private static final ArraySet<Task.TaskKey> EMPTY_TASK_SET = new ArraySet<>();

    LayoutInflater mInflater;
    TaskStack mStack;
    TaskStackLayoutAlgorithm mLayoutAlgorithm;
    TaskStackViewScroller mStackScroller;
@@ -144,16 +149,15 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
    boolean mScreenPinningEnabled;

    // The stable stack bounds are the full bounds that we were measured with from RecentsView
    Rect mStableStackBounds = new Rect();
    private Rect mStableStackBounds = new Rect();
    // The current stack bounds are dynamic and may change as the user drags and drops
    Rect mStackBounds = new Rect();
    private Rect mStackBounds = new Rect();

    int[] mTmpVisibleRange = new int[2];
    Rect mTmpRect = new Rect();
    ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
    List<TaskView> mTmpTaskViews = new ArrayList<>();
    TaskViewTransform mTmpTransform = new TaskViewTransform();
    LayoutInflater mInflater;
    private int[] mTmpVisibleRange = new int[2];
    private Rect mTmpRect = new Rect();
    private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
    private List<TaskView> mTmpTaskViews = new ArrayList<>();
    private TaskViewTransform mTmpTransform = new TaskViewTransform();

    // A convenience update listener to request updating clipping of tasks
    private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
@@ -398,6 +402,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
        int frontMostVisibleIndex = -1;
        int backMostVisibleIndex = -1;
        boolean useTargetStackScroll = Float.compare(curStackScroll, targetStackScroll) != 0;
        boolean targetScrollIsInFront = targetStackScroll > curStackScroll;

        // We can reuse the task transforms where possible to reduce object allocation
        Utilities.matchTaskListSize(tasks, taskTransforms);
@@ -441,7 +446,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
                    frontMostVisibleIndex = i;
                }
                backMostVisibleIndex = i;
            } else {
            } else if (!targetScrollIsInFront) {
                if (backMostVisibleIndex != -1) {
                    // We've reached the end of the visible range, so going down the rest of the
                    // stack, we can just reset the transforms accordingly
@@ -533,7 +538,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
            }

            // Skip the invisible non-freeform stack tasks
            if (i > visibleStackRange[0] && !task.isFreeformTask()) {
            if (!task.isFreeformTask() && !transform.visible) {
                continue;
            }

@@ -673,11 +678,19 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
        for (int i = tasks.size() - 1; i >= 0; i--) {
            Task task = tasks.get(i);
            TaskViewTransform transform = transformsOut.get(i);
            mLayoutAlgorithm.getStackTransform(task, stackScroll, transform, null);
            mLayoutAlgorithm.getStackTransform(task, stackScroll, transform, null,
                    true /* forceUpdate */);
            transform.visible = true;
        }
    }

    /**
     * Cancels the next deferred task view layout.
     */
    void cancelDeferredTaskViewLayoutAnimation() {
        mDeferredTaskViewLayoutAnimation = null;
    }

    /**
     * Cancels all {@link TaskView} animations.
     *
@@ -718,7 +731,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
            TaskView frontTv = null;
            int clipBottom = 0;

            if (mIgnoreTasks.contains(tv.getTask().key)) {
            if (isIgnoredTask(tv.getTask())) {
                // For each of the ignore tasks, update the translationZ of its TaskView to be
                // between the translationZ of the tasks immediately underneath it
                if (prevVisibleTv != null) {
@@ -806,15 +819,15 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
    }

    /**
     * Sets the focused task to the provided (bounded taskIndex).
     * Sets the focused task to the provided (bounded focusTaskIndex).
     *
     * @return whether or not the stack will scroll as a part of this focus change
     */
    private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
            final boolean requestViewFocus, final int timerIndicatorDuration) {
    private boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
            boolean requestViewFocus, int timerIndicatorDuration) {
        // Find the next task to focus
        int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
                Math.max(0, Math.min(mStack.getTaskCount() - 1, taskIndex)) : -1;
                Math.max(0, Math.min(mStack.getTaskCount() - 1, focusTaskIndex)) : -1;
        final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
                mStack.getStackTasks().get(newFocusedTaskIndex) : null;

@@ -832,7 +845,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
        }

        boolean willScroll = false;

        mFocusedTask = newFocusedTask;

        if (newFocusedTask != null) {
@@ -847,33 +859,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
                }
            }

            Runnable focusTaskRunnable = new Runnable() {
                @Override
                public void run() {
                    final TaskView tv = getChildViewForTask(newFocusedTask);
                    if (tv != null) {
                        tv.setFocusedState(true, requestViewFocus);
                    }
                }
            };

            if (scrollToTask) {
                // Cancel any running enter animations at this point when we scroll or change focus
                if (!mEnterAnimationComplete) {
                    cancelAllTaskViewAnimations();
                }

                // TODO: Center the newly focused task view, only if not freeform
                float newScroll = mLayoutAlgorithm.getStackScrollForTask(newFocusedTask);
                if (Float.compare(newScroll, mStackScroller.getStackScroll()) != 0) {
                    mStackScroller.animateScroll(newScroll, focusTaskRunnable);
                    willScroll = true;
                willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
                        requestViewFocus);
            } else {
                    focusTaskRunnable.run();
                // Focus the task view
                TaskView newFocusedTaskView = getChildViewForTask(newFocusedTask);
                if (newFocusedTaskView != null) {
                    newFocusedTaskView.setFocusedState(true, requestViewFocus);
                }
                mLayoutAlgorithm.animateFocusState(TaskStackLayoutAlgorithm.STATE_FOCUSED);
            } else {
                focusTaskRunnable.run();
            }
        }
        return willScroll;
@@ -1278,7 +1277,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
            Task task = tasks.get(i);

            // Ignore deleting tasks
            if (mIgnoreTasks.contains(task.key)) {
            if (isIgnoredTask(task)) {
                if (i == tasks.size() - 1) {
                    isFrontMostTask.value = true;
                }
@@ -1392,7 +1391,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
    }

    @Override
    public void prepareViewToEnterPool(TaskView tv) {
    public void onReturnViewToPool(TaskView tv) {
        final Task task = tv.getTask();

        // Report that this tasks's data is no longer being used
@@ -1413,7 +1412,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
    }

    @Override
    public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) {
    public void onPickUpViewFromPool(TaskView tv, Task task, boolean isNewView) {
        // Find the index where this task should be placed in the stack
        int taskIndex = mStack.indexOfStackTask(task);
        int insertIndex = findTaskViewInsertIndex(task, taskIndex);
Loading