OSDN Git Service

Updating paging animation to spec.
authorWinson <winsonc@google.com>
Fri, 5 Feb 2016 23:40:29 +0000 (15:40 -0800)
committerWinson <winsonc@google.com>
Tue, 9 Feb 2016 01:12:14 +0000 (17:12 -0800)
- 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: Winson <winsonc@google.com>
packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java

index f7ebd94..82e7861 100644 (file)
@@ -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;
index 4deea54..52043f4 100644 (file)
@@ -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.
      *
index 7eaa193..76972d7 100644 (file)
@@ -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;
+    }
 }
index bd37c3b..19ac1e7 100644 (file)
@@ -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)));
index 59bc120..fb3515a 100644 (file)
@@ -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,12 +678,20 @@ 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.
      *
      * @see #cancelAllTaskViewAnimations(ArraySet<Task.TaskKey>)
@@ -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;
-                } else {
-                    focusTaskRunnable.run();
-                }
-                mLayoutAlgorithm.animateFocusState(TaskStackLayoutAlgorithm.STATE_FOCUSED);
+                willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
+                        requestViewFocus);
             } else {
-                focusTaskRunnable.run();
+                // Focus the task view
+                TaskView newFocusedTaskView = getChildViewForTask(newFocusedTask);
+                if (newFocusedTaskView != null) {
+                    newFocusedTaskView.setFocusedState(true, requestViewFocus);
+                }
             }
         }
         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);
index c641d75..d1bce55 100644 (file)
@@ -191,21 +191,27 @@ public class TaskStackViewScroller {
         stopScroller();
         stopBoundScrollAnimation();
 
-        mFinalAnimatedScroll = newScroll;
-        mScrollAnimator = ObjectAnimator.ofFloat(this, STACK_SCROLL, getStackScroll(), newScroll);
-        mScrollAnimator.setDuration(mContext.getResources().getInteger(
-                R.integer.recents_animate_task_stack_scroll_duration));
-        mScrollAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        mScrollAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (postRunnable != null) {
-                    postRunnable.run();
+        if (Float.compare(mStackScrollP, newScroll) != 0) {
+            mFinalAnimatedScroll = newScroll;
+            mScrollAnimator = ObjectAnimator.ofFloat(this, STACK_SCROLL, getStackScroll(), newScroll);
+            mScrollAnimator.setDuration(mContext.getResources().getInteger(
+                    R.integer.recents_animate_task_stack_scroll_duration));
+            mScrollAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+            mScrollAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (postRunnable != null) {
+                        postRunnable.run();
+                    }
+                    mScrollAnimator.removeAllListeners();
                 }
-                mScrollAnimator.removeAllListeners();
+            });
+            mScrollAnimator.start();
+        } else {
+            if (postRunnable != null) {
+                postRunnable.run();
             }
-        });
-        mScrollAnimator.start();
+        }
     }
 
     /** Aborts any current stack scrolls */
index a7bc3df..5d1bb66 100644 (file)
@@ -59,8 +59,6 @@ import java.util.List;
 class TaskStackViewTouchHandler implements SwipeHelper.Callback {
 
     private static final int INACTIVE_POINTER_ID = -1;
-
-    private static final RectFEvaluator RECT_EVALUATOR = new RectFEvaluator();
     private static final Interpolator STACK_TRANSFORM_INTERPOLATOR =
             new PathInterpolator(0.73f, 0.33f, 0.42f, 0.85f);
 
@@ -545,8 +543,8 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
 
             mTmpTransform.copyFrom(fromTransform);
             // We only really need to interpolate the bounds, progress and translation
-            mTmpTransform.rect.set(RECT_EVALUATOR.evaluate(dismissFraction, fromTransform.rect,
-                    toTransform.rect));
+            mTmpTransform.rect.set(Utilities.RECTF_EVALUATOR.evaluate(dismissFraction,
+                    fromTransform.rect, toTransform.rect));
             mTmpTransform.p = fromTransform.p + (toTransform.p - fromTransform.p) * dismissFraction;
             mTmpTransform.translationZ = fromTransform.translationZ +
                     (toTransform.translationZ - fromTransform.translationZ) * dismissFraction;
index 31fbd3e..a287fe6 100644 (file)
@@ -29,8 +29,8 @@ public class ViewPool<V, T> {
     /* An interface to the consumer of a view pool */
     public interface ViewPoolConsumer<V, T> {
         public V createView(Context context);
-        public void prepareViewToEnterPool(V v);
-        public void prepareViewToLeavePool(V v, T prepareData, boolean isNewView);
+        public void onReturnViewToPool(V v);
+        public void onPickUpViewFromPool(V v, T prepareData, boolean isNewView);
         public boolean hasPreferredData(V v, T preferredData);
     }
 
@@ -46,7 +46,7 @@ public class ViewPool<V, T> {
 
     /** Returns a view into the pool */
     void returnViewToPool(V v) {
-        mViewCreator.prepareViewToEnterPool(v);
+        mViewCreator.onReturnViewToPool(v);
         mPool.push(v);
     }
 
@@ -73,7 +73,7 @@ public class ViewPool<V, T> {
                 v = mPool.pop();
             }
         }
-        mViewCreator.prepareViewToLeavePool(v, prepareData, isNewView);
+        mViewCreator.onPickUpViewFromPool(v, prepareData, isNewView);
         return v;
     }