summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI/src/com/android/systemui/recents
diff options
context:
space:
mode:
authorWinson Chung <winson.chung@gmail.com>2014-07-31 18:36:25 -0700
committerWinson Chung <winsonc@google.com>2014-08-01 04:32:08 +0000
commit012ef36a6c5e9745d112c734aed916cab052558c (patch)
treecf8578a0aae30bd4a048ac64d5047156b3602afd /packages/SystemUI/src/com/android/systemui/recents
parent7c3a95633d307c4be30c9dbbf1071063aa7a3c64 (diff)
downloadframeworks_base-012ef36a6c5e9745d112c734aed916cab052558c.zip
frameworks_base-012ef36a6c5e9745d112c734aed916cab052558c.tar.gz
frameworks_base-012ef36a6c5e9745d112c734aed916cab052558c.tar.bz2
Updating the stack layout to use a parameterized curve.
- Fixing issue with search box not being layered on top of the task stack view (Bug 16643875) - Fixing issue with there being no animation when dismissing recents while the stack is scrolling. Change-Id: I990f3c527de655d62fbf8a4539dcbaed3ed422c8
Diffstat (limited to 'packages/SystemUI/src/com/android/systemui/recents')
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Constants.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java276
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java316
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java202
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java94
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java23
13 files changed, 625 insertions, 432 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index cbcacc4..0b08b93 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -105,7 +105,8 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
}
public void onStart() {
- // Do nothing
+ // Initialize some static datastructures
+ TaskStackViewLayoutAlgorithm.initializeCurve();
}
public void onBootCompleted() {
@@ -322,7 +323,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
Rect taskStackBounds = new Rect(mTaskStackBounds);
taskStackBounds.bottom -= mSystemInsets.bottom;
tsv.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
- tsv.setStackScrollToInitialState();
+ tsv.getScroller().setStackScrollToInitialState();
// Find the running task in the TaskStack
Task task = null;
@@ -344,7 +345,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
}
// Get the transform for the running task
- mTmpTransform = algo.getStackTransform(task, tsv.getStackScroll(), mTmpTransform);
+ mTmpTransform = algo.getStackTransform(task, tsv.getScroller().getStackScroll(), mTmpTransform, null);
return new Rect(mTmpTransform.rect);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index c49e244..3d4d6c4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -25,8 +25,6 @@ public class Constants {
public static final boolean Verbose = false;
public static class App {
- // Enables the simulated task affiliations
- public static final boolean EnableSimulatedTaskGroups = false;
// Enables the screenshot app->Recents transition
public static final boolean EnableScreenshotAppTransition = false;
// Enables the filtering of tasks according to their grouping
@@ -43,11 +41,15 @@ public class Constants {
public static final boolean EnableShadows = true;
// This disables the bitmap and icon caches
public static final boolean DisableBackgroundCache = false;
- // For debugging, this enables us to create mock recents tasks
+ // Enables the simulated task affiliations
+ public static final boolean EnableSimulatedTaskGroups = false;
+ // Defines the number of mock task affiliations per group
+ public static final int TaskAffiliationsGroupCount = 12;
+ // Enables us to create mock recents tasks
public static final boolean EnableSystemServicesProxy = false;
- // For debugging, this defines the number of mock recents packages to create
+ // Defines the number of mock recents packages to create
public static final int SystemServicesProxyMockPackageCount = 3;
- // For debugging, this defines the number of mock recents tasks to create
+ // Defines the number of mock recents tasks to create
public static final int SystemServicesProxyMockTaskCount = 100;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index fbcbe2c..cf0a1dc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -76,7 +76,7 @@ public class RecentsConfiguration {
public int taskViewRemoveAnimDuration;
public int taskViewRemoveAnimTranslationXPx;
public int taskViewTranslationZMinPx;
- public int taskViewTranslationZIncrementPx;
+ public int taskViewTranslationZMaxPx;
public int taskViewRoundedCornerRadiusPx;
public int taskViewHighlightPx;
public int taskViewAffiliateGroupEnterOffsetPx;
@@ -208,8 +208,7 @@ public class RecentsConfiguration {
res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
taskViewHighlightPx = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
- taskViewTranslationZIncrementPx =
- res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
+ taskViewTranslationZMaxPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
taskViewAffiliateGroupEnterOffsetPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_affiliate_group_enter_offset);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 04ee9dd..fd6303f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -337,7 +337,7 @@ public class TaskStack {
String prevPackage = "";
int prevAffiliation = -1;
Random r = new Random();
- int groupCountDown = 5;
+ int groupCountDown = Constants.DebugFlags.App.TaskAffiliationsGroupCount;
for (int i = 0; i < taskCount; i++) {
Task t = tasks.get(i);
String packageName = t.key.baseIntent.getComponent().getPackageName();
@@ -352,7 +352,7 @@ public class TaskStack {
addGroup(group);
prevAffiliation = affiliation;
prevPackage = packageName;
- groupCountDown = 5;
+ groupCountDown = Constants.DebugFlags.App.TaskAffiliationsGroupCount;
}
group.addTask(t);
taskMap.put(t.key, t);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java b/packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java
index 4c3fbf0..452830d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java
@@ -26,6 +26,7 @@ import android.view.View;
import android.widget.FrameLayout;
import android.widget.SeekBar;
import com.android.systemui.R;
+import com.android.systemui.recents.RecentsConfiguration;
import java.util.ArrayList;
@@ -42,11 +43,14 @@ public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarCh
final static int sCornerRectSize = 50;
+ RecentsConfiguration mConfig;
DebugOverlayViewCallbacks mCb;
ArrayList<Pair<Rect, Integer>> mRects = new ArrayList<Pair<Rect, Integer>>();
+ String mText;
Paint mDebugOutline = new Paint();
Paint mTmpPaint = new Paint();
+ Rect mTmpRect = new Rect();
boolean mEnabled = true;
SeekBar mPrimarySeekBar;
@@ -66,6 +70,7 @@ public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarCh
public DebugOverlayView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mConfig = RecentsConfiguration.getInstance();
mDebugOutline.setColor(0xFFff0000);
mDebugOutline.setStyle(Paint.Style.STROKE);
mDebugOutline.setStrokeWidth(8f);
@@ -124,6 +129,12 @@ public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarCh
invalidate();
}
+ /** Sets the debug text at the bottom of the screen. */
+ void setText(String message) {
+ mText = message;
+ invalidate();
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -145,6 +156,14 @@ public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarCh
mTmpPaint.setColor(r.second);
canvas.drawRect(r.first, mTmpPaint);
}
+
+ // Draw the text
+ if (mText != null && mText.length() > 0) {
+ mTmpPaint.setColor(0xFFff0000);
+ mTmpPaint.setTextSize(60);
+ mTmpPaint.getTextBounds(mText, 0, 1, mTmpRect);
+ canvas.drawText(mText, 10f, getMeasuredHeight() - mTmpRect.height() - mConfig.systemInsets.bottom, mTmpPaint);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 189578c..cd2cbe7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -225,6 +225,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
public void setSearchBarVisibility(int visibility) {
if (mSearchBar != null) {
mSearchBar.setVisibility(visibility);
+ // Always bring the search bar to the top
+ mSearchBar.bringToFront();
}
}
@@ -364,17 +366,17 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
View sourceView = tv;
int offsetX = 0;
int offsetY = 0;
- int stackScroll = stackView.getStackScroll();
+ float stackScroll = stackView.getScroller().getStackScroll();
if (tv == null) {
// If there is no actual task view, then use the stack view as the source view
// and then offset to the expected transform rect, but bound this to just
// outside the display rect (to ensure we don't animate from too far away)
sourceView = stackView;
- transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform);
+ transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
offsetX = transform.rect.left;
offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
} else {
- transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform);
+ transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
}
// Compute the thumbnail to scale up from
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index 492e3aa..90bf12f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -126,28 +126,6 @@ class TaskBarView extends FrameLayout {
mIsFullscreen = isFullscreen;
}
- /** Synchronizes this bar view's properties with the task's transform */
- void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
- if (duration > 0 && (mDismissButton.getVisibility() == View.VISIBLE)) {
- ViewPropertyAnimator anim = mDismissButton.animate();
-
- // Animate to the final state
- if (toTransform.hasDismissAlphaChangedFrom(mDismissButton.getAlpha())) {
- anim.alpha(toTransform.dismissAlpha)
- .setStartDelay(0)
- .setDuration(duration)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .withLayer()
- .start();
- }
- } else {
- // Set the changed properties
- if (toTransform.hasDismissAlphaChangedFrom(mDismissButton.getAlpha())) {
- mDismissButton.setAlpha(toTransform.dismissAlpha);
- }
- }
- }
-
@Override
public boolean hasOverlappingRendering() {
return false;
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 aa67c1e..986df91 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -16,25 +16,17 @@
package com.android.systemui.recents.views;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.content.ComponentName;
import android.content.Context;
-import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
-import android.widget.OverScroller;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.DozeTrigger;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
-import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
@@ -47,8 +39,8 @@ import java.util.HashSet;
/* The visual representation of a task stack view */
public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
- TaskView.TaskViewCallbacks, ViewPool.ViewPoolConsumer<TaskView, Task>,
- RecentsPackageMonitor.PackageCallbacks {
+ TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
+ ViewPool.ViewPoolConsumer<TaskView, Task>, RecentsPackageMonitor.PackageCallbacks {
/** The TaskView callbacks */
interface TaskStackViewCallbacks {
@@ -64,8 +56,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
RecentsConfiguration mConfig;
TaskStack mStack;
- TaskStackViewLayoutAlgorithm mStackAlgorithm;
+ TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
TaskStackViewFilterAlgorithm mFilterAlgorithm;
+ TaskStackViewScroller mStackScroller;
TaskStackViewTouchHandler mTouchHandler;
TaskStackViewCallbacks mCb;
ViewPool<TaskView, Task> mViewPool;
@@ -73,18 +66,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
DozeTrigger mUIDozeTrigger;
DebugOverlayView mDebugOverlay;
Rect mTaskStackBounds = new Rect();
-
- // The virtual stack scroll that we use for the card layout
- int mStackScroll;
- int mMinScroll;
- int mMaxScroll;
- int mStashedScroll;
int mFocusedTaskIndex = -1;
- OverScroller mScroller;
- ObjectAnimator mScrollAnimator;
// Optimizations
- ReferenceCountedTrigger mHwLayersTrigger;
int mStackViewsAnimationDuration;
boolean mStackViewsDirty = true;
boolean mAwaitingFirstLayout = true;
@@ -114,12 +98,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mConfig = RecentsConfiguration.getInstance();
mStack = stack;
mStack.setCallbacks(this);
- mScroller = new OverScroller(context);
- mTouchHandler = new TaskStackViewTouchHandler(context, this);
mViewPool = new ViewPool<TaskView, Task>(context, this);
mInflater = LayoutInflater.from(context);
- mStackAlgorithm = new TaskStackViewLayoutAlgorithm(mConfig);
+ mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(mConfig);
mFilterAlgorithm = new TaskStackViewFilterAlgorithm(mConfig, this, mViewPool);
+ mStackScroller = new TaskStackViewScroller(context, mConfig, mLayoutAlgorithm);
+ mStackScroller.setCallbacks(this);
+ mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
mUIDozeTrigger = new DozeTrigger(mConfig.taskBarDismissDozeDelaySeconds, new Runnable() {
@Override
public void run() {
@@ -149,7 +134,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
void requestSynchronizeStackViewsWithModel(int duration) {
if (!mStackViewsDirty) {
- invalidate(mStackAlgorithm.mStackRect);
+ invalidate();
mStackViewsDirty = true;
}
if (mAwaitingFirstLayout) {
@@ -174,7 +159,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/** Returns the stack algorithm for this task stack. */
public TaskStackViewLayoutAlgorithm getStackAlgorithm() {
- return mStackAlgorithm;
+ return mLayoutAlgorithm;
}
/**
@@ -182,7 +167,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
*/
private boolean updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
ArrayList<Task> tasks,
- int stackScroll,
+ float stackScroll,
int[] visibleRangeOut,
boolean boundTranslationsToRect) {
// XXX: We should be intelligent about where to look for the visible stack range using the
@@ -207,8 +192,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Update the stack transforms
+ TaskViewTransform prevTransform = null;
for (int i = taskCount - 1; i >= 0; i--) {
- TaskViewTransform transform = mStackAlgorithm.getStackTransform(tasks.get(i), stackScroll, taskTransforms.get(i));
+ TaskViewTransform transform = mLayoutAlgorithm.getStackTransform(tasks.get(i),
+ stackScroll, taskTransforms.get(i), prevTransform);
if (transform.visible) {
if (frontMostVisibleIndex < 0) {
frontMostVisibleIndex = i;
@@ -228,8 +215,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (boundTranslationsToRect) {
transform.translationY = Math.min(transform.translationY,
- mStackAlgorithm.mViewRect.bottom);
+ mLayoutAlgorithm.mViewRect.bottom);
}
+ prevTransform = transform;
}
if (visibleRangeOut != null) {
visibleRangeOut[0] = frontMostVisibleIndex;
@@ -243,7 +231,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
* call is less optimal than calling updateStackTransforms directly.
*/
private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks,
- int stackScroll,
+ float stackScroll,
int[] visibleRangeOut,
boolean boundTranslationsToRect) {
ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>();
@@ -257,9 +245,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (mStackViewsDirty) {
// Get all the task transforms
ArrayList<Task> tasks = mStack.getTasks();
- int stackScroll = getStackScroll();
+ float stackScroll = mStackScroller.getStackScroll();
int[] visibleRange = mTmpVisibleRange;
- boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
+ boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks,
+ stackScroll, visibleRange, false);
+ if (mDebugOverlay != null) {
+ mDebugOverlay.setText("vis[" + visibleRange[1] + "-" + visibleRange[0] + "]");
+ }
// Return all the invisible children to the pool
mTmpTaskViewMap.clear();
@@ -288,11 +280,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (mStackViewsAnimationDuration > 0) {
// For items in the list, put them in start animating them from the
// approriate ends of the list where they are expected to appear
- if (transform.t < 0) {
- mTmpTransform = mStackAlgorithm.getStackTransform(tasks.get(0), stackScroll, mTmpTransform);
+ if (Float.compare(transform.p, 0f) <= 0) {
+ mLayoutAlgorithm.getStackTransform(0f, 0f, mTmpTransform, null);
} else {
- int nextTaskStackScroll = mStackAlgorithm.getStackScrollForTaskIndex(task, 1);
- mStackAlgorithm.getStackTransform(nextTaskStackScroll, stackScroll, mTmpTransform);
+ mLayoutAlgorithm.getStackTransform(1f, 0f, mTmpTransform, null);
}
tv.updateViewPropertiesToTaskTransform(mTmpTransform, 0);
}
@@ -357,143 +348,22 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mTaskStackBounds.set(r);
}
- /** Sets the current stack scroll */
- public void setStackScroll(int value) {
- mStackScroll = value;
- mUIDozeTrigger.poke();
- requestSynchronizeStackViewsWithModel();
- }
-
- /** Sets the current stack scroll without synchronizing the stack view with the model */
- public void setStackScrollRaw(int value) {
- mStackScroll = value;
- mUIDozeTrigger.poke();
- }
- /** Sets the current stack scroll to the initial state when you first enter recents */
- public void setStackScrollToInitialState() {
- setStackScroll(getInitialStackScroll());
- }
- /** Computes the initial stack scroll for the stack. */
- int getInitialStackScroll() {
- if (mStack.getTaskCount() > 2) {
- return Math.max(mMinScroll, mMaxScroll - (int) (mStackAlgorithm.mTaskRect.height() * (3f/4f)));
- }
- return mMaxScroll;
- }
-
- /** Gets the current stack scroll */
- public int getStackScroll() {
- return mStackScroll;
- }
-
- /** Animates the stack scroll into bounds */
- ObjectAnimator animateBoundScroll() {
- int curScroll = getStackScroll();
- int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
- if (newScroll != curScroll) {
- // Start a new scroll animation
- animateScroll(curScroll, newScroll, null);
- }
- return mScrollAnimator;
- }
-
- /** Animates the stack scroll */
- void animateScroll(int curScroll, int newScroll, final Runnable postRunnable) {
- // Abort any current animations
- abortScroller();
- abortBoundScrollAnimation();
-
- mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll);
- mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
- curScroll, 250));
- mScrollAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
- mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- setStackScroll((Integer) animation.getAnimatedValue());
- }
- });
- mScrollAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (postRunnable != null) {
- postRunnable.run();
- }
- mScrollAnimator.removeAllListeners();
- }
- });
- mScrollAnimator.start();
- }
-
- /** Aborts any current stack scrolls */
- void abortBoundScrollAnimation() {
- if (mScrollAnimator != null) {
- mScrollAnimator.cancel();
- }
- }
-
- /** Aborts the scroller and any current fling */
- void abortScroller() {
- if (!mScroller.isFinished()) {
- // Abort the scroller
- mScroller.abortAnimation();
- }
- }
-
- /** Bounds the current scroll if necessary */
- public boolean boundScroll() {
- int curScroll = getStackScroll();
- int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
- if (newScroll != curScroll) {
- setStackScroll(newScroll);
- return true;
- }
- return false;
- }
-
- /**
- * Bounds the current scroll if necessary, but does not synchronize the stack view with the
- * model.
- */
- public boolean boundScrollRaw() {
- int curScroll = getStackScroll();
- int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
- if (newScroll != curScroll) {
- setStackScrollRaw(newScroll);
- return true;
- }
- return false;
- }
-
-
- /** Returns the amount that the scroll is out of bounds */
- int getScrollAmountOutOfBounds(int scroll) {
- if (scroll < mMinScroll) {
- return mMinScroll - scroll;
- } else if (scroll > mMaxScroll) {
- return scroll - mMaxScroll;
- }
- return 0;
- }
-
- /** Returns whether the specified scroll is out of bounds */
- boolean isScrollOutOfBounds() {
- return getScrollAmountOutOfBounds(mStackScroll) != 0;
- }
-
/** Updates the min and max virtual scroll bounds */
void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
// Compute the min and max scroll values
- mStackAlgorithm.computeMinMaxScroll(mStack.getTasks());
- mMinScroll = mStackAlgorithm.mMinScroll;
- mMaxScroll = mStackAlgorithm.mMaxScroll;
+ mLayoutAlgorithm.computeMinMaxScroll(mStack.getTasks());
// Debug logging
if (boundScrollToNewMinMax) {
- boundScroll();
+ mStackScroller.boundScroll();
}
}
+ /** Returns the scroller. */
+ public TaskStackViewScroller getScroller() {
+ return mStackScroller;
+ }
+
/** Focuses the task at the specified index in the stack */
void focusTask(int taskIndex, boolean scrollToNewPosition) {
if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
@@ -520,10 +390,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (scrollToNewPosition) {
// Scroll the view into position
- int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll,
- mStackAlgorithm.getStackScrollForTaskIndex(t)));
-
- animateScroll(getStackScroll(), newScroll, postScrollRunnable);
+ // XXX: We probably want this to be centered in view instead of p = 0f
+ float newScroll = mStackScroller.getBoundedStackScroll(
+ mLayoutAlgorithm.getStackScrollForTaskIndex(t));
+ mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll, postScrollRunnable);
} else {
if (postScrollRunnable != null) {
postScrollRunnable.run();
@@ -558,24 +428,17 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void computeScroll() {
- if (mScroller.computeScrollOffset()) {
- setStackScroll(mScroller.getCurrY());
- invalidate();
- }
- }
-
- @Override
- public void dispatchDraw(Canvas canvas) {
+ // Synchronize the views
if (synchronizeStackViewsWithModel()) {
clipTaskViews();
}
- super.dispatchDraw(canvas);
+ mStackScroller.computeScroll();
}
/** Computes the stack and task rects */
public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
// Compute the rects in the stack algorithm
- mStackAlgorithm.computeRects(mStack.getTasks(), windowWidth, windowHeight, taskStackBounds);
+ mLayoutAlgorithm.computeRects(windowWidth, windowHeight, taskStackBounds);
// Update the scroll bounds
updateMinMaxScroll(false);
@@ -598,7 +461,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// If this is the first layout, then scroll to the front of the stack and synchronize the
// stack views immediately to load all the views
if (mAwaitingFirstLayout) {
- setStackScrollToInitialState();
+ mStackScroller.setStackScrollToInitialState();
requestSynchronizeStackViewsWithModel();
synchronizeStackViewsWithModel();
}
@@ -611,9 +474,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
tv.measure(widthMeasureSpec, heightMeasureSpec);
} else {
tv.measure(
- MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.width(),
+ MeasureSpec.makeMeasureSpec(mLayoutAlgorithm.mTaskRect.width(),
MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.height() +
+ MeasureSpec.makeMeasureSpec(mLayoutAlgorithm.mTaskRect.height() +
tv.getMaxFooterHeight(), MeasureSpec.EXACTLY));
}
}
@@ -635,8 +498,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (tv.isFullScreenView()) {
tv.layout(left, top, left + tv.getMeasuredWidth(), top + tv.getMeasuredHeight());
} else {
- tv.layout(mStackAlgorithm.mTaskRect.left, mStackAlgorithm.mTaskRect.top,
- mStackAlgorithm.mTaskRect.right, mStackAlgorithm.mTaskRect.bottom +
+ tv.layout(mLayoutAlgorithm.mTaskRect.left, mLayoutAlgorithm.mTaskRect.top,
+ mLayoutAlgorithm.mTaskRect.right, mLayoutAlgorithm.mTaskRect.bottom +
tv.getMaxFooterHeight());
}
}
@@ -649,8 +512,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/** Handler for the first layout. */
void onFirstLayout() {
- int offscreenY = mStackAlgorithm.mViewRect.bottom -
- (mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
+ int offscreenY = mLayoutAlgorithm.mViewRect.bottom -
+ (mLayoutAlgorithm.mTaskRect.top - mLayoutAlgorithm.mViewRect.top);
// Find the launch target task
Task launchTargetTask = null;
@@ -717,10 +580,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
ctx.currentTaskTransform = new TaskViewTransform();
ctx.currentStackViewIndex = i;
ctx.currentStackViewCount = childCount;
- ctx.currentTaskRect = mStackAlgorithm.mTaskRect;
+ ctx.currentTaskRect = mLayoutAlgorithm.mTaskRect;
ctx.currentTaskOccludesLaunchTarget = (launchTargetTask != null) &&
launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
- mStackAlgorithm.getStackTransform(task, getStackScroll(), ctx.currentTaskTransform);
+ mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(), ctx.currentTaskTransform, null);
tv.startEnterRecentsAnimation(ctx);
}
@@ -737,9 +600,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/** Requests this task stacks to start it's exit-recents animation. */
public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
- // Animate all the task views into view
- ctx.offscreenTranslationY = mStackAlgorithm.mViewRect.bottom -
- (mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
+ // Stop any scrolling
+ mStackScroller.stopScroller();
+ mStackScroller.stopBoundScrollAnimation();
+ // Animate all the task views out of view
+ ctx.offscreenTranslationY = mLayoutAlgorithm.mViewRect.bottom -
+ (mLayoutAlgorithm.mTaskRect.top - mLayoutAlgorithm.mViewRect.top);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
TaskView tv = (TaskView) getChildAt(i);
@@ -753,8 +619,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/** Animates a task view in this stack as it launches. */
public void startLaunchTaskAnimation(TaskView tv, final Runnable r) {
Task launchTargetTask = tv.getTask();
- int offscreenTranslationY = mStackAlgorithm.mViewRect.bottom -
- (mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
TaskView t = (TaskView) getChildAt(i);
@@ -788,17 +652,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void onStackTaskAdded(TaskStack stack, Task t) {
- // Update the task offsets
- mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
-
requestSynchronizeStackViewsWithModel();
}
@Override
public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask) {
- // Update the task offsets
- mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
-
// Remove the view associated with this task, we can't rely on updateTransforms
// to work here because the task is no longer in the list
TaskView tv = getChildViewForTask(removedTask);
@@ -811,8 +669,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Update the min/max scroll and animate other task views into their new positions
updateMinMaxScroll(true);
- int movement = (int) mStackAlgorithm.getTaskOverlapHeight();
- requestSynchronizeStackViewsWithModel(Utilities.calculateTranslationAnimationDuration(movement));
+ requestSynchronizeStackViewsWithModel(200);
// Update the new front most task
if (newFrontMostTask != null) {
@@ -839,6 +696,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void onStackFiltered(TaskStack newStack, final ArrayList<Task> curTasks,
Task filteredTask) {
+ /*
// Stash the scroll and filtered task for us to restore to when we unfilter
mStashedScroll = getStackScroll();
@@ -847,11 +705,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
getStackTransforms(curTasks, getStackScroll(), null, true);
// Update the task offsets
- mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
+ mLayoutAlgorithm.updateTaskOffsets(mStack.getTasks());
// Scroll the item to the top of the stack (sans-peek) rect so that we can see it better
updateMinMaxScroll(false);
- float overlapHeight = mStackAlgorithm.getTaskOverlapHeight();
+ float overlapHeight = mLayoutAlgorithm.getTaskOverlapHeight();
setStackScrollRaw((int) (newStack.indexOfTask(filteredTask) * overlapHeight));
boundScrollRaw();
@@ -865,16 +723,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Notify any callbacks
mCb.onTaskStackFilterTriggered();
+ */
}
@Override
public void onStackUnfiltered(TaskStack newStack, final ArrayList<Task> curTasks) {
+ /*
// Calculate the current task transforms
final ArrayList<TaskViewTransform> curTaskTransforms =
getStackTransforms(curTasks, getStackScroll(), null, true);
// Update the task offsets
- mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
+ mLayoutAlgorithm.updateTaskOffsets(mStack.getTasks());
// Restore the stashed scroll
updateMinMaxScroll(false);
@@ -894,6 +754,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Notify any callbacks
mCb.onTaskStackUnfilterTriggered();
+ */
}
/**** ViewPoolConsumer Implementation ****/
@@ -1014,7 +875,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void onTaskViewClipStateChanged(TaskView tv) {
- invalidate(mStackAlgorithm.mStackRect);
+ invalidate();
}
@Override
@@ -1022,6 +883,15 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
requestSynchronizeStackViewsWithModel();
}
+ /**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
+
+ @Override
+ public void onScrollChanged(float p) {
+ mUIDozeTrigger.poke();
+ requestSynchronizeStackViewsWithModel();
+ invalidate();
+ }
+
/**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 0fd4e86..b1482bb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -18,184 +18,276 @@ package com.android.systemui.recents.views;
import android.graphics.Rect;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
import java.util.ArrayList;
import java.util.HashMap;
-/* The layout logic for a TaskStackView */
+/* The layout logic for a TaskStackView.
+ *
+ * We are using a curve that defines the curve of the tasks as that go back in the recents list.
+ * The curve is defined such that at curve progress p = 0 is the end of the curve (the top of the
+ * stack rect), and p = 1 at the start of the curve and the bottom of the stack rect.
+ */
public class TaskStackViewLayoutAlgorithm {
// These are all going to change
- static final float StackOverlapPct = 0.65f; // The overlap height relative to the task height
- static final float StackPeekHeightPct = 0.075f; // The height of the peek space relative to the stack height
- static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area
- static final int StackPeekNumCards = 3; // The number of cards we see in the peek space
+ static final float StackPeekMinScale = 0.825f; // The min scale of the last card in the peek area
RecentsConfiguration mConfig;
// The various rects that define the stack view
Rect mViewRect = new Rect();
+ Rect mStackVisibleRect = new Rect();
Rect mStackRect = new Rect();
- Rect mStackRectSansPeek = new Rect();
Rect mTaskRect = new Rect();
- // The min/max scroll
- int mMinScroll;
- int mMaxScroll;
+ // The min/max scroll progress
+ float mMinScrollP;
+ float mMaxScrollP;
+ float mInitialScrollP;
+ int mWithinAffiliationOffset;
+ int mBetweenAffiliationOffset;
+ HashMap<Task.TaskKey, Float> mTaskProgressMap = new HashMap<Task.TaskKey, Float>();
- HashMap<Task.TaskKey, Integer> mTaskOffsetMap = new HashMap<Task.TaskKey, Integer>();
+ // Log function
+ static final float XScale = 1.75f; // The large the XScale, the longer the flat area of the curve
+ static final float LogBase = 300;
+ static final int PrecisionSteps = 250;
+ static float[] xp;
+ static float[] px;
public TaskStackViewLayoutAlgorithm(RecentsConfiguration config) {
mConfig = config;
+ mWithinAffiliationOffset = mConfig.taskBarHeight;
+ mBetweenAffiliationOffset = 4 * mConfig.taskBarHeight;
+
+ // Precompute the path
+ initializeCurve();
}
/** Computes the stack and task rects */
- public void computeRects(ArrayList<Task> tasks, int windowWidth, int windowHeight,
- Rect taskStackBounds) {
- // Note: We let the stack view be the full height because we want the cards to go under the
- // navigation bar if possible. However, the stack rects which we use to calculate
- // max scroll, etc. need to take the nav bar into account
-
+ public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
// Compute the stack rects
mViewRect.set(0, 0, windowWidth, windowHeight);
mStackRect.set(taskStackBounds);
+ mStackVisibleRect.set(taskStackBounds);
+ mStackVisibleRect.bottom = mViewRect.bottom;
int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
int heightPadding = mConfig.taskStackTopPaddingPx;
mStackRect.inset(widthPadding, heightPadding);
- mStackRectSansPeek.set(mStackRect);
- mStackRectSansPeek.top += StackPeekHeightPct * windowHeight;
// Compute the task rect
int size = mStackRect.width();
int left = mStackRect.left + (mStackRect.width() - size) / 2;
- mTaskRect.set(left, mStackRectSansPeek.top,
- left + size, mStackRectSansPeek.top + size);
-
- // Update the task offsets once the size changes
- updateTaskOffsets(tasks);
+ mTaskRect.set(left, mStackRect.top,
+ left + size, mStackRect.top + size);
}
+ /** Computes the minimum and maximum scroll progress values */
void computeMinMaxScroll(ArrayList<Task> tasks) {
- // Compute the min and max scroll values
- int numTasks = Math.max(1, tasks.size());
+ // Clear the progress map
+ mTaskProgressMap.clear();
+
+ // Return early if we have no tasks
+ if (tasks.isEmpty()) {
+ mMinScrollP = mMaxScrollP = 0;
+ return;
+ }
+
int taskHeight = mTaskRect.height();
- int stackHeight = mStackRectSansPeek.height();
-
- if (numTasks <= 1) {
- // If there is only one task, then center the task in the stack rect (sans peek)
- mMinScroll = mMaxScroll = -(stackHeight -
- (taskHeight + mConfig.taskViewLockToAppButtonHeight)) / 2;
- } else {
- int maxScrollHeight = getStackScrollForTaskIndex(tasks.get(tasks.size() - 1))
- + taskHeight + mConfig.taskViewLockToAppButtonHeight;
- mMinScroll = Math.min(stackHeight, maxScrollHeight) - stackHeight;
- mMaxScroll = maxScrollHeight - stackHeight;
+ float pAtBottomOfStackRect = screenYToCurveProgress(mStackVisibleRect.bottom);
+ float pWithinAffiliateOffset = pAtBottomOfStackRect -
+ screenYToCurveProgress(mStackVisibleRect.bottom - mWithinAffiliationOffset);
+ float pBetweenAffiliateOffset = pAtBottomOfStackRect -
+ screenYToCurveProgress(mStackVisibleRect.bottom - mBetweenAffiliationOffset);
+ float pTaskHeightOffset = pAtBottomOfStackRect -
+ screenYToCurveProgress(mStackVisibleRect.bottom - taskHeight);
+ float pNavBarOffset = pAtBottomOfStackRect -
+ screenYToCurveProgress(mStackVisibleRect.bottom - (mStackVisibleRect.bottom - mStackRect.bottom));
+
+ // Update the task offsets
+ float pAtBackMostCardTop = screenYToCurveProgress(mStackVisibleRect.top +
+ (mStackVisibleRect.height() - taskHeight) / 2);
+ float pAtFrontMostCardTop = pAtBackMostCardTop;
+ float pAtSecondFrontMostCardTop = pAtBackMostCardTop;
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ mTaskProgressMap.put(task.key, pAtFrontMostCardTop);
+
+ if (i < (taskCount - 1)) {
+ // Increment the peek height
+ float pPeek = task.group.isFrontMostTask(task) ? pBetweenAffiliateOffset :
+ pWithinAffiliateOffset;
+ pAtSecondFrontMostCardTop = pAtFrontMostCardTop;
+ pAtFrontMostCardTop += pPeek;
+ }
}
+
+ mMinScrollP = 0f;
+ mMaxScrollP = pAtFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
+ mInitialScrollP = pAtSecondFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
}
/** Update/get the transform */
- public TaskViewTransform getStackTransform(Task task, int stackScroll, TaskViewTransform transformOut) {
+ public TaskViewTransform getStackTransform(Task task, float stackScroll, TaskViewTransform transformOut,
+ TaskViewTransform prevTransform) {
// Return early if we have an invalid index
if (task == null) {
transformOut.reset();
return transformOut;
}
- return getStackTransform(getStackScrollForTaskIndex(task), stackScroll, transformOut);
+ return getStackTransform(mTaskProgressMap.get(task.key), stackScroll, transformOut, prevTransform);
}
/** Update/get the transform */
- public TaskViewTransform getStackTransform(int taskStackScroll, int stackScroll, TaskViewTransform transformOut) {
- // Map the items to an continuous position relative to the specified scroll
- int numPeekCards = StackPeekNumCards;
- float overlapHeight = StackOverlapPct * mTaskRect.height();
- float peekHeight = StackPeekHeightPct * mStackRect.height();
- float t = (taskStackScroll - stackScroll) / overlapHeight;
- float boundedT = Math.max(t, -(numPeekCards + 1));
-
- // Set the scale relative to its position
- int numFrontScaledCards = 3;
- float minScale = StackPeekMinScale;
- float scaleRange = 1f - minScale;
- float scaleInc = scaleRange / (numPeekCards + numFrontScaledCards);
- float scale = Math.max(minScale, Math.min(1f, minScale +
- ((boundedT + (numPeekCards + 1)) * scaleInc)));
- float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
- // Account for the bar offsets being scaled?
- float scaleBarYOffset = (1f - scale) * mConfig.taskBarHeight;
- transformOut.scale = scale;
-
- // Set the y translation
- if (boundedT < 0f) {
- transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
- numPeekCards) * peekHeight - scaleYOffset);
- } else {
- transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
+ public TaskViewTransform getStackTransform(float taskProgress, float stackScroll, TaskViewTransform transformOut, TaskViewTransform prevTransform) {
+ float pTaskRelative = taskProgress - stackScroll;
+ float pBounded = Math.max(0, Math.min(pTaskRelative, 1f));
+ // If the task top is outside of the bounds below the screen, then immediately reset it
+ if (pTaskRelative > 1f) {
+ transformOut.reset();
+ return transformOut;
}
-
- // Set the z translation
+ // The check for the top is trickier, since we want to show the next task if it is at all
+ // visible, even if p < 0.
+ if (pTaskRelative < 0f) {
+ if (prevTransform != null && Float.compare(prevTransform.p, 0f) <= 0) {
+ transformOut.reset();
+ return transformOut;
+ }
+ }
+ float scale = curveProgressToScale(pBounded);
+ int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
int minZ = mConfig.taskViewTranslationZMinPx;
- int incZ = mConfig.taskViewTranslationZIncrementPx;
- transformOut.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
-
- // Set the alphas
- // transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
- transformOut.dismissAlpha = 1f;
-
- // Update the rect and visibility
+ int maxZ = mConfig.taskViewTranslationZMaxPx;
+ transformOut.scale = scale;
+ transformOut.translationY = curveProgressToScreenY(pBounded) - mStackVisibleRect.top -
+ scaleYOffset;
+ transformOut.translationZ = Math.max(minZ, minZ + (pBounded * (maxZ - minZ)));
transformOut.rect.set(mTaskRect);
- if (t < -(numPeekCards + 1)) {
- transformOut.visible = false;
- } else {
- transformOut.rect.offset(0, transformOut.translationY);
- Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
- transformOut.visible = Rect.intersects(mViewRect, transformOut.rect);
- }
- transformOut.t = t;
+ transformOut.rect.offset(0, transformOut.translationY);
+ Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+ transformOut.visible = true;
+ transformOut.p = pTaskRelative;
return transformOut;
}
/**
- * Returns the overlap between one task and the next.
+ * Returns the scroll to such task top = 1f;
*/
- float getTaskOverlapHeight() {
- return StackOverlapPct * mTaskRect.height();
+ float getStackScrollForTaskIndex(Task t) {
+ return mTaskProgressMap.get(t.key);
}
- /**
- * Returns the scroll to such that the task transform at that index will have t=0. (If the scroll
- * is not bounded)
- */
- int getStackScrollForTaskIndex(Task t) {
- return mTaskOffsetMap.get(t.key);
+ /** Initializes the curve. */
+ public static void initializeCurve() {
+ if (xp != null && px != null) return;
+ xp = new float[PrecisionSteps + 1];
+ px = new float[PrecisionSteps + 1];
+
+ // Approximate f(x)
+ float[] fx = new float[PrecisionSteps + 1];
+ float step = 1f / PrecisionSteps;
+ float x = 0;
+ for (int xStep = 0; xStep <= PrecisionSteps; xStep++) {
+ fx[xStep] = logFunc(x);
+ x += step;
+ }
+ // Calculate the arc length for x:1->0
+ float pLength = 0;
+ float[] dx = new float[PrecisionSteps + 1];
+ dx[0] = 0;
+ for (int xStep = 1; xStep < PrecisionSteps; xStep++) {
+ dx[xStep] = (float) Math.sqrt(Math.pow(fx[xStep] - fx[xStep - 1], 2) + Math.pow(step, 2));
+ pLength += dx[xStep];
+ }
+ // Approximate p(x), a function of cumulative progress with x, normalized to 0..1
+ float p = 0;
+ px[0] = 0f;
+ px[PrecisionSteps] = 1f;
+ for (int xStep = 1; xStep <= PrecisionSteps; xStep++) {
+ p += Math.abs(dx[xStep] / pLength);
+ px[xStep] = p;
+ }
+ // Given p(x), calculate the inverse function x(p). This assumes that x(p) is also a valid
+ // function.
+ int xStep = 0;
+ p = 0;
+ xp[0] = 0f;
+ xp[PrecisionSteps] = 1f;
+ for (int pStep = 0; pStep < PrecisionSteps; pStep++) {
+ // Walk forward in px and find the x where px <= p && p < px+1
+ while (xStep < PrecisionSteps) {
+ if (px[xStep] > p) break;
+ xStep++;
+ }
+ // Now, px[xStep-1] <= p < px[xStep]
+ if (xStep == 0) {
+ xp[pStep] = 0;
+ } else {
+ // Find x such that proportionally, x is correct
+ float fraction = (p - px[xStep - 1]) / (px[xStep] - px[xStep - 1]);
+ x = (xStep - 1 + fraction) * step;
+ xp[pStep] = x;
+ }
+ p += step;
+ }
}
- /**
- * Returns the scroll to such that the task transform at that task + index will have t=0.
- * (If the scroll is not bounded)
- */
- int getStackScrollForTaskIndex(Task t, int relativeIndexOffset) {
- return mTaskOffsetMap.get(t.key) + (int) (relativeIndexOffset * getTaskOverlapHeight());
+ /** Reverses and scales out x. */
+ static float reverse(float x) {
+ return (-x * XScale) + 1;
+ }
+ /** The log function describing the curve. */
+ static float logFunc(float x) {
+ return 1f - (float) (Math.pow(LogBase, reverse(x))) / (LogBase);
+ }
+ /** The inverse of the log function describing the curve. */
+ float invLogFunc(float y) {
+ return (float) (Math.log((1f - reverse(y)) * (LogBase - 1) + 1) / Math.log(LogBase));
}
- /**
- * Updates the cache of tasks to offsets.
- */
- void updateTaskOffsets(ArrayList<Task> tasks) {
- mTaskOffsetMap.clear();
- int offset = 0;
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task t = tasks.get(i);
- mTaskOffsetMap.put(t.key, offset);
- if (t.group.isFrontMostTask(t)) {
- offset += getTaskOverlapHeight();
- } else {
- offset += mConfig.taskBarHeight;
- }
+ /** Converts from the progress along the curve to a screen coordinate. */
+ int curveProgressToScreenY(float p) {
+ if (p < 0 || p > 1) return mStackVisibleRect.top + (int) (p * mStackVisibleRect.height());
+ float pIndex = p * PrecisionSteps;
+ int pFloorIndex = (int) Math.floor(pIndex);
+ int pCeilIndex = (int) Math.ceil(pIndex);
+ float xFraction = 0;
+ if (pFloorIndex < PrecisionSteps && (pCeilIndex != pFloorIndex)) {
+ float pFraction = (pIndex - pFloorIndex) / (pCeilIndex - pFloorIndex);
+ xFraction = (xp[pCeilIndex] - xp[pFloorIndex]) * pFraction;
}
+ float x = xp[pFloorIndex] + xFraction;
+ return mStackVisibleRect.top + (int) (x * mStackVisibleRect.height());
+ }
+
+ /** Converts from the progress along the curve to a scale. */
+ float curveProgressToScale(float p) {
+ if (p < 0) return StackPeekMinScale;
+ if (p > 1) return 1f;
+ float scaleRange = (1f - StackPeekMinScale);
+ float scale = StackPeekMinScale + (p * scaleRange);
+ return scale;
}
+ /** Converts from a screen coordinate to the progress along the curve. */
+ float screenYToCurveProgress(int screenY) {
+ float x = (float) (screenY - mStackVisibleRect.top) / mStackVisibleRect.height();
+ if (x < 0 || x > 1) return x;
+ float xIndex = x * PrecisionSteps;
+ int xFloorIndex = (int) Math.floor(xIndex);
+ int xCeilIndex = (int) Math.ceil(xIndex);
+ float pFraction = 0;
+ if (xFloorIndex < PrecisionSteps && (xCeilIndex != xFloorIndex)) {
+ float xFraction = (xIndex - xFloorIndex) / (xCeilIndex - xFloorIndex);
+ pFraction = (px[xCeilIndex] - px[xFloorIndex]) * xFraction;
+ }
+ return px[xFloorIndex] + pFraction;
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
new file mode 100644
index 0000000..0a12dab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.widget.OverScroller;
+import com.android.systemui.recents.RecentsConfiguration;
+
+/* The scrolling logic for a TaskStackView */
+public class TaskStackViewScroller {
+ public interface TaskStackViewScrollerCallbacks {
+ public void onScrollChanged(float p);
+ }
+
+ RecentsConfiguration mConfig;
+ TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
+ TaskStackViewScrollerCallbacks mCb;
+
+ float mStackScrollP;
+
+ OverScroller mScroller;
+ ObjectAnimator mScrollAnimator;
+
+ public TaskStackViewScroller(Context context, RecentsConfiguration config, TaskStackViewLayoutAlgorithm layoutAlgorithm) {
+ mConfig = config;
+ mScroller = new OverScroller(context);
+ mLayoutAlgorithm = layoutAlgorithm;
+ setStackScroll(getStackScroll());
+ }
+
+ /** Sets the callbacks */
+ void setCallbacks(TaskStackViewScrollerCallbacks cb) {
+ mCb = cb;
+ }
+
+ /** Gets the current stack scroll */
+ public float getStackScroll() {
+ return mStackScrollP;
+ }
+
+ /** Sets the current stack scroll */
+ public void setStackScroll(float s) {
+ mStackScrollP = s;
+ if (mCb != null) {
+ mCb.onScrollChanged(mStackScrollP);
+ }
+ }
+
+ /** Sets the current stack scroll without calling the callback. */
+ void setStackScrollRaw(float s) {
+ mStackScrollP = s;
+ }
+
+ /** Sets the current stack scroll to the initial state when you first enter recents */
+ public void setStackScrollToInitialState() {
+ setStackScroll(getBoundedStackScroll(mLayoutAlgorithm.mInitialScrollP));
+ }
+
+ /** Bounds the current scroll if necessary */
+ public boolean boundScroll() {
+ float curScroll = getStackScroll();
+ float newScroll = getBoundedStackScroll(curScroll);
+ if (Float.compare(newScroll, curScroll) != 0) {
+ setStackScroll(newScroll);
+ return true;
+ }
+ return false;
+ }
+ /** Bounds the current scroll if necessary, but does not synchronize the stack view with the model. */
+ public boolean boundScrollRaw() {
+ float curScroll = getStackScroll();
+ float newScroll = getBoundedStackScroll(curScroll);
+ if (Float.compare(newScroll, curScroll) != 0) {
+ setStackScrollRaw(newScroll);
+ return true;
+ }
+ return false;
+ }
+
+ /** Returns the bounded stack scroll */
+ float getBoundedStackScroll(float scroll) {
+ return Math.max(mLayoutAlgorithm.mMinScrollP, Math.min(mLayoutAlgorithm.mMaxScrollP, scroll));
+ }
+
+ /** Returns the amount that the aboslute value of how much the scroll is out of bounds. */
+ float getScrollAmountOutOfBounds(float scroll) {
+ if (scroll < mLayoutAlgorithm.mMinScrollP) {
+ return Math.abs(scroll - mLayoutAlgorithm.mMinScrollP);
+ } else if (scroll > mLayoutAlgorithm.mMaxScrollP) {
+ return Math.abs(scroll - mLayoutAlgorithm.mMaxScrollP);
+ }
+ return 0f;
+ }
+
+ /** Returns whether the specified scroll is out of bounds */
+ boolean isScrollOutOfBounds() {
+ return Float.compare(getScrollAmountOutOfBounds(mStackScrollP), 0f) != 0;
+ }
+
+ /** Animates the stack scroll into bounds */
+ ObjectAnimator animateBoundScroll() {
+ float curScroll = getStackScroll();
+ float newScroll = getBoundedStackScroll(curScroll);
+ if (Float.compare(newScroll, curScroll) != 0) {
+ // Start a new scroll animation
+ animateScroll(curScroll, newScroll, null);
+ }
+ return mScrollAnimator;
+ }
+
+ /** Animates the stack scroll */
+ void animateScroll(float curScroll, float newScroll, final Runnable postRunnable) {
+ // Abort any current animations
+ stopScroller();
+ stopBoundScrollAnimation();
+
+ mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll);
+ mScrollAnimator.setDuration(250);
+ // We would have to project the difference into the screen coords, and then use that as the
+ // duration
+// mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
+// curScroll, 250));
+ mScrollAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
+ mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ setStackScroll((Float) animation.getAnimatedValue());
+ }
+ });
+ mScrollAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (postRunnable != null) {
+ postRunnable.run();
+ }
+ mScrollAnimator.removeAllListeners();
+ }
+ });
+ mScrollAnimator.start();
+ }
+
+ /** Aborts any current stack scrolls */
+ void stopBoundScrollAnimation() {
+ if (mScrollAnimator != null) {
+ mScrollAnimator.removeAllListeners();
+ mScrollAnimator.cancel();
+ }
+ }
+
+ /**** OverScroller ****/
+
+ int progressToScrollRange(float p) {
+ return (int) (p * mLayoutAlgorithm.mStackVisibleRect.height());
+ }
+
+ float scrollRangeToProgress(int s) {
+ return (float) s / mLayoutAlgorithm.mStackVisibleRect.height();
+ }
+
+ /** Called from the view draw, computes the next scroll. */
+ boolean computeScroll() {
+ if (mScroller.computeScrollOffset()) {
+ float scroll = scrollRangeToProgress(mScroller.getCurrY());
+ setStackScrollRaw(scroll);
+ if (mCb != null) {
+ mCb.onScrollChanged(scroll);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /** Returns whether the overscroller is scrolling. */
+ boolean isScrolling() {
+ return !mScroller.isFinished();
+ }
+
+ /** Stops the scroller and any current fling. */
+ void stopScroller() {
+ if (!mScroller.isFinished()) {
+ mScroller.abortAnimation();
+ }
+ }
+} \ No newline at end of file
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 b1c35f3..4cf6b82 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -29,16 +29,19 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
static int INACTIVE_POINTER_ID = -1;
TaskStackView mSv;
+ TaskStackViewScroller mScroller;
VelocityTracker mVelocityTracker;
boolean mIsScrolling;
+ float mInitialP;
+ float mLastP;
+ float mTotalPMotion;
int mInitialMotionX, mInitialMotionY;
int mLastMotionX, mLastMotionY;
int mActivePointerId = INACTIVE_POINTER_ID;
TaskView mActiveTaskView = null;
- int mTotalScrollMotion;
int mMinimumVelocity;
int mMaximumVelocity;
// The scroll touch slop is used to calculate when we start scrolling
@@ -49,14 +52,14 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
SwipeHelper mSwipeHelper;
boolean mInterceptedBySwipeHelper;
- public TaskStackViewTouchHandler(Context context, TaskStackView sv) {
+ public TaskStackViewTouchHandler(Context context, TaskStackView sv, TaskStackViewScroller scroller) {
ViewConfiguration configuration = ViewConfiguration.get(context);
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mScrollTouchSlop = configuration.getScaledTouchSlop();
mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
mSv = sv;
-
+ mScroller = scroller;
float densityScale = context.getResources().getDisplayMetrics().density;
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop);
@@ -97,6 +100,13 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
return null;
}
+ /** Constructs a simulated motion event for the current stack scroll. */
+ MotionEvent createMotionEventForStackScroll(MotionEvent ev) {
+ MotionEvent pev = MotionEvent.obtainNoHistory(ev);
+ pev.setLocation(0, mScroller.progressToScrollRange(mScroller.getStackScroll()));
+ return pev;
+ }
+
/** Touch preprocessing for handling below */
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Return early if we have no children
@@ -111,24 +121,25 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
return true;
}
- boolean wasScrolling = !mSv.mScroller.isFinished() ||
- (mSv.mScrollAnimator != null && mSv.mScrollAnimator.isRunning());
+ boolean wasScrolling = mScroller.isScrolling() ||
+ (mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning());
int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
// Save the touch down info
mInitialMotionX = mLastMotionX = (int) ev.getX();
mInitialMotionY = mLastMotionY = (int) ev.getY();
+ mInitialP = mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
mActivePointerId = ev.getPointerId(0);
mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
// Stop the current scroll if it is still flinging
- mSv.abortScroller();
- mSv.abortBoundScrollAnimation();
+ mScroller.stopScroller();
+ mScroller.stopBoundScrollAnimation();
// Initialize the velocity tracker
initOrResetVelocityTracker();
- mVelocityTracker.addMovement(ev);
+ mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
// Check if the scroller is finished yet
- mIsScrolling = !mSv.mScroller.isFinished();
+ mIsScrolling = mScroller.isScrolling();
break;
}
case MotionEvent.ACTION_MOVE: {
@@ -142,7 +153,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mIsScrolling = true;
// Initialize the velocity tracker if necessary
initVelocityTrackerIfNotExists();
- mVelocityTracker.addMovement(ev);
+ mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
// Disallow parents from intercepting touch events
final ViewParent parent = mSv.getParent();
if (parent != null) {
@@ -152,17 +163,18 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mLastMotionX = x;
mLastMotionY = y;
+ mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
// Animate the scroll back if we've cancelled
- mSv.animateBoundScroll();
+ mScroller.animateBoundScroll();
// Reset the drag state and the velocity tracker
mIsScrolling = false;
mActivePointerId = INACTIVE_POINTER_ID;
mActiveTaskView = null;
- mTotalScrollMotion = 0;
+ mTotalPMotion = 0;
recycleVelocityTracker();
break;
}
@@ -186,7 +198,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
// Update the velocity tracker
initVelocityTrackerIfNotExists();
- mVelocityTracker.addMovement(ev);
int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
@@ -194,14 +205,15 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
// Save the touch down info
mInitialMotionX = mLastMotionX = (int) ev.getX();
mInitialMotionY = mLastMotionY = (int) ev.getY();
+ mInitialP = mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
mActivePointerId = ev.getPointerId(0);
mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
// Stop the current scroll if it is still flinging
- mSv.abortScroller();
- mSv.abortBoundScrollAnimation();
+ mScroller.stopScroller();
+ mScroller.stopBoundScrollAnimation();
// Initialize the velocity tracker
initOrResetVelocityTracker();
- mVelocityTracker.addMovement(ev);
+ mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
// Disallow parents from intercepting touch events
final ViewParent parent = mSv.getParent();
if (parent != null) {
@@ -214,6 +226,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mActivePointerId = ev.getPointerId(index);
mLastMotionX = (int) ev.getX(index);
mLastMotionY = (int) ev.getY(index);
+ mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
break;
}
case MotionEvent.ACTION_MOVE: {
@@ -223,13 +236,14 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
int x = (int) ev.getX(activePointerIndex);
int y = (int) ev.getY(activePointerIndex);
int yTotal = Math.abs(y - mInitialMotionY);
- int deltaY = mLastMotionY - y;
+ float curP = mSv.mLayoutAlgorithm.screenYToCurveProgress(y);
+ float deltaP = mLastP - curP;
if (!mIsScrolling) {
if (yTotal > mScrollTouchSlop) {
mIsScrolling = true;
// Initialize the velocity tracker
initOrResetVelocityTracker();
- mVelocityTracker.addMovement(ev);
+ mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
// Disallow parents from intercepting touch events
final ViewParent parent = mSv.getParent();
if (parent != null) {
@@ -238,23 +252,26 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
}
}
if (mIsScrolling) {
- int curStackScroll = mSv.getStackScroll();
- int overScrollAmount = mSv.getScrollAmountOutOfBounds(curStackScroll + deltaY);
- if (overScrollAmount != 0) {
+ float curStackScroll = mScroller.getStackScroll();
+ float overScrollAmount = mScroller.getScrollAmountOutOfBounds(curStackScroll + deltaP);
+ if (Float.compare(overScrollAmount, 0f) != 0) {
// Bound the overscroll to a fixed amount, and inversely scale the y-movement
// relative to how close we are to the max overscroll
- float maxOverScroll = mSv.mStackAlgorithm.mTaskRect.height() / 3f;
- deltaY = Math.round(deltaY * (1f - (Math.min(maxOverScroll, overScrollAmount)
- / maxOverScroll)));
+ float maxOverScroll = 0.25f;
+ deltaP *= (1f - (Math.min(maxOverScroll, overScrollAmount)
+ / maxOverScroll));
}
- mSv.setStackScroll(curStackScroll + deltaY);
- if (mSv.isScrollOutOfBounds()) {
+ mScroller.setStackScroll(curStackScroll + deltaP);
+ if (mScroller.isScrollOutOfBounds()) {
mVelocityTracker.clear();
+ } else {
+ mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
}
}
mLastMotionX = x;
mLastMotionY = y;
- mTotalScrollMotion += Math.abs(deltaY);
+ mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
+ mTotalPMotion += Math.abs(deltaP);
break;
}
case MotionEvent.ACTION_UP: {
@@ -263,25 +280,27 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
+ // XXX: Should this be calculated as a percentage of a curve?
int overscrollRange = (int) (Math.min(1f,
Math.abs((float) velocity / mMaximumVelocity)) *
Constants.Values.TaskStackView.TaskStackOverscrollRange);
// Fling scroll
- mSv.mScroller.fling(0, mSv.getStackScroll(),
- 0, -velocity,
+ mScroller.mScroller.fling(0, mScroller.progressToScrollRange(mScroller.getStackScroll()),
+ 0, velocity,
0, 0,
- mSv.mMinScroll, mSv.mMaxScroll,
+ mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMinScrollP),
+ mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMaxScrollP),
0, overscrollRange);
// Invalidate to kick off computeScroll
- mSv.invalidate(mSv.mStackAlgorithm.mStackRect);
- } else if (mSv.isScrollOutOfBounds()) {
+ mSv.invalidate();
+ } else if (mScroller.isScrollOutOfBounds()) {
// Animate the scroll back into bounds
- mSv.animateBoundScroll();
+ mScroller.animateBoundScroll();
}
mActivePointerId = INACTIVE_POINTER_ID;
mIsScrolling = false;
- mTotalScrollMotion = 0;
+ mTotalPMotion = 0;
recycleVelocityTracker();
break;
}
@@ -294,18 +313,19 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mActivePointerId = ev.getPointerId(newPointerIndex);
mLastMotionX = (int) ev.getX(newPointerIndex);
mLastMotionY = (int) ev.getY(newPointerIndex);
+ mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
mVelocityTracker.clear();
}
break;
}
case MotionEvent.ACTION_CANCEL: {
- if (mSv.isScrollOutOfBounds()) {
+ if (mScroller.isScrollOutOfBounds()) {
// Animate the scroll back into bounds
- mSv.animateBoundScroll();
+ mScroller.animateBoundScroll();
}
mActivePointerId = INACTIVE_POINTER_ID;
mIsScrolling = false;
- mTotalScrollMotion = 0;
+ mTotalPMotion = 0;
recycleVelocityTracker();
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 8a16d30..abf3c50 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -52,9 +52,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
RecentsConfiguration mConfig;
+ float mTaskProgress;
+ ObjectAnimator mTaskProgressAnimator;
+ float mMaxDimScale;
int mDim;
- int mMaxDim;
- AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
+ AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(1.25f);
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.MULTIPLY);
Task mTask;
@@ -76,7 +78,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- updateDimOverlayFromScale();
+ setTaskProgress((Float) animation.getAnimatedValue());
}
};
@@ -96,10 +98,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = RecentsConfiguration.getInstance();
- mMaxDim = mConfig.taskStackMaxDim;
+ mMaxDimScale = mConfig.taskStackMaxDim / 255f;
mClipViewInStack = true;
mViewBounds = new AnimateableViewBounds(this, mConfig.taskViewRoundedCornerRadiusPx);
setOutlineProvider(mViewBounds);
+ setTaskProgress(getTaskProgress());
setDim(getDim());
}
@@ -159,9 +162,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
/** Synchronizes this view's properties with the task's transform */
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
- // Update the bar view
- mBarView.updateViewPropertiesToTaskTransform(toTransform, duration);
-
// If we are a full screen view, then only update the Z to keep it in order
// XXX: Also update/animate the dim as well
if (mIsFullScreenView) {
@@ -173,8 +173,21 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
}
// Apply the transform
- toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false,
- mUpdateDimListener);
+ toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false);
+
+ // Update the task progress
+ if (mTaskProgressAnimator != null) {
+ mTaskProgressAnimator.removeAllListeners();
+ mTaskProgressAnimator.cancel();
+ }
+ if (duration <= 0) {
+ setTaskProgress(toTransform.p);
+ } else {
+ mTaskProgressAnimator = ObjectAnimator.ofFloat(this, "taskProgress", toTransform.p);
+ mTaskProgressAnimator.setDuration(duration);
+ mTaskProgressAnimator.addUpdateListener(mUpdateDimListener);
+ mTaskProgressAnimator.start();
+ }
}
/** Resets this view's properties */
@@ -325,7 +338,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
mThumbnailView.enableTaskBarClipAsRunnable(mBarView));
// Animate the dim into view as well
- ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
+ ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimFromTaskProgress());
anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
anim.setDuration(mConfig.taskBarEnterAnimDuration);
anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
@@ -556,6 +569,17 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
}
}
+ /** Sets the current task progress. */
+ public void setTaskProgress(float p) {
+ mTaskProgress = p;
+ updateDimFromTaskProgress();
+ }
+
+ /** Returns the current task progress. */
+ public float getTaskProgress() {
+ return mTaskProgress;
+ }
+
/** Returns the current dim. */
public void setDim(int dim) {
mDim = dim;
@@ -571,17 +595,14 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
}
/** Compute the dim as a function of the scale of this view. */
- int getDimOverlayFromScale() {
- float minScale = TaskStackViewLayoutAlgorithm.StackPeekMinScale;
- float scaleRange = 1f - minScale;
- float dim = (1f - getScaleX()) / scaleRange;
- dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
- return Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
+ int getDimFromTaskProgress() {
+ float dim = mMaxDimScale * mDimInterpolator.getInterpolation(1f - mTaskProgress);
+ return (int) (dim * 255);
}
/** Update the dim as a function of the scale of this view. */
- void updateDimOverlayFromScale() {
- setDim(getDimOverlayFromScale());
+ void updateDimFromTaskProgress() {
+ setDim(getDimFromTaskProgress());
}
/**** View focus state ****/
@@ -650,9 +671,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
}
mBarView.rebindToTask(mTask);
// Rebind any listeners
- if (Constants.DebugFlags.App.EnableTaskFiltering) {
- mBarView.mApplicationIcon.setOnClickListener(this);
- }
+ mBarView.mApplicationIcon.setOnClickListener(this);
mBarView.mDismissButton.setOnClickListener(this);
if (mFooterView != null) {
mFooterView.setOnClickListener(this);
@@ -675,9 +694,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
mThumbnailView.unbindFromTask();
mBarView.unbindFromTask();
// Unbind any listeners
- if (Constants.DebugFlags.App.EnableTaskFiltering) {
- mBarView.mApplicationIcon.setOnClickListener(null);
- }
+ mBarView.mApplicationIcon.setOnClickListener(null);
mBarView.mDismissButton.setOnClickListener(null);
if (mFooterView != null) {
mFooterView.setOnClickListener(null);
@@ -717,7 +734,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
postDelayed(new Runnable() {
@Override
public void run() {
- if (v == mBarView.mApplicationIcon) {
+ if (Constants.DebugFlags.App.EnableTaskFiltering && v == mBarView.mApplicationIcon) {
mCb.onTaskViewAppIconClicked(tv);
} else if (v == mBarView.mDismissButton) {
// Animate out the view and call the callback
@@ -729,7 +746,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
});
// Hide the footer
tv.animateFooterVisibility(false, mConfig.taskViewRemoveAnimDuration);
- } else if (v == tv || (v == mFooterView || v == mActionButtonView)) {
+ } else {
mCb.onTaskViewClicked(tv, tv.getTask(),
(v == mFooterView || v == mActionButtonView));
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index aeb4fe4..ce2e80b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -28,13 +28,12 @@ import com.android.systemui.recents.Constants;
public class TaskViewTransform {
public int startDelay = 0;
public int translationY = 0;
- public int translationZ = 0;
+ public float translationZ = 0;
public float scale = 1f;
public float alpha = 1f;
- public float dismissAlpha = 1f;
public boolean visible = false;
public Rect rect = new Rect();
- float t = 0f;
+ float p = 0f;
public TaskViewTransform() {
// Do nothing
@@ -46,10 +45,9 @@ public class TaskViewTransform {
translationZ = o.translationZ;
scale = o.scale;
alpha = o.alpha;
- dismissAlpha = o.dismissAlpha;
visible = o.visible;
rect.set(o.rect);
- t = o.t;
+ p = o.p;
}
/** Resets the current transform */
@@ -59,19 +57,15 @@ public class TaskViewTransform {
translationZ = 0;
scale = 1f;
alpha = 1f;
- dismissAlpha = 1f;
visible = false;
rect.setEmpty();
- t = 0f;
+ p = 0f;
}
/** Convenience functions to compare against current property values */
public boolean hasAlphaChangedFrom(float v) {
return (Float.compare(alpha, v) != 0);
}
- public boolean hasDismissAlphaChangedFrom(float v) {
- return (Float.compare(dismissAlpha, v) != 0);
- }
public boolean hasScaleChangedFrom(float v) {
return (Float.compare(scale, v) != 0);
}
@@ -83,8 +77,7 @@ public class TaskViewTransform {
}
/** Applies this transform to a view. */
- public void applyToTaskView(View v, int duration, Interpolator interp, boolean allowLayers,
- ValueAnimator.AnimatorUpdateListener scaleUpdateListener) {
+ public void applyToTaskView(View v, int duration, Interpolator interp, boolean allowLayers) {
// Check to see if any properties have changed, and update the task view
if (duration > 0) {
ViewPropertyAnimator anim = v.animate();
@@ -100,8 +93,7 @@ public class TaskViewTransform {
}
if (hasScaleChangedFrom(v.getScaleX())) {
anim.scaleX(scale)
- .scaleY(scale)
- .setUpdateListener(scaleUpdateListener);
+ .scaleY(scale);
requiresLayers = true;
}
if (hasAlphaChangedFrom(v.getAlpha())) {
@@ -128,7 +120,6 @@ public class TaskViewTransform {
if (hasScaleChangedFrom(v.getScaleX())) {
v.setScaleX(scale);
v.setScaleY(scale);
- scaleUpdateListener.onAnimationUpdate(null);
}
if (hasAlphaChangedFrom(v.getAlpha())) {
v.setAlpha(alpha);
@@ -152,6 +143,6 @@ public class TaskViewTransform {
public String toString() {
return "TaskViewTransform delay: " + startDelay + " y: " + translationY + " z: " + translationZ +
" scale: " + scale + " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
- " dismissAlpha: " + dismissAlpha;
+ " p: " + p;
}
}