diff options
12 files changed, 390 insertions, 95 deletions
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml index f6e875c..f5ce222 100644 --- a/packages/SystemUI/res/layout/recents_task_view.xml +++ b/packages/SystemUI/res/layout/recents_task_view.xml @@ -26,7 +26,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top|center_horizontal" - android:background="#88000000"> + android:background="#e6444444"> <ImageView android:id="@+id/activity_icon" android:layout_width="@dimen/recents_task_view_icon_size" diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java index b5950e9..2b08141 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java @@ -26,7 +26,7 @@ public class Constants { public static final boolean Verbose = false; public static class App { - public static final boolean EnableTaskFiltering = false; + public static final boolean EnableTaskFiltering = true; public static final boolean EnableTaskStackClipping = false; public static final boolean EnableToggleNewRecentsActivity = false; // This disables the bitmap and icon caches to @@ -81,6 +81,10 @@ public class Constants { public static class Animation { public static final int TaskRemovedReshuffleDuration = 200; public static final int SnapScrollBackDuration = 650; + public static final int FilteredCurrentViewsDuration = 150; + public static final int FilteredNewViewsDuration = 200; + public static final int UnfilteredCurrentViewsDuration = 150; + public static final int UnfilteredNewViewsDuration = 200; } public static final int TaskStackOverscrollRange = 150; @@ -107,7 +111,7 @@ public class Constants { public static final boolean AnimateFrontTaskIconOnEnterRecents = true; public static final boolean AnimateFrontTaskIconOnLeavingRecents = true; - public static final boolean UseRoundedCorners = true; + public static final boolean UseRoundedCorners = false; public static final float RoundedCornerRadiusDps = 3; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index b65b864..dd75921 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -54,8 +54,11 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView finish(); } } else if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) { - // Dismiss recents and launch the first task if possible - dismissRecentsIfVisible(); + // Try and unfilter and filtered stacks + if (!mRecentsView.unfilterFilteredStacks()) { + // If there are no filtered stacks, dismiss recents and launch the first task + dismissRecentsIfVisible(); + } } } }; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java index 515cec1..22363bb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java @@ -66,7 +66,7 @@ class SystemUIMessageHandler extends Handler { // in a bottom inset tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top, 0); tsv.boundScroll(); - TaskViewTransform transform = tsv.getStackTransform(0); + TaskViewTransform transform = tsv.getStackTransform(0, tsv.getStackScroll()); Rect taskRect = new Rect(transform.rect); data.putParcelable("taskRect", taskRect); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java index b497b69..90c83d0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java @@ -190,7 +190,7 @@ class TaskResourceLoader implements Runnable { // Load the icon if (loadIcon == null || forceLoadTask) { PackageManager pm = mContext.getPackageManager(); - ActivityInfo info = pm.getActivityInfo(t.key.intent.getComponent(), + ActivityInfo info = pm.getActivityInfo(t.key.baseIntent.getComponent(), PackageManager.GET_META_DATA); Drawable icon = info.loadIcon(pm); if (!mCancelled) { @@ -218,7 +218,7 @@ class TaskResourceLoader implements Runnable { } else { Console.logError(mContext, "Failed to load task top thumbnail for: " + - t.key.intent.getComponent().getPackageName()); + t.key.baseIntent.getComponent().getPackageName()); } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java index 33e4246..9048cba 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java @@ -20,9 +20,6 @@ import android.graphics.Rect; /* Common code */ public class Utilities { - public static final Rect tmpRect = new Rect(); - public static final Rect tmpRect2 = new Rect(); - /** Scales a rect about its centroid */ public static void scaleRectAboutCenter(Rect r, float scale) { if (scale != 1.0f) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java index cda4ab2..677334d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java @@ -37,11 +37,11 @@ public class Task { /* The Task Key represents the unique primary key for the task */ public static class TaskKey { public final int id; - public final Intent intent; + public final Intent baseIntent; public TaskKey(int id, Intent intent) { this.id = id; - this.intent = intent; + this.baseIntent = intent; } @Override @@ -56,7 +56,7 @@ public class Task { @Override public String toString() { - return "Task.Key: " + id + ", " + intent.getComponent().getPackageName(); + return "Task.Key: " + id + ", " + baseIntent.getComponent().getPackageName(); } } @@ -120,6 +120,6 @@ public class Task { @Override public String toString() { - return "Task: " + key.intent.getComponent().getPackageName() + " [" + super.toString() + "]"; + return "Task: " + key.baseIntent.getComponent().getPackageName() + " [" + super.toString() + "]"; } } 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 f2f89ae..a0e5b6a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -39,9 +39,11 @@ class FilteredTaskList { TaskFilter mFilter; /** Sets the task filter, saving the current touch state */ - void setFilter(TaskFilter filter) { + boolean setFilter(TaskFilter filter) { + ArrayList<Task> prevFilteredTasks = new ArrayList<Task>(mFilteredTasks); mFilter = filter; updateFilteredTasks(); + return !prevFilteredTasks.equals(mFilteredTasks); } /** Removes the task filter and returns the previous touch state */ @@ -126,9 +128,9 @@ public class TaskStack { /* Notifies when a task has been removed from the stack */ public void onStackTaskRemoved(TaskStack stack, Task t); /** Notifies when the stack was filtered */ - public void onStackFiltered(TaskStack stack); + public void onStackFiltered(TaskStack newStack, ArrayList<Task> curStack, Task t); /** Notifies when the stack was un-filtered */ - public void onStackUnfiltered(TaskStack stack); + public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curStack); } Context mContext; @@ -201,29 +203,30 @@ public class TaskStack { } /** Filters the stack into tasks similar to the one specified */ - public void filterTasks(Task t) { + public void filterTasks(final Task t) { + ArrayList<Task> oldStack = new ArrayList<Task>(mTaskList.getTasks()); + // Set the task list filter - // XXX: This is a dummy filter that currently just accepts every other task. - mTaskList.setFilter(new TaskFilter() { + boolean filtered = mTaskList.setFilter(new TaskFilter() { @Override - public boolean acceptTask(Task t, int i) { - if (i % 2 == 0) { - return true; - } - return false; + public boolean acceptTask(Task at, int i) { + return t.key.baseIntent.getComponent().getPackageName().equals( + at.key.baseIntent.getComponent().getPackageName()); } }); - if (mCb != null) { - mCb.onStackFiltered(this); + if (filtered && mCb != null) { + mCb.onStackFiltered(this, oldStack, t); } } /** Unfilters the current stack */ public void unfilterTasks() { + ArrayList<Task> oldStack = new ArrayList<Task>(mTaskList.getTasks()); + // Unset the filter, then update the virtual scroll mTaskList.removeFilter(); if (mCb != null) { - mCb.onStackUnfiltered(this); + mCb.onStackUnfiltered(this, oldStack); } } 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 cc85439..e89bde5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -211,17 +211,18 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV View sourceView = tv; int offsetX = 0; int offsetY = 0; + int stackScroll = stackView.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) RecentsConfiguration config = RecentsConfiguration.getInstance(); sourceView = stackView; - transform = stackView.getStackTransform(stack.indexOfTask(task)); + transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll); offsetX = transform.rect.left; offsetY = Math.min(transform.rect.top, config.displayRect.height()); } else { - transform = stackView.getStackTransform(stack.indexOfTask(task)); + transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll); } // Compute the thumbnail to scale up from @@ -255,7 +256,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } } else { // Launch the activity with the desired animation - Intent i = new Intent(task.key.intent); + Intent i = new Intent(task.key.baseIntent); i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_TASK_ON_HOME | Intent.FLAG_ACTIVITY_NEW_TASK); 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 e7f517f..aeb571d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -18,6 +18,7 @@ package com.android.systemui.recents.views; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.app.Activity; @@ -34,6 +35,7 @@ import android.view.ViewConfiguration; import android.view.ViewParent; import android.widget.FrameLayout; import android.widget.OverScroller; +import com.android.systemui.R; import com.android.systemui.recents.Console; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; @@ -41,7 +43,6 @@ import com.android.systemui.recents.RecentsTaskLoader; import com.android.systemui.recents.Utilities; import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskStack; -import com.android.systemui.R; import java.util.ArrayList; @@ -71,6 +72,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int mStackScroll; int mMinScroll; int mMaxScroll; + int mStashedScroll; OverScroller mScroller; ObjectAnimator mScrollAnimator; @@ -79,6 +81,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int mStackViewsAnimationDuration; boolean mStackViewsDirty = true; boolean mAwaitingFirstLayout = true; + int[] mTmpVisibleRange = new int[2]; + Rect mTmpRect = new Rect(); + Rect mTmpRect2 = new Rect(); LayoutInflater mInflater; public TaskStackView(Context context, TaskStack stack) { @@ -102,7 +107,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } void requestSynchronizeStackViewsWithModel(int duration) { Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel, - "[TaskStackView|requestSynchronize]", "", Console.AnsiYellow); + "[TaskStackView|requestSynchronize]", "" + duration + "ms", Console.AnsiYellow); if (!mStackViewsDirty) { invalidate(); } @@ -128,14 +133,17 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } /** Update/get the transform */ - public TaskViewTransform getStackTransform(int indexInStack) { + public TaskViewTransform getStackTransform(int indexInStack, int stackScroll) { TaskViewTransform transform = new TaskViewTransform(); - // Map the items to an continuous position relative to the current scroll + // Return early if we have an invalid index + if (indexInStack < 0) return transform; + + // Map the items to an continuous position relative to the specified scroll int numPeekCards = Constants.Values.TaskStackView.StackPeekNumCards; float overlapHeight = Constants.Values.TaskStackView.StackOverlapPct * mTaskRect.height(); float peekHeight = Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height(); - float t = ((indexInStack * overlapHeight) - getStackScroll()) / overlapHeight; + float t = ((indexInStack * overlapHeight) - stackScroll) / overlapHeight; float boundedT = Math.max(t, -(numPeekCards + 1)); // Set the scale relative to its position @@ -167,25 +175,57 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal return transform; } + /** + * Gets the stack transforms of a list of tasks, and returns the visible range of tasks. + */ + private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks, + int stackScroll, + int[] visibleRangeOut) { + // XXX: Optimization: Use binary search to find the visible range + + ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>(); + int taskCount = tasks.size(); + int firstVisibleIndex = -1; + int lastVisibleIndex = -1; + for (int i = 0; i < taskCount; i++) { + TaskViewTransform transform = getStackTransform(i, stackScroll); + taskTransforms.add(transform); + if (transform.visible) { + if (firstVisibleIndex < 0) { + firstVisibleIndex = i; + } + lastVisibleIndex = i; + } + } + if (visibleRangeOut != null) { + visibleRangeOut[0] = firstVisibleIndex; + visibleRangeOut[1] = lastVisibleIndex; + } + return taskTransforms; + } + /** Synchronizes the views with the model */ void synchronizeStackViewsWithModel() { Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel, "[TaskStackView|synchronizeViewsWithModel]", "mStackViewsDirty: " + mStackViewsDirty, Console.AnsiYellow); if (mStackViewsDirty) { - - // XXX: Optimization: Use binary search to find the visible range - // XXX: Optimize to not call getStackTransform() so many times // XXX: Consider using TaskViewTransform pool to prevent allocations // XXX: Iterate children views, update transforms and remove all that are not visible // For all remaining tasks, update transforms and if visible add the view - // Update the visible state of all the tasks + // Get all the task transforms + int[] visibleRange = mTmpVisibleRange; + int stackScroll = getStackScroll(); ArrayList<Task> tasks = mStack.getTasks(); + ArrayList<TaskViewTransform> taskTransforms = getStackTransforms(tasks, stackScroll, + visibleRange); + + // Update the visible state of all the tasks int taskCount = tasks.size(); for (int i = 0; i < taskCount; i++) { Task task = tasks.get(i); - TaskViewTransform transform = getStackTransform(i); + TaskViewTransform transform = taskTransforms.get(i); TaskView tv = getChildViewForTask(task); if (transform.visible) { @@ -194,11 +234,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // When we are picking up a new view from the view pool, prepare it for any // following animation by putting it in a reasonable place if (mStackViewsAnimationDuration > 0 && i != 0) { - // XXX: We have to animate when filtering, etc. Maybe we should have a - // runnable that ensures that tasks are animated in a special way - // when they are entering the scene? int fromIndex = (transform.t < 0) ? (i - 1) : (i + 1); - tv.updateViewPropertiesFromTask(null, getStackTransform(fromIndex), 0); + tv.updateViewPropertiesToTaskTransform(null, + getStackTransform(fromIndex, stackScroll), 0); } } } else { @@ -208,17 +246,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } - // Update all the current view children + // Update all the remaining view children // NOTE: We have to iterate in reverse where because we are removing views directly int childCount = getChildCount(); for (int i = childCount - 1; i >= 0; i--) { TaskView tv = (TaskView) getChildAt(i); Task task = tv.getTask(); - TaskViewTransform transform = getStackTransform(mStack.indexOfTask(task)); - if (!transform.visible) { + int taskIndex = mStack.indexOfTask(task); + if (taskIndex < 0 || !taskTransforms.get(taskIndex).visible) { mViewPool.returnViewToPool(tv); } else { - tv.updateViewPropertiesFromTask(null, transform, mStackViewsAnimationDuration); + tv.updateViewPropertiesToTaskTransform(null, taskTransforms.get(taskIndex), + mStackViewsAnimationDuration); } } @@ -235,6 +274,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mStackScroll = value; requestSynchronizeStackViewsWithModel(); } + /** Sets the current stack scroll without synchronizing the stack view with the model */ + public void setStackScrollRaw(int value) { + mStackScroll = value; + } /** Gets the current stack scroll */ public int getStackScroll() { @@ -251,36 +294,39 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Abort any current animations abortScroller(); - if (mScrollAnimator != null) { - mScrollAnimator.cancel(); - mScrollAnimator.removeAllListeners(); - } + abortBoundScrollAnimation(); // Start a new scroll animation - mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll); - mScrollAnimator.setDuration(duration); - 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) { - // Disable hw layers on the stack - decHwLayersRefCount("animateBoundScroll"); - } - }); + animateScroll(curScroll, newScroll, duration); mScrollAnimator.start(); } return mScrollAnimator; } + /** Animates the stack scroll */ + void animateScroll(int curScroll, int newScroll, int duration) { + mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll); + mScrollAnimator.setDuration(duration); + 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) { + // Disable hw layers on the stack + decHwLayersRefCount("animateBoundScroll"); + } + }); + } + /** Aborts any current stack scrolls */ void abortBoundScrollAnimation() { if (mScrollAnimator != null) { mScrollAnimator.cancel(); + mScrollAnimator.removeAllListeners(); } } @@ -304,6 +350,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 whether the current scroll is out of bounds */ boolean isScrollOutOfBounds() { return (getStackScroll() < 0) || (getStackScroll() > mMaxScroll); @@ -404,12 +464,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal TaskView tv = (TaskView) child; TaskView nextTv = null; int curIndex = indexOfChild(tv); - if (curIndex < (getChildCount() - 1)) { + if ((curIndex > -1) && (curIndex < (getChildCount() - 1))) { // Clip against the next view (if we aren't animating its alpha) nextTv = (TaskView) getChildAt(curIndex + 1); if (nextTv.getAlpha() == 1f) { - Rect curRect = tv.getClippingRect(Utilities.tmpRect, false); - Rect nextRect = nextTv.getClippingRect(Utilities.tmpRect2, true); + Rect curRect = tv.getClippingRect(mTmpRect, false); + Rect nextRect = nextTv.getClippingRect(mTmpRect2, true); RecentsConfiguration config = RecentsConfiguration.getInstance(); // The hit rects are relative to the task view, which needs to be offset by the // system bar height @@ -528,9 +588,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mTaskRect.right, mStackRectSansPeek.top + mTaskRect.height()); } - if (!mAwaitingFirstLayout) { - requestSynchronizeStackViewsWithModel(); - } else { + if (mAwaitingFirstLayout) { mAwaitingFirstLayout = false; } } @@ -570,13 +628,185 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } @Override - public void onStackFiltered(TaskStack stack) { - requestSynchronizeStackViewsWithModel(); + public void onStackFiltered(TaskStack newStack, final ArrayList<Task> curStack, + Task filteredTask) { + // NOTE: This code assumes that the current (unfiltered) stack is a superset of the new + // (filtered) stack + // XXX: Use HW Layers + + // Stash the scroll for us to restore to when we unfilter + mStashedScroll = getStackScroll(); + + // Compute the transforms of the items in the current stack + final ArrayList<TaskViewTransform> curTaskTransforms = + getStackTransforms(curStack, mStashedScroll, null); + + // Bound the new stack scroll + updateMinMaxScroll(false); + boundScrollRaw(); + + // Compute the transforms of the items in the new stack + final ArrayList<TaskViewTransform> taskTransforms = + getStackTransforms(mStack.getTasks(), getStackScroll(), null); + + // Animate all of the existing views on screen either out of view (if they are not visible + // in the new stack) or to their final positions in the new stack + final ArrayList<TaskView> childrenToReturnToPool = new ArrayList<TaskView>(); + final ArrayList<Task> tasks = mStack.getTasks(); + ArrayList<Animator> childViewAnims = new ArrayList<Animator>(); + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + TaskView tv = (TaskView) getChildAt(i); + Task task = tv.getTask(); + TaskViewTransform toTransform; + int taskIndex = tasks.indexOf(task); + if ((taskIndex < 0) || !taskTransforms.get(taskIndex).visible) { + // Compose a new transform that animates the task view out of view + TaskViewTransform fromTransform = curTaskTransforms.get(curStack.indexOf(task)); + toTransform = new TaskViewTransform(fromTransform); + tv.updateViewPropertiesToTaskTransform(null, fromTransform, 0); + tv.prepareTaskTransformForFilterTaskHidden(toTransform); + + childrenToReturnToPool.add(tv); + } else { + toTransform = taskTransforms.get(taskIndex); + } + childViewAnims.add(tv.getAnimatorToTaskTransform(toTransform)); + } + + AnimatorSet childViewAnimSet = new AnimatorSet(); + childViewAnimSet.setDuration( + Constants.Values.TaskStackView.Animation.FilteredCurrentViewsDuration); + childViewAnimSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Return all the removed children to the view pool + for (TaskView tv : childrenToReturnToPool) { + mViewPool.returnViewToPool(tv); + } + + // For views that are not already visible, animate them in + int taskCount = tasks.size(); + for (int i = 0; i < taskCount; i++) { + Task task = tasks.get(i); + TaskViewTransform toTransform = taskTransforms.get(i); + if (toTransform.visible) { + TaskViewTransform fromTransform = + curTaskTransforms.get(curStack.indexOf(task)); + TaskView tv = getChildViewForTask(task); + if (tv == null) { + tv = mViewPool.pickUpViewFromPool(task, task); + + // Animate from the current position to the new position + tv.prepareTaskTransformForFilterTaskVisible(fromTransform); + tv.updateViewPropertiesToTaskTransform(fromTransform, + toTransform, + Constants.Values.TaskStackView.Animation.FilteredNewViewsDuration); + } + } + } + invalidate(); + } + }); + childViewAnimSet.playTogether(childViewAnims); + childViewAnimSet.start(); } @Override - public void onStackUnfiltered(TaskStack stack) { - requestSynchronizeStackViewsWithModel(); + public void onStackUnfiltered(TaskStack newStack, final ArrayList<Task> curStack) { + // Compute the transforms of the items in the current stack + final int curScroll = getStackScroll(); + final ArrayList<TaskViewTransform> curTaskTransforms = + getStackTransforms(curStack, curScroll, null); + + // Restore the stashed scroll + updateMinMaxScroll(false); + setStackScrollRaw(mStashedScroll); + boundScrollRaw(); + + // Compute the transforms of the items in the new stack + final ArrayList<TaskViewTransform> taskTransforms = + getStackTransforms(mStack.getTasks(), getStackScroll(), null); + + // Animate all of the existing views out of view (if they are not in the visible range in + // the new stack) or to their final positions in the new stack + final ArrayList<TaskView> childrenToRemove = new ArrayList<TaskView>(); + final ArrayList<Task> tasks = mStack.getTasks(); + ArrayList<Animator> childViewAnims = new ArrayList<Animator>(); + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + TaskView tv = (TaskView) getChildAt(i); + Task task = tv.getTask(); + int taskIndex = tasks.indexOf(task); + TaskViewTransform transform; + + // If the view is no longer visible, then we should just animate it out + if (taskIndex < 0 || !taskTransforms.get(taskIndex).visible) { + transform = new TaskViewTransform(curTaskTransforms.get(curStack.indexOf(task))); + tv.prepareTaskTransformForFilterTaskVisible(transform); + childrenToRemove.add(tv); + } else { + transform = taskTransforms.get(taskIndex); + } + childViewAnims.add(tv.getAnimatorToTaskTransform(transform)); + } + + AnimatorSet childViewAnimSet = new AnimatorSet(); + childViewAnimSet.setDuration( + Constants.Values.TaskStackView.Animation.UnfilteredCurrentViewsDuration); + childViewAnimSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Return all the removed children to the view pool + for (TaskView tv : childrenToRemove) { + mViewPool.returnViewToPool(tv); + } + + // Increment the hw layers ref count + addHwLayersRefCount("unfilteredNewViews"); + + // For views that are not already visible, animate them in + ArrayList<Animator> newViewAnims = new ArrayList<Animator>(); + AnimatorSet newViewAnimSet = new AnimatorSet(); + int taskCount = tasks.size(); + int offset = 0; + for (int i = 0; i < taskCount; i++) { + Task task = tasks.get(i); + TaskViewTransform toTransform = taskTransforms.get(i); + if (toTransform.visible) { + TaskView tv = getChildViewForTask(task); + if (tv == null) { + // For views that are not already visible, animate them in + tv = mViewPool.pickUpViewFromPool(task, task); + + // Animate in this new view + TaskViewTransform fromTransform = new TaskViewTransform(toTransform); + tv.prepareTaskTransformForFilterTaskHidden(fromTransform); + tv.updateViewPropertiesToTaskTransform(null, fromTransform, 0); + newViewAnims.add(tv.getAnimatorToTaskTransform(toTransform)); + offset++; + } + } + } + + // Run the animation + newViewAnimSet.setDuration( + Constants.Values.TaskStackView.Animation.UnfilteredNewViewsDuration); + newViewAnimSet.playTogether(newViewAnims); + newViewAnimSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Decrement the hw layers ref count + decHwLayersRefCount("unfilteredNewViews"); + } + }); + newViewAnimSet.start(); + + invalidate(); + } + }); + childViewAnimSet.playTogether(childViewAnims); + childViewAnimSet.start(); } /**** ViewPoolConsumer Implementation ****/ @@ -845,7 +1075,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { /** Handles touch events once we have intercepted them */ public boolean onTouchEvent(MotionEvent ev) { - Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel, + Console.log(Constants.DebugFlags.UI.TouchEvents, "[TaskStackViewTouchHandler|touchEvent]", Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue); @@ -1045,9 +1275,17 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { ActivityManager.REMOVE_TASK_KILL_PROCESS); } - // If there are no remaining tasks, then just close the activity + // If there are no remaining tasks, then either unfilter the current stack, or just close + // the activity if there are no filtered stacks if (mSv.mStack.getTaskCount() == 0) { - activity.finish(); + boolean shouldFinishActivity = true; + if (mSv.mStack.hasFilteredTasks()) { + mSv.mStack.unfilterTasks(); + shouldFinishActivity = (mSv.mStack.getTaskCount() == 0); + } + if (shouldFinishActivity) { + activity.finish(); + } } // Disable HW layers 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 c86b0e1..e04a1b2 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -16,6 +16,9 @@ package com.android.systemui.recents.views; +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Path; @@ -72,6 +75,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task. // Bind the views mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail); mBarView = (TaskBarView) findViewById(R.id.task_view_bar); + mBarView.mActivityIcon.setOnClickListener(this); if (mTaskDataLoaded) { onTaskDataLoaded(false); } @@ -90,12 +94,16 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task. } @Override - protected void onDraw(Canvas canvas) { + protected void dispatchDraw(Canvas canvas) { + int restoreCount = 0; if (Constants.Values.TaskView.UseRoundedCorners) { + restoreCount = canvas.save(); canvas.clipPath(mRoundedRectClipPath); } - - super.onDraw(canvas); + super.dispatchDraw(canvas); + if (Constants.Values.TaskView.UseRoundedCorners) { + canvas.restoreToCount(restoreCount); + } } /** Set callback */ @@ -109,27 +117,43 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task. } /** Synchronizes this view's properties with the task's transform */ - void updateViewPropertiesFromTask(TaskViewTransform animateFromTransform, - TaskViewTransform transform, int duration) { + void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform, + TaskViewTransform toTransform, int duration) { if (duration > 0) { if (animateFromTransform != null) { setTranslationY(animateFromTransform.translationY); setScaleX(animateFromTransform.scale); setScaleY(animateFromTransform.scale); + setAlpha(animateFromTransform.alpha); } - animate().translationY(transform.translationY) - .scaleX(transform.scale) - .scaleY(transform.scale) + animate().translationY(toTransform.translationY) + .scaleX(toTransform.scale) + .scaleY(toTransform.scale) + .alpha(toTransform.alpha) .setDuration(duration) .setInterpolator(new AccelerateDecelerateInterpolator()) + .withLayer() .start(); } else { - setTranslationY(transform.translationY); - setScaleX(transform.scale); - setScaleY(transform.scale); + setTranslationY(toTransform.translationY); + setScaleX(toTransform.scale); + setScaleY(toTransform.scale); + setAlpha(toTransform.alpha); } } + /** Returns an animator to animate this task to the specified transform */ + Animator getAnimatorToTaskTransform(TaskViewTransform toTransform) { + AnimatorSet anims = new AnimatorSet(); + anims.playTogether( + ObjectAnimator.ofFloat(this, "translationY", toTransform.translationY), + ObjectAnimator.ofFloat(this, "scaleX", toTransform.scale), + ObjectAnimator.ofFloat(this, "scaleY", toTransform.scale), + ObjectAnimator.ofFloat(this, "alpha", toTransform.alpha) + ); + return anims; + } + /** Resets this view's properties */ void resetViewProperties() { setTranslationX(0f); @@ -139,6 +163,17 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task. setAlpha(1f); } + void prepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform) { + // Fade the view out and slide it away + toTransform.alpha = 0f; + toTransform.translationY += 200; + } + + void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) { + // Fade the view in + fromTransform.alpha = 0f; + } + /** Animates this task view as it enters recents */ public void animateOnEnterRecents() { RecentsConfiguration config = RecentsConfiguration.getInstance(); 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 66c52a0..0748bbb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java @@ -23,13 +23,27 @@ import android.graphics.Rect; public class TaskViewTransform { public int translationY = 0; public float scale = 1f; - public boolean visible = true; + public float alpha = 1f; + public boolean visible = false; public Rect rect = new Rect(); float t; + public TaskViewTransform() { + // Do nothing + } + + public TaskViewTransform(TaskViewTransform o) { + translationY = o.translationY; + scale = o.scale; + alpha = o.alpha; + visible = o.visible; + rect.set(o.rect); + t = o.t; + } + @Override public String toString() { - return "TaskViewTransform y: " + translationY + " scale: " + scale + + return "TaskViewTransform y: " + translationY + " scale: " + scale + " alpha: " + alpha + " visible: " + visible + " rect: " + rect; } } |