diff options
| author | 2016-02-23 18:45:47 -0800 | |
|---|---|---|
| committer | 2016-02-24 16:31:00 -0800 | |
| commit | 66474134a7dfaf2e63946e2d021dc162365f3a90 (patch) | |
| tree | 2fbf032faa7a3b7d1f874544339418df21970a8a | |
| parent | e559171fe1458ced8b7b0f8bb19c97b3982ab4c7 (diff) | |
Improving transition from paging to stack.
- When we start scrolling, project the tasks onto the unfocused curve,
then reduce the offsets from the projected task indices back to the
normal indices as you scroll. This doesn’t give you a perfect result,
especially when scrolling in the same direction as the tasks are
offset, but is better than what we have now.
Change-Id: I055b08257fe1427e00e26ffa02f261cf51a8a2e0
3 files changed, 79 insertions, 70 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java index 88bebdbf173b..e67b75f263dc 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -16,20 +16,16 @@ package com.android.systemui.recents.views; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Resources; import android.graphics.Path; import android.graphics.Rect; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.FloatProperty; -import android.util.Property; +import android.util.SparseArray; +import android.util.SparseIntArray; import android.view.ViewDebug; -import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsActivityLaunchState; @@ -135,11 +131,11 @@ public class TaskStackLayoutAlgorithm { new FreePathInterpolator(FOCUSED_DIM_PATH); // The various focus states - public static final float STATE_FOCUSED = 1f; - public static final float STATE_UNFOCUSED = 0f; + public static final int STATE_FOCUSED = 1; + public static final int STATE_UNFOCUSED = 0; public interface TaskStackLayoutAlgorithmCallbacks { - void onFocusStateChanged(float prevFocusState, float curFocusState); + void onFocusStateChanged(int prevFocusState, int curFocusState); } /** @@ -209,24 +205,6 @@ public class TaskStackLayoutAlgorithm { } } - /** - * A Property wrapper around the <code>focusState</code> functionality handled by the - * {@link TaskStackLayoutAlgorithm#setFocusState(float)} and - * {@link TaskStackLayoutAlgorithm#getFocusState()} methods. - */ - private static final Property<TaskStackLayoutAlgorithm, Float> FOCUS_STATE = - new FloatProperty<TaskStackLayoutAlgorithm>("focusState") { - @Override - public void setValue(TaskStackLayoutAlgorithm object, float value) { - object.setFocusState(value); - } - - @Override - public Float get(TaskStackLayoutAlgorithm object) { - return object.getFocusState(); - } - }; - // A report of the visibility state of the stack public class VisibilityReport { public int numVisibleTasks; @@ -289,10 +267,7 @@ public class TaskStackLayoutAlgorithm { // The state of the stack focus (0..1), which controls the transition of the stack from the // focused to non-focused state @ViewDebug.ExportedProperty(category="recents") - private float mFocusState; - - // The animator used to reset the focused state - private ObjectAnimator mFocusStateAnimator; + private int mFocusState; // The smallest scroll progress, at this value, the back most task will be visible @ViewDebug.ExportedProperty(category="recents") @@ -321,7 +296,8 @@ public class TaskStackLayoutAlgorithm { int mMaxTranslationZ; // Optimization, allows for quick lookup of task -> index - private ArrayMap<Task.TaskKey, Integer> mTaskIndexMap = new ArrayMap<>(); + private SparseIntArray mTaskIndexMap = new SparseIntArray(); + private SparseArray<Float> mTaskIndexOverrideMap = new SparseArray<>(); // The freeform workspace layout FreeformWorkspaceLayoutAlgorithm mFreeformLayoutAlgorithm; @@ -354,6 +330,7 @@ public class TaskStackLayoutAlgorithm { * Resets this layout when the stack view is reset. */ public void reset() { + mTaskIndexOverrideMap.clear(); setFocusState(getDefaultFocusState()); } @@ -367,8 +344,8 @@ public class TaskStackLayoutAlgorithm { /** * Sets the focused state. */ - public void setFocusState(float focusState) { - float prevFocusState = mFocusState; + public void setFocusState(int focusState) { + int prevFocusState = mFocusState; mFocusState = focusState; updateFrontBackTransforms(); if (mCb != null) { @@ -379,7 +356,7 @@ public class TaskStackLayoutAlgorithm { /** * Gets the focused state. */ - public float getFocusState() { + public int getFocusState() { return mFocusState; } @@ -469,7 +446,7 @@ public class TaskStackLayoutAlgorithm { int taskCount = stackTasks.size(); for (int i = 0; i < taskCount; i++) { Task task = stackTasks.get(i); - mTaskIndexMap.put(task.key, i); + mTaskIndexMap.put(task.key.id, i); } // Calculate the min/max scroll @@ -516,35 +493,56 @@ public class TaskStackLayoutAlgorithm { } /** - * Updates this stack when a scroll happens. + * Adds and override task progress for the given task when transitioning from focused to + * unfocused state. */ - public void updateFocusStateOnScroll(int yMovement) { - Utilities.cancelAnimationWithoutCallbacks(mFocusStateAnimator); - if (mFocusState > STATE_UNFOCUSED) { - float delta = (float) yMovement / (UNFOCUS_MULTIPLIER * mStackRect.height()); - setFocusState(mFocusState - Math.min(mFocusState, Math.abs(delta))); + public void addUnfocusedTaskOverride(Task task, float stackScroll) { + if (mFocusState != STATE_UNFOCUSED) { + mFocusedRange.offset(stackScroll); + mUnfocusedRange.offset(stackScroll); + float focusedRangeX = mFocusedRange.getNormalizedX(mTaskIndexMap.get(task.key.id)); + float focusedY = mFocusedCurveInterpolator.getInterpolation(focusedRangeX); + float unfocusedRangeX = mUnfocusedCurveInterpolator.getX(focusedY); + float unfocusedTaskProgress = stackScroll + mUnfocusedRange.getAbsoluteX(unfocusedRangeX); + if (Float.compare(focusedRangeX, unfocusedRangeX) != 0) { + mTaskIndexOverrideMap.put(task.key.id, unfocusedTaskProgress); + } } } /** - * Aniamtes the focused state back to its orginal state. + * Updates this stack when a scroll happens. */ - public void animateFocusState(float newState) { - Utilities.cancelAnimationWithoutCallbacks(mFocusStateAnimator); - if (Float.compare(newState, getFocusState()) != 0) { - mFocusStateAnimator = ObjectAnimator.ofFloat(this, FOCUS_STATE, getFocusState(), - newState); - mFocusStateAnimator.setDuration(mContext.getResources().getInteger( - R.integer.recents_animate_task_stack_scroll_duration)); - mFocusStateAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); - mFocusStateAnimator.start(); + public void updateFocusStateOnScroll(float stackScroll, float deltaScroll) { + for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) { + int taskId = mTaskIndexOverrideMap.keyAt(i); + float x = mTaskIndexMap.get(taskId); + float overrideX = mTaskIndexOverrideMap.get(taskId, 0f); + float newOverrideX = overrideX + deltaScroll; + mUnfocusedRange.offset(stackScroll); + boolean outOfBounds = mUnfocusedRange.getNormalizedX(newOverrideX) < 0f || + mUnfocusedRange.getNormalizedX(newOverrideX) > 1f; + if (outOfBounds || (overrideX >= x && x >= newOverrideX) || + (overrideX <= x && x <= newOverrideX)) { + // Remove the override once we reach the original task index + mTaskIndexOverrideMap.removeAt(i); + } else if ((overrideX >= x && deltaScroll <= 0f) || + (overrideX <= x && deltaScroll >= 0f)) { + // Scrolling from override x towards x, then lock the task in place + mTaskIndexOverrideMap.put(taskId, newOverrideX); + } else { + // Scrolling override x away from x, we should still move the scroll towards x + float deltaX = overrideX - x; + newOverrideX = Math.signum(deltaX) * (Math.abs(deltaX) - deltaScroll); + mTaskIndexOverrideMap.put(taskId, x + newOverrideX); + } } } /** * Returns the default focus state. */ - public float getDefaultFocusState() { + public int getDefaultFocusState() { return STATE_FOCUSED; } @@ -650,18 +648,19 @@ public class TaskStackLayoutAlgorithm { false /* forceUpdate */); } - public TaskViewTransform getStackTransform(Task task, float stackScroll, float focusState, + public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState, TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate) { if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) { mFreeformLayoutAlgorithm.getTransform(task, transformOut, this); return transformOut; } else { // Return early if we have an invalid index - if (task == null || !mTaskIndexMap.containsKey(task.key)) { + if (task == null || mTaskIndexMap.get(task.key.id, -1) == -1) { transformOut.reset(); return transformOut; } - getStackTransform(mTaskIndexMap.get(task.key), stackScroll, focusState, transformOut, + float taskProgress = getStackScrollForTask(task); + getStackTransform(taskProgress, stackScroll, focusState, transformOut, frontTransform, false /* ignoreSingleTaskCase */, forceUpdate); return transformOut; } @@ -687,7 +686,7 @@ public class TaskStackLayoutAlgorithm { * internally to ensure that we can calculate the transform for any * position in the stack. */ - public void getStackTransform(float taskProgress, float stackScroll, float focusState, + public void getStackTransform(float taskProgress, float stackScroll, int focusState, TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean ignoreSingleTaskCase, boolean forceUpdate) { SystemServicesProxy ssp = Recents.getSystemServices(); @@ -773,8 +772,7 @@ public class TaskStackLayoutAlgorithm { * stack. */ float getStackScrollForTask(Task t) { - if (!mTaskIndexMap.containsKey(t.key)) return 0f; - return mTaskIndexMap.get(t.key); + return mTaskIndexOverrideMap.get(t.key.id, (float) mTaskIndexMap.get(t.key.id, 0)); } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 1707c4f4ccd0..d5f88f7ac56c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -665,7 +665,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal public void getCurrentTaskTransforms(ArrayList<Task> tasks, ArrayList<TaskViewTransform> transformsOut) { Utilities.matchTaskListSize(tasks, transformsOut); - float focusState = mLayoutAlgorithm.getFocusState(); + int focusState = mLayoutAlgorithm.getFocusState(); for (int i = tasks.size() - 1; i >= 0; i--) { Task task = tasks.get(i); TaskViewTransform transform = transformsOut.get(i); @@ -684,7 +684,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal * Returns the task transforms for all the tasks in the stack if the stack was at the given * {@param stackScroll} and {@param focusState}. */ - public void getLayoutTaskTransforms(float stackScroll, float focusState, ArrayList<Task> tasks, + public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList<Task> tasks, ArrayList<TaskViewTransform> transformsOut) { Utilities.matchTaskListSize(tasks, transformsOut); for (int i = tasks.size() - 1; i >= 0; i--) { @@ -1055,7 +1055,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal protected Parcelable onSaveInstanceState() { Bundle savedState = new Bundle(); savedState.putParcelable(KEY_SAVED_STATE_SUPER, super.onSaveInstanceState()); - savedState.putFloat(KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE, mLayoutAlgorithm.getFocusState()); + savedState.putInt(KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE, mLayoutAlgorithm.getFocusState()); savedState.putFloat(KEY_SAVED_STATE_LAYOUT_STACK_SCROLL, mStackScroller.getStackScroll()); return super.onSaveInstanceState(); } @@ -1065,7 +1065,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal Bundle savedState = (Bundle) state; super.onRestoreInstanceState(savedState.getParcelable(KEY_SAVED_STATE_SUPER)); - mLayoutAlgorithm.setFocusState(savedState.getFloat(KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE)); + mLayoutAlgorithm.setFocusState(savedState.getInt(KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE)); mStackScroller.setStackScroll(savedState.getFloat(KEY_SAVED_STATE_LAYOUT_STACK_SCROLL)); } @@ -1517,7 +1517,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /**** TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks ****/ @Override - public void onFocusStateChanged(float prevFocusState, float curFocusState) { + public void onFocusStateChanged(int prevFocusState, int curFocusState) { if (mDeferredTaskViewLayoutAnimation == null) { mUIDozeTrigger.poke(); relayoutTaskViewsOnNextFrame(AnimationProps.IMMEDIATE); @@ -1532,6 +1532,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal if (animation != null) { relayoutTaskViewsOnNextFrame(animation); } + mLayoutAlgorithm.updateFocusStateOnScroll(curScroll, curScroll - prevScroll); if (mEnterAnimationComplete) { if (shouldShowHistoryButton() && @@ -1636,11 +1637,19 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } public final void onBusEvent(FocusNextTaskViewEvent event) { + // Stop any scrolling + mStackScroller.stopScroller(); + mStackScroller.stopBoundScrollAnimation(); + setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false, event.timerIndicatorDuration); } public final void onBusEvent(FocusPreviousTaskViewEvent event) { + // Stop any scrolling + mStackScroller.stopScroller(); + mStackScroller.stopBoundScrollAnimation(); + setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */); } @@ -1771,10 +1780,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal removeIgnoreTask(event.task); } - public final void onBusEvent(StackViewScrolledEvent event) { - mLayoutAlgorithm.updateFocusStateOnScroll(event.yMovement.value); - } - public final void onBusEvent(IterateRecentsEvent event) { if (!mEnterAnimationComplete) { // Cancel the previous task's window transition before animating the focused state diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index 3f0630d70c94..20933ee3cf6f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -223,6 +223,13 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { int xDiff = Math.abs(x - mDownX); if (Math.abs(y - mDownY) > mScrollTouchSlop && yDiff > xDiff) { mIsScrolling = true; + float stackScroll = mScroller.getStackScroll(); + List<TaskView> taskViews = mSv.getTaskViews(); + for (int i = taskViews.size() - 1; i >= 0; i--) { + layoutAlgorithm.addUnfocusedTaskOverride(taskViews.get(i).getTask(), + stackScroll); + } + layoutAlgorithm.setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED); // Disallow parents from intercepting touch events final ViewParent parent = mSv.getParent(); @@ -429,8 +436,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { // Otherwise, offset the scroll by the movement of the anchor task float anchorTaskScroll = layoutAlgorithm.getStackScrollForTask(anchorTask); float stackScrollOffset = (anchorTaskScroll - prevAnchorTaskScroll); - if (layoutAlgorithm.getFocusState() != - TaskStackLayoutAlgorithm.STATE_FOCUSED) { + if (layoutAlgorithm.getFocusState() != TaskStackLayoutAlgorithm.STATE_FOCUSED) { // If we are focused, we don't want the front task to move, but otherwise, we // allow the back task to move up, and the front task to move back stackScrollOffset /= 2; |