diff options
author | Winson Chung <winson.chung@gmail.com> | 2014-07-31 18:36:25 -0700 |
---|---|---|
committer | Winson Chung <winsonc@google.com> | 2014-08-01 04:32:08 +0000 |
commit | 012ef36a6c5e9745d112c734aed916cab052558c (patch) | |
tree | cf8578a0aae30bd4a048ac64d5047156b3602afd /packages/SystemUI/src/com/android/systemui/recents | |
parent | 7c3a95633d307c4be30c9dbbf1071063aa7a3c64 (diff) | |
download | frameworks_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')
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; } } |