Merge "Enabling filtering by base intent package."

This commit is contained in:
Winson Chung
2014-04-01 23:10:25 +00:00
committed by Android (Google) Code Review
12 changed files with 390 additions and 95 deletions

View File

@@ -26,7 +26,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal" android:layout_gravity="top|center_horizontal"
android:background="#88000000"> android:background="#e6444444">
<ImageView <ImageView
android:id="@+id/activity_icon" android:id="@+id/activity_icon"
android:layout_width="@dimen/recents_task_view_icon_size" android:layout_width="@dimen/recents_task_view_icon_size"

View File

@@ -26,7 +26,7 @@ public class Constants {
public static final boolean Verbose = false; public static final boolean Verbose = false;
public static class App { 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 EnableTaskStackClipping = false;
public static final boolean EnableToggleNewRecentsActivity = false; public static final boolean EnableToggleNewRecentsActivity = false;
// This disables the bitmap and icon caches to // This disables the bitmap and icon caches to
@@ -81,6 +81,10 @@ public class Constants {
public static class Animation { public static class Animation {
public static final int TaskRemovedReshuffleDuration = 200; public static final int TaskRemovedReshuffleDuration = 200;
public static final int SnapScrollBackDuration = 650; 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; public static final int TaskStackOverscrollRange = 150;
@@ -107,7 +111,7 @@ public class Constants {
public static final boolean AnimateFrontTaskIconOnEnterRecents = true; public static final boolean AnimateFrontTaskIconOnEnterRecents = true;
public static final boolean AnimateFrontTaskIconOnLeavingRecents = 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; public static final float RoundedCornerRadiusDps = 3;
} }
} }

View File

@@ -54,8 +54,11 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
finish(); finish();
} }
} else if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) { } else if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
// Dismiss recents and launch the first task if possible // Try and unfilter and filtered stacks
dismissRecentsIfVisible(); if (!mRecentsView.unfilterFilteredStacks()) {
// If there are no filtered stacks, dismiss recents and launch the first task
dismissRecentsIfVisible();
}
} }
} }
}; };

View File

@@ -66,7 +66,7 @@ class SystemUIMessageHandler extends Handler {
// in a bottom inset // in a bottom inset
tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top, 0); tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top, 0);
tsv.boundScroll(); tsv.boundScroll();
TaskViewTransform transform = tsv.getStackTransform(0); TaskViewTransform transform = tsv.getStackTransform(0, tsv.getStackScroll());
Rect taskRect = new Rect(transform.rect); Rect taskRect = new Rect(transform.rect);
data.putParcelable("taskRect", taskRect); data.putParcelable("taskRect", taskRect);

View File

@@ -190,7 +190,7 @@ class TaskResourceLoader implements Runnable {
// Load the icon // Load the icon
if (loadIcon == null || forceLoadTask) { if (loadIcon == null || forceLoadTask) {
PackageManager pm = mContext.getPackageManager(); PackageManager pm = mContext.getPackageManager();
ActivityInfo info = pm.getActivityInfo(t.key.intent.getComponent(), ActivityInfo info = pm.getActivityInfo(t.key.baseIntent.getComponent(),
PackageManager.GET_META_DATA); PackageManager.GET_META_DATA);
Drawable icon = info.loadIcon(pm); Drawable icon = info.loadIcon(pm);
if (!mCancelled) { if (!mCancelled) {
@@ -218,7 +218,7 @@ class TaskResourceLoader implements Runnable {
} else { } else {
Console.logError(mContext, Console.logError(mContext,
"Failed to load task top thumbnail for: " + "Failed to load task top thumbnail for: " +
t.key.intent.getComponent().getPackageName()); t.key.baseIntent.getComponent().getPackageName());
} }
} }
} }

View File

