Merge "Allow for different rects when animating to a single task stack view and a multiple task stack view."

This commit is contained in:
Winson Chung
2014-04-09 23:50:32 +00:00
committed by Android (Google) Code Review
10 changed files with 116 additions and 99 deletions

View File

@ -43,7 +43,11 @@
android:textSize="24sp"
android:textColor="#ffffffff"
android:text="@string/recents_empty_message"
android:fontFamily="sans-serif-thin" />
android:fontFamily="sans-serif-thin"
android:singleLine="true"
android:maxLines="2"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
<ImageView
android:id="@+id/activity_icon"
android:layout_width="@dimen/recents_task_view_activity_icon_size"

View File

@ -112,5 +112,7 @@
<integer name="recents_filter_animate_current_views_min_duration">175</integer>
<!-- The min animation duration for animating views that are newly visible. -->
<integer name="recents_filter_animate_new_views_min_duration">125</integer>
<!-- The min animation duration for animating views that are newly visible. -->
<integer name="recents_animate_task_bar_enter_duration">200</integer>
</resources>

View File

@ -44,6 +44,7 @@ import android.view.View;
import android.view.WindowManager;
import com.android.systemui.R;
import java.util.Iterator;
import java.util.List;
/** A proxy implementation for the recents component */
@ -57,8 +58,11 @@ public class AlternateRecentsComponent {
Resources res = mContext.getResources();
float statusBarHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
mFirstTaskRect = (Rect) msg.getData().getParcelable("taskRect");
mFirstTaskRect.offset(0, (int) statusBarHeight);
Bundle replyData = msg.getData().getParcelable("replyData");
mSingleCountFirstTaskRect = replyData.getParcelable("singleCountTaskRect");
mSingleCountFirstTaskRect.offset(0, (int) statusBarHeight);
mMultipleCountFirstTaskRect = replyData.getParcelable("multipleCountTaskRect");
mMultipleCountFirstTaskRect.offset(0, (int) statusBarHeight);
}
}
}
@ -114,7 +118,8 @@ public class AlternateRecentsComponent {
RecentsServiceConnection mConnection = new RecentsServiceConnection();
View mStatusBarView;
Rect mFirstTaskRect = new Rect();
Rect mSingleCountFirstTaskRect = new Rect();
Rect mMultipleCountFirstTaskRect = new Rect();
long mLastToggleTime;
public AlternateRecentsComponent(Context context) {
@ -227,20 +232,24 @@ public class AlternateRecentsComponent {
return null;
}
/** Returns whether there is a first task */
boolean hasFirstTask() {
/** Returns whether there is are multiple recents tasks */
boolean hasMultipleRecentsTask() {
// NOTE: Currently there's no method to get the number of non-home tasks, so we have to
// compute this ourselves
SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> tasks = ssp.getRecentTasks(1,
List<ActivityManager.RecentTaskInfo> tasks = ssp.getRecentTasks(4,
UserHandle.CURRENT.getIdentifier());
for (ActivityManager.RecentTaskInfo t : tasks) {
Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
while (iter.hasNext()) {
ActivityManager.RecentTaskInfo t = iter.next();
// Skip tasks in the home stack
if (ssp.isInHomeStack(t.persistentId)) {
iter.remove();
continue;
}
return true;
}
return false;
return (tasks.size() > 1);
}
/** Converts from the device rotation to the degree */
@ -287,8 +296,10 @@ public class AlternateRecentsComponent {
// to launch the first task or dismiss itself
SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
boolean isTopTaskHome = false;
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
ActivityManager.RunningTaskInfo topTask = tasks.get(0);
ComponentName topActivity = topTask.topActivity;
// Check if the front most activity is recents
if (topActivity.getPackageName().equals(sRecentsPackage) &&
@ -311,16 +322,30 @@ public class AlternateRecentsComponent {
mLastToggleTime = System.currentTimeMillis();
return;
}
// Determine whether the top task is currently home
isTopTaskHome = ssp.isInHomeStack(topTask.id);
}
// Otherwise, Recents is not the front-most activity and we should animate into it
Rect taskRect = mFirstTaskRect;
if (taskRect != null && taskRect.width() > 0 && taskRect.height() > 0 && hasFirstTask()) {
boolean hasMultipleTasks = hasMultipleRecentsTask();
Rect taskRect = hasMultipleTasks ? mMultipleCountFirstTaskRect : mSingleCountFirstTaskRect;
if (!isTopTaskHome && taskRect != null && taskRect.width() > 0 && taskRect.height() > 0) {
// Loading from thumbnail
Bitmap thumbnail;
Bitmap firstThumbnail = loadFirstTaskThumbnail();
if (firstThumbnail == null) {
// Load the thumbnail from the screenshot
if (firstThumbnail != null) {// Create the thumbnail
thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
Bitmap.Config.ARGB_8888);
int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight());
Canvas c = new Canvas(thumbnail);
c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size),
new Rect(0, 0, taskRect.width(), taskRect.height()), null);
c.setBitmap(null);
// Recycle the old thumbnail
firstThumbnail.recycle();
} else {
// Load the thumbnail from the screenshot if can't get one from the system
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Bitmap screenshot = takeScreenshot(display);
@ -328,29 +353,18 @@ public class AlternateRecentsComponent {
int size = Math.min(screenshot.getWidth(), screenshot.getHeight());
int statusBarHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
thumbnail = Bitmap.createBitmap(mFirstTaskRect.width(), mFirstTaskRect.height(),
thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(thumbnail);
c.drawBitmap(screenshot, new Rect(0, statusBarHeight, size, statusBarHeight + size),
new Rect(0, 0, mFirstTaskRect.width(), mFirstTaskRect.height()), null);
new Rect(0, 0, taskRect.width(), taskRect.height()), null);
c.setBitmap(null);
// Recycle the old screenshot
// Recycle the temporary screenshot
screenshot.recycle();
} else {
// Create the thumbnail
thumbnail = Bitmap.createBitmap(mFirstTaskRect.width(), mFirstTaskRect.height(),
Bitmap.Config.ARGB_8888);
int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight());
Canvas c = new Canvas(thumbnail);
c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size),
new Rect(0, 0, mFirstTaskRect.width(), mFirstTaskRect.height()), null);
c.setBitmap(null);
// Recycle the old thumbnail
firstThumbnail.recycle();
}
ActivityOptions opts = ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView,
thumbnail, mFirstTaskRect.left, mFirstTaskRect.top, null);
thumbnail, taskRect.left, taskRect.top, null);
startAlternateRecentsActivity(opts);
} else {
ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,

View File

@ -72,8 +72,6 @@ public class Constants {
public static class Window {
// The dark background dim is set behind the empty recents view
public static final float DarkBackgroundDim = 0.5f;
// The background dim is set behind the card stack
public static final float BackgroundDim = 0.35f;
}
public static class RecentsTaskLoader {
@ -100,9 +98,6 @@ public class Constants {
public static class TaskView {
public static final boolean AnimateFrontTaskIconOnEnterRecents = true;
public static final boolean AnimateFrontTaskIconOnLeavingRecents = true;
public static final boolean UseRoundedCorners = false;
public static final float RoundedCornerRadiusDps = 3;
}
}
}

View File

@ -39,6 +39,7 @@ public class RecentsConfiguration {
public int filteringCurrentViewsMinAnimDuration;
public int filteringNewViewsMinAnimDuration;
public int taskBarEnterAnimDuration;
/** Private constructor */
private RecentsConfiguration() {}
@ -76,6 +77,8 @@ public class RecentsConfiguration {
res.getInteger(R.integer.recents_filter_animate_current_views_min_duration);
filteringNewViewsMinAnimDuration =
res.getInteger(R.integer.recents_filter_animate_new_views_min_duration);
taskBarEnterAnimDuration =
res.getInteger(R.integer.recents_animate_task_bar_enter_duration);
}
public void updateSystemInsets(Rect insets) {

View File

@ -26,6 +26,7 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.TaskStackView;
import com.android.systemui.recents.views.TaskViewTransform;
@ -62,14 +63,27 @@ class SystemUIMessageHandler extends Handler {
// Create a dummy task stack & compute the rect for the thumbnail to animate to
TaskStack stack = new TaskStack(context);
TaskStackView tsv = new TaskStackView(context, stack);
// Since the nav bar height is already accounted for in the windowRect, don't pass
// in a bottom inset
Bundle replyData = new Bundle();
TaskViewTransform transform;
// Calculate the target task rect for when there is one task
// NOTE: Since the nav bar height is already accounted for in the windowRect, don't
// pass in a bottom inset
stack.addTask(new Task());
tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top, 0);
tsv.boundScroll();
TaskViewTransform transform = tsv.getStackTransform(0, tsv.getStackScroll());
Rect taskRect = new Rect(transform.rect);
transform = tsv.getStackTransform(0, tsv.getStackScroll());
replyData.putParcelable("singleCountTaskRect", new Rect(transform.rect));
data.putParcelable("taskRect", taskRect);
// Also calculate the target task rect when there are multiple tasks
stack.addTask(new Task());
tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top, 0);
tsv.setStackScrollRaw(Integer.MAX_VALUE);
tsv.boundScroll();
transform = tsv.getStackTransform(1, tsv.getStackScroll());
replyData.putParcelable("multipleCountTaskRect", new Rect(transform.rect));
data.putParcelable("replyData", replyData);
Message reply = Message.obtain(null,
RecentsService.MSG_UPDATE_RECENTS_FOR_CONFIGURATION, 0, 0);
reply.setData(data);

View File

@ -358,18 +358,9 @@ public class RecentsTaskLoader {
return mSystemServicesProxy;
}
/** Reload the set of recent tasks */
SpaceNode reload(Context context, int preloadCount) {
Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
Resources res = context.getResources();
ArrayList<Task> tasksToForceLoad = new ArrayList<Task>();
TaskStack stack = new TaskStack(context);
SpaceNode root = new SpaceNode(context);
root.setStack(stack);
private List<ActivityManager.RecentTaskInfo> getRecentTasks(Context context) {
long t1 = System.currentTimeMillis();
// Get the recent tasks
SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> tasks =
ssp.getRecentTasks(25, UserHandle.CURRENT.getIdentifier());
@ -397,6 +388,24 @@ public class RecentsTaskLoader {
}
}
return tasks;
}
/** Reload the set of recent tasks */
SpaceNode reload(Context context, int preloadCount) {
long t1 = System.currentTimeMillis();
Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
Resources res = context.getResources();
ArrayList<Task> tasksToForceLoad = new ArrayList<Task>();
TaskStack stack = new TaskStack(context);
SpaceNode root = new SpaceNode(context);
root.setStack(stack);
// Get the recent tasks
SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(context);
// Add each task to the task stack
t1 = System.currentTimeMillis();
int taskCount = tasks.size();

View File

@ -69,6 +69,10 @@ public class Task {
TaskCallbacks mCb;
public Task() {
// Only used by RecentsService for task rect calculations.
}
public Task(int id, boolean isActive, Intent intent, String activityTitle,
Bitmap activityIcon, int userId) {
this.key = new TaskKey(id, intent);

View File

@ -171,7 +171,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
transform.visible = false;
} else {
transform.rect.offset(0, transform.translationY);
Utilities.scaleRectAboutCenter(transform.rect, scale);
Utilities.scaleRectAboutCenter(transform.rect, transform.scale);
transform.visible = Rect.intersects(mRect, transform.rect);
}
transform.t = t;
@ -388,8 +388,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
int stackHeight = mStackRectSansPeek.height();
int maxScrollHeight = taskHeight + (int) ((numTasks - 1) *
Constants.Values.TaskStackView.StackOverlapPct * taskHeight);
if (numTasks <= 1) {
// If there is only one task, then center the task in the stack rect (sans peek)
mMinScroll = mMaxScroll = -(stackHeight - taskHeight) / 2;
} else {
mMinScroll = Math.min(stackHeight, maxScrollHeight) - stackHeight;
mMaxScroll = maxScrollHeight - stackHeight;
}
// Debug logging
if (Constants.DebugFlags.UI.MeasureAndLayout) {
@ -479,8 +485,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// 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(mTmpRect, false);
Rect nextRect = nextTv.getClippingRect(mTmpRect2, true);
Rect curRect = tv.getClippingRect(mTmpRect);
Rect nextRect = nextTv.getClippingRect(mTmpRect2);
RecentsConfiguration config = RecentsConfiguration.getInstance();
// The hit rects are relative to the task view, which needs to be offset by the
// system bar height
@ -523,7 +529,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
int minHeight = (int) (mStackRect.height() -
(Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height()));
int size = Math.min(minHeight, Math.min(mStackRect.width(), mStackRect.height()));
int centerX = mStackRect.centerX();
mTaskRect.set(mStackRect.left, mStackRectSansPeek.top,
mStackRect.right, mStackRectSansPeek.top + size);

View File

@ -20,9 +20,8 @@ import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
@ -36,8 +35,6 @@ import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.Utilities;
import com.android.systemui.recents.model.Task;
import java.util.Random;
/* A task view */
public class TaskView extends FrameLayout implements View.OnClickListener, Task.TaskCallbacks {
@ -54,8 +51,6 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task.
TaskBarView mBarView;
TaskViewCallbacks mCb;
Path mRoundedRectClipPath = new Path();
public TaskView(Context context) {
this(context, null);
@ -71,7 +66,6 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task.
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setWillNotDraw(false);
}
@Override
@ -85,31 +79,6 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task.
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Update the rounded rect clip path
RecentsConfiguration config = RecentsConfiguration.getInstance();
float radius = config.pxFromDp(Constants.Values.TaskView.RoundedCornerRadiusDps);
mRoundedRectClipPath.reset();
mRoundedRectClipPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()),
radius, radius, Path.Direction.CW);
}
@Override
protected void dispatchDraw(Canvas canvas) {
int restoreCount = 0;
if (Constants.Values.TaskView.UseRoundedCorners) {
restoreCount = canvas.save();
canvas.clipPath(mRoundedRectClipPath);
}
super.dispatchDraw(canvas);
if (Constants.Values.TaskView.UseRoundedCorners) {
canvas.restoreToCount(restoreCount);
}
}
/** Set callback */
void setCallbacks(TaskViewCallbacks cb) {
mCb = cb;
@ -195,7 +164,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task.
.translationY(0)
.setStartDelay(235)
.setInterpolator(BakedBezierInterpolator.INSTANCE)
.setDuration(Utilities.calculateTranslationAnimationDuration(translate))
.setDuration(config.taskBarEnterAnimDuration)
.withLayer()
.start();
}
@ -214,23 +183,21 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task.
.setInterpolator(BakedBezierInterpolator.INSTANCE)
.setDuration(Utilities.calculateTranslationAnimationDuration(translate))
.withLayer()
.withEndAction(r)
.withEndAction(new Runnable() {
@Override
public void run() {
post(r);
}
})
.start();
}
/** Returns the rect we want to clip (it may not be the full rect) */
Rect getClippingRect(Rect outRect, boolean accountForRoundedRects) {
Rect getClippingRect(Rect outRect) {
getHitRect(outRect);
// XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
outRect.right = outRect.left + mThumbnailView.getRight();
outRect.bottom = outRect.top + mThumbnailView.getBottom();
// We need to shrink the next rect by the rounded corners since those are draw on
// top of the current view
if (accountForRoundedRects) {
RecentsConfiguration config = RecentsConfiguration.getInstance();
float radius = config.pxFromDp(Constants.Values.TaskView.RoundedCornerRadiusDps);
outRect.inset((int) radius, (int) radius);
}
return outRect;
}