summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Winson <winsonc@google.com> 2016-02-23 18:45:47 -0800
committer Winson <winsonc@google.com> 2016-02-24 16:31:00 -0800
commit66474134a7dfaf2e63946e2d021dc162365f3a90 (patch)
tree2fbf032faa7a3b7d1f874544339418df21970a8a
parente559171fe1458ced8b7b0f8bb19c97b3982ab4c7 (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
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java116
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java10
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;