@@ -20,9 +20,6 @@ import android.graphics.Rect;
/* Common code */ /* Common code */
public class Utilities { public class Utilities {
public static final Rect tmpRect = new Rect();
public static final Rect tmpRect2 = new Rect();
/** Scales a rect about its centroid */ /** Scales a rect about its centroid */
public static void scaleRectAboutCenter(Rect r, float scale) { public static void scaleRectAboutCenter(Rect r, float scale) {
if (scale != 1.0f) { if (scale != 1.0f) {

View File

@@ -37,11 +37,11 @@ public class Task {
/* The Task Key represents the unique primary key for the task */ /* The Task Key represents the unique primary key for the task */
public static class TaskKey { public static class TaskKey {
public final int id; public final int id;
public final Intent intent; public final Intent baseIntent;
public TaskKey(int id, Intent intent) { public TaskKey(int id, Intent intent) {
this.id = id; this.id = id;
this.intent = intent; this.baseIntent = intent;
} }
@Override @Override
@@ -56,7 +56,7 @@ public class Task {
@Override @Override
public String toString() { 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 @Override
public String toString() { public String toString() {
return "Task: " + key.intent.getComponent().getPackageName() + " [" + super.toString() + "]"; return "Task: " + key.baseIntent.getComponent().getPackageName() + " [" + super.toString() + "]";
} }
} }

View File

@@ -39,9 +39,11 @@ class FilteredTaskList {
TaskFilter mFilter; TaskFilter mFilter;
/** Sets the task filter, saving the current touch state */ /** 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; mFilter = filter;
updateFilteredTasks(); updateFilteredTasks();
return !prevFilteredTasks.equals(mFilteredTasks);
} }
/** Removes the task filter and returns the previous touch state */ /** 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 */ /* Notifies when a task has been removed from the stack */
public void onStackTaskRemoved(TaskStack stack, Task t); public void onStackTaskRemoved(TaskStack stack, Task t);
/** Notifies when the stack was filtered */ /** 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 */ /** Notifies when the stack was un-filtered */
public void onStackUnfiltered(TaskStack stack); public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curStack);
} }
Context mContext; Context mContext;
@@ -201,29 +203,30 @@ public class TaskStack {
} }
/** Filters the stack into tasks similar to the one specified */ /** 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 // Set the task list filter
// XXX: This is a dummy filter that currently just accepts every other task. boolean filtered = mTaskList.setFilter(new TaskFilter() {
mTaskList.setFilter(new TaskFilter() {
@Override @Override
public boolean acceptTask(Task t, int i) { public boolean acceptTask(Task at, int i) {
if (i % 2 == 0) { return t.key.baseIntent.getComponent().getPackageName().equals(
return true; at.key.baseIntent.getComponent().getPackageName());
}
return false;
} }
}); });
if (mCb != null) { if (filtered && mCb != null) {
mCb.onStackFiltered(this); mCb.onStackFiltered(this, oldStack, t);
} }
} }
/** Unfilters the current stack */ /** Unfilters the current stack */
public void unfilterTasks() { public void unfilterTasks() {
ArrayList<Task> oldStack = new ArrayList<Task>(mTaskList.getTasks());
// Unset the filter, then update the virtual scroll // Unset the filter, then update the virtual scroll
mTaskList.removeFilter(); mTaskList.removeFilter();
if (mCb != null) { if (mCb != null) {
mCb.onStackUnfiltered(this); mCb.onStackUnfiltered(this, oldStack);
} }
} }

View File

@@ -211,17 +211,18 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
View sourceView = tv; View sourceView = tv;
int offsetX = 0; int offsetX = 0;
int offsetY = 0; int offsetY = 0;
int stackScroll = stackView.getStackScroll();
if (tv == null) { if (tv == null) {
// If there is no actual task view, then use the stack view as the source view // 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 // 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) // outside the display rect (to ensure we don't animate from too far away)
RecentsConfiguration config = RecentsConfiguration.getInstance(); RecentsConfiguration config = RecentsConfiguration.getInstance();
sourceView = stackView; sourceView = stackView;
transform = stackView.getStackTransform(stack.indexOfTask(task)); transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
offsetX = transform.rect.left; offsetX = transform.rect.left;
offsetY = Math.min(transform.rect.top, config.displayRect.height()); offsetY = Math.min(transform.rect.top, config.displayRect.height());
} else { } else {
transform = stackView.getStackTransform(stack.indexOfTask(task)); transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
} }
// Compute the thumbnail to scale up from // Compute the thumbnail to scale up from
@@ -255,7 +256,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
} }
} else { } else {
// Launch the activity with the desired animation // 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 i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
| Intent.FLAG_ACTIVITY_TASK_ON_HOME | Intent.FLAG_ACTIVITY_TASK_ON_HOME
| Intent.FLAG_ACTIVITY_NEW_TASK); | Intent.FLAG_ACTIVITY_NEW_TASK);

View File

@@ -18,6 +18,7 @@ package com.android.systemui.recents.views;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.app.Activity; import android.app.Activity;
@@ -34,6 +35,7 @@ import android.view.ViewConfiguration;
import android.view.ViewParent; import android.view.ViewParent;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.OverScroller; import android.widget.OverScroller;
import com.android.systemui.R;
import com.android.systemui.recents.Console; import com.android.systemui.recents.Console;
import com.android.systemui.recents.Constants; import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration; 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.Utilities;
import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack; import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.R;
import java.util.ArrayList; import java.util.ArrayList;
@@ -71,6 +72,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
int mStackScroll; int mStackScroll;
int mMinScroll; int mMinScroll;
int mMaxScroll; int mMaxScroll;
int mStashedScroll;
OverScroller mScroller; OverScroller mScroller;
ObjectAnimator mScrollAnimator; ObjectAnimator mScrollAnimator;
@@ -79,6 +81,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
int mStackViewsAnimationDuration; int mStackViewsAnimationDuration;
boolean mStackViewsDirty = true; boolean mStackViewsDirty = true;
boolean mAwaitingFirstLayout = true; boolean mAwaitingFirstLayout = true;
int[] mTmpVisibleRange = new int[2];
Rect mTmpRect = new Rect();
Rect mTmpRect2 = new Rect();
LayoutInflater mInflater; LayoutInflater mInflater;
public TaskStackView(Context context, TaskStack stack) { public TaskStackView(Context context, TaskStack stack) {
@@ -102,7 +107,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
} }
void requestSynchronizeStackViewsWithModel(int duration) { void requestSynchronizeStackViewsWithModel(int duration) {
Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel, Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel,
"[TaskStackView|requestSynchronize]", "", Console.AnsiYellow); "[TaskStackView|requestSynchronize]", "" + duration + "ms", Console.AnsiYellow);
if (!mStackViewsDirty) { if (!mStackViewsDirty) {
invalidate(); invalidate();
} }
@@ -128,14 +133,17 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
} }
/** Update/get the transform */ /** Update/get the transform */
public TaskViewTransform getStackTransform(int indexInStack) { public TaskViewTransform getStackTransform(int indexInStack, int stackScroll) {
TaskViewTransform transform = new TaskViewTransform(); 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; int numPeekCards = Constants.Values.TaskStackView.StackPeekNumCards;
float overlapHeight = Constants.Values.TaskStackView.StackOverlapPct * mTaskRect.height(); float overlapHeight = Constants.Values.TaskStackView.StackOverlapPct * mTaskRect.height();
float peekHeight = Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.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)); float boundedT = Math.max(t, -(numPeekCards + 1));
// Set the scale relative to its position // Set the scale relative to its position
@@ -167,25 +175,57 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
return transform; 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 */ /** Synchronizes the views with the model */
void synchronizeStackViewsWithModel() { void synchronizeStackViewsWithModel() {
Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel, Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel,
"[TaskStackView|synchronizeViewsWithModel]", "[TaskStackView|synchronizeViewsWithModel]",
"mStackViewsDirty: " + mStackViewsDirty, Console.AnsiYellow); "mStackViewsDirty: " + mStackViewsDirty, Console.AnsiYellow);
if (mStackViewsDirty) { 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: Consider using TaskViewTransform pool to prevent allocations
// XXX: Iterate children views, update transforms and remove all that are not visible // 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 // 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<Task> tasks = mStack.getTasks();
ArrayList<TaskViewTransform> taskTransforms = getStackTransforms(tasks, stackScroll,
visibleRange);
// Update the visible state of all the tasks
int taskCount = tasks.size(); int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) { for (int i = 0; i < taskCount; i++) {
Task task = tasks.get(i); Task task = tasks.get(i);
TaskViewTransform transform = getStackTransform(i); TaskViewTransform transform = taskTransforms.get(i);
TaskView tv = getChildViewForTask(task); TaskView tv = getChildViewForTask(task);
if (transform.visible) { 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 // 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 // following animation by putting it in a reasonable place
if (mStackViewsAnimationDuration > 0 && i != 0) { 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); int fromIndex = (transform.t < 0) ? (i - 1) : (i + 1);
tv.updateViewPropertiesFromTask(null, getStackTransform(fromIndex), 0); tv.updateViewPropertiesToTaskTransform(null,
getStackTransform(fromIndex, stackScroll), 0);
} }
} }
} else { } 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 // NOTE: We have to iterate in reverse where because we are removing views directly
int childCount = getChildCount(); int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) { for (int i = childCount - 1; i >= 0; i--) {
TaskView tv = (TaskView) getChildAt(i); TaskView tv = (TaskView) getChildAt(i);
Task task = tv.getTask(); Task task = tv.getTask();
TaskViewTransform transform = getStackTransform(mStack.indexOfTask(task)); int taskIndex = mStack.indexOfTask(task);
if (!transform.visible) { if (taskIndex < 0 || !taskTransforms.get(taskIndex).visible) {
mViewPool.returnViewToPool(tv); mViewPool.returnViewToPool(tv);
} else { } 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; mStackScroll = value;
requestSynchronizeStackViewsWithModel(); 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 */ /** Gets the current stack scroll */
public int getStackScroll() { public int getStackScroll() {
@@ -251,36 +294,39 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Abort any current animations // Abort any current animations
abortScroller(); abortScroller();
if (mScrollAnimator != null) { abortBoundScrollAnimation();
mScrollAnimator.cancel();
mScrollAnimator.removeAllListeners();
}
// Start a new scroll animation // Start a new scroll animation
mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll); animateScroll(curScroll, newScroll, duration);
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");
}
});
mScrollAnimator.start(); mScrollAnimator.start();
} }
return mScrollAnimator; 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 */ /** Aborts any current stack scrolls */
void abortBoundScrollAnimation() { void abortBoundScrollAnimation() {
if (mScrollAnimator != null) { if (mScrollAnimator != null) {
mScrollAnimator.cancel(); mScrollAnimator.cancel();
mScrollAnimator.removeAllListeners();
} }
} }
@@ -304,6 +350,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
return false; 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 */ /** Returns whether the current scroll is out of bounds */
boolean isScrollOutOfBounds() { boolean isScrollOutOfBounds() {
return (getStackScroll() < 0) || (getStackScroll() > mMaxScroll); return (getStackScroll() < 0) || (getStackScroll() > mMaxScroll);
@@ -404,12 +464,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
TaskView tv = (TaskView) child; TaskView tv = (TaskView) child;
TaskView nextTv = null; TaskView nextTv = null;
int curIndex = indexOfChild(tv); 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) // Clip against the next view (if we aren't animating its alpha)
nextTv = (TaskView) getChildAt(curIndex + 1); nextTv = (TaskView) getChildAt(curIndex + 1);
if (nextTv.getAlpha() == 1f) { if (nextTv.getAlpha() == 1f) {
Rect curRect = tv.getClippingRect(Utilities.tmpRect, false); Rect curRect = tv.getClippingRect(mTmpRect, false);
Rect nextRect = nextTv.getClippingRect(Utilities.tmpRect2, true); Rect nextRect = nextTv.getClippingRect(mTmpRect2, true);
RecentsConfiguration config = RecentsConfiguration.getInstance(); RecentsConfiguration config = RecentsConfiguration.getInstance();
// The hit rects are relative to the task view, which needs to be offset by the // The hit rects are relative to the task view, which needs to be offset by the
// system bar height // system bar height
@@ -528,9 +588,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mTaskRect.right, mStackRectSansPeek.top + mTaskRect.height()); mTaskRect.right, mStackRectSansPeek.top + mTaskRect.height());
} }
if (!mAwaitingFirstLayout) { if (mAwaitingFirstLayout) {
requestSynchronizeStackViewsWithModel();
} else {
mAwaitingFirstLayout = false; mAwaitingFirstLayout = false;
} }
} }
@@ -570,13 +628,185 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
} }
@Override @Override
public void onStackFiltered(TaskStack stack) { public void onStackFiltered(TaskStack newStack, final ArrayList<Task> curStack,
requestSynchronizeStackViewsWithModel(); 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 @Override
public void onStackUnfiltered(TaskStack stack) { public void onStackUnfiltered(TaskStack newStack, final ArrayList<Task> curStack) {
requestSynchronizeStackViewsWithModel(); // 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 ****/ /**** ViewPoolConsumer Implementation ****/
@@ -845,7 +1075,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
/** Handles touch events once we have intercepted them */ /** Handles touch events once we have intercepted them */
public boolean onTouchEvent(MotionEvent ev) { public boolean onTouchEvent(MotionEvent ev) {
Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel, Console.log(Constants.DebugFlags.UI.TouchEvents,
"[TaskStackViewTouchHandler|touchEvent]", "[TaskStackViewTouchHandler|touchEvent]",
Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue); Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
@@ -1045,9 +1275,17 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
ActivityManager.REMOVE_TASK_KILL_PROCESS); 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) { 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 // Disable HW layers

View File

@@ -16,6 +16,9 @@
package com.android.systemui.recents.views; package com.android.systemui.recents.views;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Path; import android.graphics.Path;
@@ -72,6 +75,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task.
// Bind the views // Bind the views
mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail); mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
mBarView = (TaskBarView) findViewById(R.id.task_view_bar); mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
mBarView.mActivityIcon.setOnClickListener(this);
if (mTaskDataLoaded) { if (mTaskDataLoaded) {
onTaskDataLoaded(false); onTaskDataLoaded(false);
} }
@@ -90,12 +94,16 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task.
} }
@Override @Override
protected void onDraw(Canvas canvas) { protected void dispatchDraw(Canvas canvas) {
int restoreCount = 0;
if (Constants.Values.TaskView.UseRoundedCorners) { if (Constants.Values.TaskView.UseRoundedCorners) {
restoreCount = canvas.save();
canvas.clipPath(mRoundedRectClipPath); canvas.clipPath(mRoundedRectClipPath);
} }
super.dispatchDraw(canvas);
super.onDraw(canvas); if (Constants.Values.TaskView.UseRoundedCorners) {
canvas.restoreToCount(restoreCount);
}
} }
/** Set callback */ /** 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 */ /** Synchronizes this view's properties with the task's transform */
void updateViewPropertiesFromTask(TaskViewTransform animateFromTransform, void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
TaskViewTransform transform, int duration) { TaskViewTransform toTransform, int duration) {
if (duration > 0) { if (duration > 0) {
if (animateFromTransform != null) { if (animateFromTransform != null) {
setTranslationY(animateFromTransform.translationY); setTranslationY(animateFromTransform.translationY);
setScaleX(animateFromTransform.scale); setScaleX(animateFromTransform.scale);
setScaleY(animateFromTransform.scale); setScaleY(animateFromTransform.scale);
setAlpha(animateFromTransform.alpha);
} }
animate().translationY(transform.translationY) animate().translationY(toTransform.translationY)
.scaleX(transform.scale) .scaleX(toTransform.scale)
.scaleY(transform.scale) .scaleY(toTransform.scale)
.alpha(toTransform.alpha)
.setDuration(duration) .setDuration(duration)
.setInterpolator(new AccelerateDecelerateInterpolator()) .setInterpolator(new AccelerateDecelerateInterpolator())
.withLayer()
.start(); .start();
} else { } else {
setTranslationY(transform.translationY); setTranslationY(toTransform.translationY);
setScaleX(transform.scale); setScaleX(toTransform.scale);
setScaleY(transform.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 */ /** Resets this view's properties */
void resetViewProperties() { void resetViewProperties() {
setTranslationX(0f); setTranslationX(0f);
@@ -139,6 +163,17 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task.
setAlpha(1f); 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 */ /** Animates this task view as it enters recents */
public void animateOnEnterRecents() { public void animateOnEnterRecents() {
RecentsConfiguration config = RecentsConfiguration.getInstance(); RecentsConfiguration config = RecentsConfiguration.getInstance();

View File

@@ -23,13 +23,27 @@ import android.graphics.Rect;
public class TaskViewTransform { public class TaskViewTransform {
public int translationY = 0; public int translationY = 0;
public float scale = 1f; public float scale = 1f;
public boolean visible = true; public float alpha = 1f;
public boolean visible = false;
public Rect rect = new Rect(); public Rect rect = new Rect();
float t; 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 @Override
public String toString() { public String toString() {
return "TaskViewTransform y: " + translationY + " scale: " + scale + return "TaskViewTransform y: " + translationY + " scale: " + scale + " alpha: " + alpha +
" visible: " + visible + " rect: " + rect; " visible: " + visible + " rect: " + rect;
} }
} }