Updating the freeform workspace layout

- Updating layout for freeform workspaces
- Consolidates the clipping and bitmap scale with the transform to make
  the transitions more unified
- Updating clipping as we synchronize scroll instead of afterwards

Change-Id: I33de4e2646c75f0bc0eb75bd6909fe94557196b5
This commit is contained in:
Winson
2015-12-01 16:02:12 -08:00
parent f0d1c44a59
commit 5b7dd536aa
12 changed files with 199 additions and 209 deletions

View File

@@ -26,7 +26,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
@@ -62,8 +61,6 @@ import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;

View File

@@ -35,7 +35,6 @@ import android.util.MutableBoolean;
import android.view.AppTransitionAnimationSpec;
import android.view.LayoutInflater;
import android.view.View;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;

View File

@@ -761,7 +761,7 @@ public class EventBus extends BroadcastReceiver {
} catch (IllegalAccessException e) {
Log.e(TAG, "Failed to invoke method", e.getCause());
} catch (InvocationTargetException e) {
throw new RuntimeException("Failed to invoke method", e);
throw new RuntimeException(e.getCause());
}
}

View File

@@ -18,7 +18,6 @@ package com.android.systemui.recents.history;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.res.Resources;
import android.support.v7.widget.RecyclerView;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
@@ -29,7 +28,6 @@ import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
import java.util.Calendar;

View File

@@ -27,7 +27,6 @@ import android.view.WindowInsets;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;

View File

@@ -69,7 +69,6 @@ public class AnimateableViewBounds extends ViewOutlineProvider {
*/
public void reset() {
mClipRect.setEmpty();
mSourceView.invalidateOutline();
updateClipBounds();
}
@@ -94,7 +93,6 @@ public class AnimateableViewBounds extends ViewOutlineProvider {
public void setClipBottom(int bottom, boolean force) {
if (bottom != mClipRect.bottom || force) {
mClipRect.bottom = bottom;
mSourceView.invalidateOutline();
updateClipBounds();
}
}
@@ -108,7 +106,6 @@ public class AnimateableViewBounds extends ViewOutlineProvider {
public void setClipRight(int right, boolean force) {
if (right != mClipRect.right || force) {
mClipRect.right = right;
mSourceView.invalidateOutline();
updateClipBounds();
}
}
@@ -123,5 +120,6 @@ public class AnimateableViewBounds extends ViewOutlineProvider {
mSourceView.getWidth() - mClipRect.right,
mSourceView.getHeight() - mClipRect.bottom);
mSourceView.setClipBounds(mClipBounds);
mSourceView.invalidateOutline();
}
}

View File

@@ -16,12 +16,13 @@
package com.android.systemui.recents.views;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -33,48 +34,91 @@ public class FreeformWorkspaceLayoutAlgorithm {
private static final String TAG = "FreeformWorkspaceLayoutAlgorithm";
private static final boolean DEBUG = false;
// The number of cells in the freeform workspace
private int mFreeformCellXCount;
private int mFreeformCellYCount;
// The width and height of the cells in the freeform workspace
private int mFreeformCellWidth;
private int mFreeformCellHeight;
// Optimization, allows for quick lookup of task -> index
private HashMap<Task.TaskKey, Integer> mTaskIndexMap = new HashMap<>();
// Optimization, allows for quick lookup of task -> rect
private HashMap<Task.TaskKey, RectF> mTaskRectMap = new HashMap<>();
/**
* Updates the layout for each of the freeform workspace tasks. This is called after the stack
* layout is updated.
*/
public void update(List<Task> freeformTasks, TaskStackLayoutAlgorithm stackLayout) {
mTaskIndexMap.clear();
Collections.reverse(freeformTasks);
mTaskRectMap.clear();
int numFreeformTasks = stackLayout.mNumFreeformTasks;
if (!freeformTasks.isEmpty()) {
// Calculate the cell width/height depending on the number of freeform tasks
mFreeformCellXCount = Math.max(1, (int) Math.ceil(Math.sqrt(numFreeformTasks)));
mFreeformCellYCount = Math.max(1, (int) Math.ceil((float) numFreeformTasks /
mFreeformCellXCount));
// For now, make the cells square
mFreeformCellWidth = Math.min(stackLayout.mFreeformRect.width() / mFreeformCellXCount,
stackLayout.mFreeformRect.height() / mFreeformCellYCount);
mFreeformCellHeight = mFreeformCellWidth;
// Put each of the tasks in the progress map at a fixed index (does not need to actually
// map to a scroll position, just by index)
int taskCount = freeformTasks.size();
for (int i = taskCount - 1; i >= 0; i--) {
// Normalize the widths so that we can calculate the best layout below
int workspaceWidth = stackLayout.mFreeformRect.width();
int workspaceHeight = stackLayout.mFreeformRect.height();
float normalizedWorkspaceWidth = (float) workspaceWidth / workspaceHeight;
float normalizedWorkspaceHeight = 1f;
float[] normalizedTaskWidths = new float[numFreeformTasks];
for (int i = 0; i < numFreeformTasks; i++) {
Task task = freeformTasks.get(i);
mTaskIndexMap.put(task.key, taskCount - i - 1);
float rowTaskWidth;
if (task.bounds != null) {
rowTaskWidth = (float) task.bounds.width() / task.bounds.height();
} else {
// If this is a stack task that was dragged into the freeform workspace, then
// the task will not yet have an associated bounds, so assume the full workspace
// width for the time being
rowTaskWidth = normalizedWorkspaceWidth;
}
// Bound the task width to the workspace width so that at the worst case, it will
// fit its own row
normalizedTaskWidths[i] = Math.min(rowTaskWidth,
normalizedWorkspaceWidth);
}
if (DEBUG) {
Log.d(TAG, "mFreeformCellXCount: " + mFreeformCellXCount);
Log.d(TAG, "mFreeformCellYCount: " + mFreeformCellYCount);
Log.d(TAG, "mFreeformCellWidth: " + mFreeformCellWidth);
Log.d(TAG, "mFreeformCellHeight: " + mFreeformCellHeight);
// Determine the scale to best fit each of the tasks in the workspace
float rowScale = 0.85f;
float rowWidth = 0f;
float maxRowWidth = 0f;
int rowCount = 1;
for (int i = 0; i < numFreeformTasks;) {
float width = normalizedTaskWidths[i] * rowScale;
if (rowWidth + width > normalizedWorkspaceWidth) {
// That is too long for this row, create new row
rowWidth = 0f;
if ((rowCount + 1) * rowScale > normalizedWorkspaceHeight) {
// The new row is too high, so we need to try fitting again. Update the
// scale to be the smaller of the scale needed to fit the task in the
// previous row, or the scale needed to fit the new row
rowScale = Math.min(normalizedWorkspaceWidth / (rowWidth + width),
normalizedWorkspaceHeight / (rowCount + 1));
rowCount = 1;
i = 0;
} else {
// The new row fits, so continue
rowCount++;
i++;
}
} else {
// Task is OK in this row
rowWidth += width;
i++;
}
maxRowWidth = Math.max(rowWidth, maxRowWidth);
}
// Normalize each of the actual rects to that scale
int height = (int) (rowScale * workspaceHeight);
float rowTop = ((1f - (rowScale * rowCount)) * workspaceHeight) / 2f;
float defaultRowLeft = ((1f - (maxRowWidth / normalizedWorkspaceWidth)) *
workspaceWidth) / 2f;
float rowLeft = defaultRowLeft;
for (int i = 0; i < numFreeformTasks; i++) {
Task task = freeformTasks.get(i);
int width = (int) (height * normalizedTaskWidths[i]);
if (rowLeft + width > workspaceWidth) {
// This goes on the next line
rowTop += height;
rowLeft = defaultRowLeft;
}
RectF rect = new RectF(rowLeft, rowTop, rowLeft + width, rowTop + height);
rowLeft += width;
mTaskRectMap.put(task.key, rect);
}
}
}
@@ -86,7 +130,7 @@ public class FreeformWorkspaceLayoutAlgorithm {
if (stackLayout.mNumFreeformTasks == 0 || task == null) {
return false;
}
return mTaskIndexMap.containsKey(task.key);
return mTaskRectMap.containsKey(task.key);
}
/**
@@ -95,34 +139,35 @@ public class FreeformWorkspaceLayoutAlgorithm {
*/
public TaskViewTransform getTransform(Task task, TaskViewTransform transformOut,
TaskStackLayoutAlgorithm stackLayout) {
if (mTaskIndexMap.containsKey(task.key)) {
if (mTaskRectMap.containsKey(task.key)) {
Rect taskRect = stackLayout.mTaskRect;
int taskIndex = mTaskIndexMap.get(task.key);
RectF ffRect = mTaskRectMap.get(task.key);
float scale = Math.max(ffRect.width() / taskRect.width(),
ffRect.height() / taskRect.height());
int topOffset = (stackLayout.mFreeformRect.top - taskRect.top);
int x = taskIndex % mFreeformCellXCount;
int y = taskIndex / mFreeformCellXCount;
int scaleXOffset = (int) (((1f - scale) * taskRect.width()) / 2);
int scaleYOffset = (int) (((1f - scale) * taskRect.height()) / 2);
Bitmap thumbnail = task.thumbnail;
float thumbnailScale = 1f;
float thumbnailWidth = mFreeformCellWidth;
float thumbnailHeight = mFreeformCellHeight;
if (thumbnail != null) {
int bitmapWidth = task.thumbnail.getWidth();
int bitmapHeight = task.thumbnail.getHeight();
thumbnailScale = Math.min((float) mFreeformCellWidth / bitmapWidth,
(float) mFreeformCellHeight / bitmapHeight);
thumbnailWidth = bitmapWidth * thumbnailScale;
thumbnailHeight = bitmapHeight * thumbnailScale;
}
int scaleXOffset = (int) (((1f - thumbnailScale) * thumbnailWidth) / 2);
int scaleYOffset = (int) (((1f - thumbnailScale) * thumbnailHeight) / 2);
transformOut.scale = thumbnailScale * 0.9f;
transformOut.translationX = x * mFreeformCellWidth - scaleXOffset;
transformOut.translationY = topOffset + y * mFreeformCellHeight - scaleYOffset;
transformOut.scale = scale * 0.95f;
transformOut.translationX = (int) (ffRect.left - scaleXOffset);
transformOut.translationY = (int) (topOffset + ffRect.top - scaleYOffset);
transformOut.translationZ = stackLayout.mMaxTranslationZ;
transformOut.rect.set(stackLayout.mTaskRect);
transformOut.clipBottom = (int) (taskRect.height() - (ffRect.height() / scale));
transformOut.clipRight = (int) (taskRect.width() - (ffRect.width() / scale));
if (task.thumbnail != null) {
transformOut.thumbnailScale = Math.min(
((float) taskRect.width() - transformOut.clipRight) /
task.thumbnail.getWidth(),
((float) taskRect.height() - transformOut.clipBottom) /
task.thumbnail.getHeight());
} else {
transformOut.thumbnailScale = 1f;
}
transformOut.rect.set(taskRect);
transformOut.rect.offset(transformOut.translationX, transformOut.translationY);
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
transformOut.rect.right -= transformOut.clipRight * scale;
transformOut.rect.bottom -= transformOut.clipBottom * scale;
transformOut.visible = true;
transformOut.p = 1f;

View File

@@ -334,7 +334,6 @@ public class TaskStackLayoutAlgorithm {
* including the search bar.
*/
public void initialize(Rect taskStackBounds, StackState state) {
SystemServicesProxy ssp = Recents.getSystemServices();
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
RecentsConfiguration config = Recents.getConfiguration();
int widthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width());
@@ -589,8 +588,15 @@ public class TaskStackLayoutAlgorithm {
transformOut.reset();
return transformOut;
}
return getStackTransform(mTaskIndexMap.get(task.key), stackScroll, transformOut,
getStackTransform(mTaskIndexMap.get(task.key), stackScroll, transformOut,
frontTransform);
if (task.thumbnail != null) {
transformOut.thumbnailScale = (float) mTaskRect.width() / task.thumbnail.getWidth();
}
if (DEBUG) {
Log.d(TAG, "getTransform: " + task.key + ", " + transformOut);
}
return transformOut;
}
}
@@ -653,6 +659,9 @@ public class TaskStackLayoutAlgorithm {
transformOut.rect.offset(transformOut.translationX, transformOut.translationY);
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
transformOut.visible = true;
transformOut.clipBottom = 0;
transformOut.clipRight = 0;
transformOut.thumbnailScale = 1f;
transformOut.p = relP;
return transformOut;
}

View File

@@ -16,10 +16,6 @@
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.content.ComponentName;
import android.content.Context;
@@ -139,11 +135,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// A convenience update listener to request updating clipping of tasks
private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
requestUpdateStackViewsClip();
}
};
@Override
public void onAnimationUpdate(ValueAnimator animation) {
requestUpdateStackViewsClip();
}
};
// The drop targets for a task drag
private DropTarget mFreeformWorkspaceDropTarget = new DropTarget() {
@@ -474,8 +470,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Animate the task into place
tv.updateViewPropertiesToTaskTransform(transform,
mStackViewsAnimationDuration, mRequestUpdateClippingListener);
tv.updateViewPropertiesToTaskTransform(transform, transform.clipBottom,
mStackViewsAnimationDuration, mFastOutSlowInInterpolator,
mRequestUpdateClippingListener);
// Reattach it in the right z order
detachViewFromParent(tv);
@@ -497,7 +494,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Pick up all the newly visible children and update all the existing children
for (int i = visibleStackRange[0]; isValidVisibleStackRange && i >= visibleStackRange[1]; i--) {
for (int i = visibleStackRange[0];
isValidVisibleStackRange && i >= visibleStackRange[1]; i--) {
Task task = tasks.get(i);
TaskViewTransform transform = mCurrentTaskTransforms.get(i);
TaskView tv = mTmpTaskViewMap.get(task);
@@ -516,21 +514,25 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mLayoutAlgorithm.getStackTransform(0f, 0f, mTmpStackBackTransform,
null);
}
tv.updateViewPropertiesToTaskTransform(mTmpStackBackTransform, 0);
tv.updateViewPropertiesToTaskTransform(mTmpStackBackTransform, 0, 0,
mFastOutSlowInInterpolator, mRequestUpdateClippingListener);
} else {
if (!hasStackFrontTransform) {
hasStackFrontTransform = true;
mLayoutAlgorithm.getStackTransform(1f, 0f, mTmpStackFrontTransform,
null);
}
tv.updateViewPropertiesToTaskTransform(mTmpStackFrontTransform, 0);
tv.updateViewPropertiesToTaskTransform(mTmpStackFrontTransform, 0, 0,
mFastOutSlowInInterpolator, mRequestUpdateClippingListener);
}
}
}
// Animate the task into place
// Animate the task into place, the clip for stack tasks will be calculated in
// clipTaskViews()
tv.updateViewPropertiesToTaskTransform(transform,
mStackViewsAnimationDuration, mRequestUpdateClippingListener);
tv.getViewBounds().getClipBottom(), mStackViewsAnimationDuration,
mFastOutSlowInInterpolator, mRequestUpdateClippingListener);
}
// Update the focus if the previous focused task was returned to the view pool
@@ -988,6 +990,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
requestSynchronizeStackViewsWithModel();
synchronizeStackViewsWithModel();
requestUpdateStackViewsClip();
clipTaskViews(true /* forceUpdate */);
}
}
@@ -1131,8 +1134,15 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
public boolean isTransformedTouchPointInView(float x, float y, View child) {
return isTransformedTouchPointInView(x, y, child, null);
public boolean isTransformedTouchPointInView(float x, float y, TaskView tv) {
final float[] point = new float[2];
point[0] = x;
point[1] = y;
transformPointToViewLocal(point, tv);
x = point[0];
y = point[1];
return (0 <= x) && (x < (tv.getMeasuredWidth() - tv.getViewBounds().getClipRight())) &&
(0 <= y) && (y < (tv.getMeasuredHeight() - tv.getViewBounds().getClipBottom()));
}
@Override
@@ -1343,9 +1353,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void onTaskViewClipStateChanged(TaskView tv) {
if (!mStackViewsDirty) {
invalidate();
}
requestUpdateStackViewsClip();
}
/**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
@@ -1425,12 +1433,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
private AnimatorSet mDropAnimation;
public final void onBusEvent(DragStartEvent event) {
// Cancel the existing drop animation
Utilities.cancelAnimationWithoutCallbacks(mDropAnimation);
if (event.task.isFreeformTask()) {
// Animate to the front of the stack
mStackScroller.animateScroll(mStackScroller.getStackScroll(),
@@ -1460,47 +1463,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
boolean hasChangedStacks =
(!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
(isFreeformTask && event.dropTarget == mStackDropTarget);
if (hasChangedStacks) {
ArrayList<Animator> animations = new ArrayList<>();
event.postAnimationTrigger.increment();
if (hasChangedStacks) {
// Move the task to the right position in the stack (ie. the front of the stack if
// freeform or the front of the stack if fullscreen). Note, we MUST move the tasks
// before we update their stack ids, otherwise, the keys will have changed.
if (event.dropTarget == mFreeformWorkspaceDropTarget) {
mStack.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID);
updateLayout(true);
// Update the clipping to match the scaled bitmap rect
TaskViewThumbnail thumbnailView = event.taskView.mThumbnailView;
float thumbnailScale = thumbnailView.computeThumbnailScale(true);
RectF bitmapRect = thumbnailView.getScaledBitmapRect(thumbnailScale);
AnimateableViewBounds viewBounds = event.taskView.getViewBounds();
int clipRight = (int) (thumbnailView.getMeasuredWidth() - bitmapRect.width());
int clipBottom = (int) (thumbnailView.getMeasuredHeight() - bitmapRect.height());
animations.add(ObjectAnimator.ofFloat(thumbnailView, TaskViewThumbnail.BITMAP_SCALE,
thumbnailView.getBitmapScale(), thumbnailScale));
animations.add(ObjectAnimator.ofInt(viewBounds, AnimateableViewBounds.CLIP_BOTTOM,
viewBounds.getClipBottom(), clipBottom));
animations.add(ObjectAnimator.ofInt(viewBounds, AnimateableViewBounds.CLIP_RIGHT,
viewBounds.getClipRight(), clipRight));
} else if (event.dropTarget == mStackDropTarget) {
mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID);
updateLayout(true);
// Reset the clipping when animating to the stack
TaskViewThumbnail thumbnailView = event.taskView.mThumbnailView;
float thumbnailScale = thumbnailView.computeThumbnailScale(false);
AnimateableViewBounds viewBounds = event.taskView.getViewBounds();
animations.add(ObjectAnimator.ofFloat(thumbnailView, TaskViewThumbnail.BITMAP_SCALE,
thumbnailView.getBitmapScale(), thumbnailScale));
animations.add(ObjectAnimator.ofInt(viewBounds, AnimateableViewBounds.CLIP_BOTTOM,
viewBounds.getClipBottom(), 0));
animations.add(ObjectAnimator.ofInt(viewBounds, AnimateableViewBounds.CLIP_RIGHT,
viewBounds.getClipRight(), 0));
}
updateLayout(true);
// Move the task to the new stack in the system after the animation completes
event.postAnimationTrigger.increment();
event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
@@ -1508,22 +1484,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId);
}
});
// Animate the normal properties of the view
mDropAnimation = new AnimatorSet();
mDropAnimation.playTogether(animations);
mDropAnimation.setDuration(250);
mDropAnimation.setInterpolator(mFastOutSlowInInterpolator);
mDropAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
event.postAnimationTrigger.decrement();
}
});
mDropAnimation.start();
}
event.postAnimationTrigger.increment();
event.taskView.animate()
.withEndAction(event.postAnimationTrigger.decrementAsRunnable());

View File

@@ -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.content.Context;
@@ -28,6 +29,7 @@ import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
@@ -80,6 +82,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
boolean mIsFocusAnimated;
boolean mClipViewInStack;
AnimateableViewBounds mViewBounds;
private AnimatorSet mClipAnimation;
View mContent;
TaskViewThumbnail mThumbnailView;
@@ -209,23 +212,45 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY));
mThumbnailView.updateClipToTaskBar(mHeaderView);
setMeasuredDimension(width, height);
invalidateOutline();
}
/** Synchronizes this view's properties with the task's transform */
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
updateViewPropertiesToTaskTransform(toTransform, duration, null);
}
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration,
ValueAnimator.AnimatorUpdateListener updateCallback) {
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int clipBottom,
int duration, Interpolator interpolator,
ValueAnimator.AnimatorUpdateListener updateCallback) {
RecentsConfiguration config = Recents.getConfiguration();
Utilities.cancelAnimationWithoutCallbacks(mClipAnimation);
// Apply the transform
toTransform.applyToTaskView(this, duration, mFastOutSlowInInterpolator, false,
toTransform.applyToTaskView(this, duration, interpolator, false,
!config.fakeShadows, updateCallback);
// Update the clipping
if (duration > 0) {
mClipAnimation = new AnimatorSet();
mClipAnimation.playTogether(
ObjectAnimator.ofInt(mViewBounds, AnimateableViewBounds.CLIP_BOTTOM,
mViewBounds.getClipBottom(), clipBottom),
ObjectAnimator.ofInt(mViewBounds, AnimateableViewBounds.CLIP_RIGHT,
mViewBounds.getClipRight(), toTransform.clipRight),
ObjectAnimator.ofFloat(mThumbnailView, TaskViewThumbnail.BITMAP_SCALE,
mThumbnailView.getBitmapScale(), toTransform.thumbnailScale));
mClipAnimation.setStartDelay(toTransform.startDelay);
mClipAnimation.setDuration(duration);
mClipAnimation.setInterpolator(interpolator);
mClipAnimation.start();
} else {
mViewBounds.setClipBottom(clipBottom, false /* forceUpdate */);
mViewBounds.setClipRight(toTransform.clipRight, false /* forceUpdate */);
mThumbnailView.setBitmapScale(toTransform.thumbnailScale);
}
if (!config.useHardwareLayers) {
mThumbnailView.updateThumbnailVisibility(clipBottom - getPaddingBottom());
}
// Update the task progress
Utilities.cancelAnimationWithoutCallbacks(mTaskProgressAnimator);
if (duration <= 0) {
@@ -673,11 +698,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
@Override
public void onTaskDataLoaded() {
SystemServicesProxy ssp = Recents.getSystemServices();
if (mThumbnailView != null && mHeaderView != null) {
// Bind each of the views to the new task data
mThumbnailView.rebindToTask(mTask);
mHeaderView.rebindToTask(mTask);
// Rebind any listeners
mActionButtonView.setOnClickListener(this);
setOnLongClickListener(this);
@@ -721,7 +746,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
@Override
public boolean onLongClick(View v) {
SystemServicesProxy ssp = Recents.getSystemServices();
if (v == this && !ssp.hasDockedTask()) {
// Since we are clipping the view to the bounds, manually do the hit test
Rect clipBounds = new Rect(mViewBounds.mClipBounds);
clipBounds.scale(getScaleX());
boolean inBounds = clipBounds.contains(mDownTouchPos.x, mDownTouchPos.y);
if (v == this && inBounds && !ssp.hasDockedTask()) {
// Start listening for drag events
setClipViewInStack(false);

View File

@@ -25,7 +25,6 @@ import android.graphics.LightingColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -56,15 +55,11 @@ public class TaskViewThumbnail extends View {
}
};
private Task mTask;
// Drawing
int mCornerRadius;
float mDimAlpha;
Matrix mScaleMatrix = new Matrix();
Paint mDrawPaint = new Paint();
RectF mBitmapRect = new RectF();
RectF mLayoutRect = new RectF();
float mBitmapScale = 1f;
BitmapShader mBitmapShader;
LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
@@ -103,15 +98,6 @@ public class TaskViewThumbnail extends View {
com.android.internal.R.interpolator.fast_out_slow_in);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
mLayoutRect.set(0, 0, getWidth(), getHeight());
setBitmapScale(computeThumbnailScale(mTask != null ? mTask.isFreeformTask() : false));
}
}
@Override
protected void onDraw(Canvas canvas) {
if (mInvisible) {
@@ -129,16 +115,10 @@ public class TaskViewThumbnail extends View {
mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP);
mDrawPaint.setShader(mBitmapShader);
mBitmapRect.set(0, 0, bm.getWidth(), bm.getHeight());
} else {
mBitmapShader = null;
mDrawPaint.setShader(null);
}
if (mTask != null) {
setBitmapScale(computeThumbnailScale(mTask != null ? mTask.isFreeformTask() : false));
} else {
setBitmapScale(1f);
}
}
/** Updates the paint to draw the thumbnail. */
@@ -159,32 +139,6 @@ public class TaskViewThumbnail extends View {
invalidate();
}
/**
* Returns the scale to apply to a thumbnail bitmap relative to this view rect.
*/
public float computeThumbnailScale(boolean isFreeformTask) {
if (isFreeformTask) {
// For freeform tasks, we scale the bitmap rect to fit in the layout rect
return Math.min(mLayoutRect.width() / mBitmapRect.width(),
mLayoutRect.height() / mBitmapRect.height());
} else {
// For stack tasks, we scale the bitmap to fit the width
return Math.max(1f, mLayoutRect.width() / mBitmapRect.width());
}
}
/**
* Returns the scaled bitmap rect.
*/
public RectF getScaledBitmapRect(float scale) {
RectF scaledBitmapRect = new RectF(mBitmapRect);
scaledBitmapRect.left *= scale;
scaledBitmapRect.top *= scale;
scaledBitmapRect.right *= scale;
scaledBitmapRect.bottom *= scale;
return scaledBitmapRect;
}
/**
* Sets the scale of the bitmap relative to this view.
*/
@@ -235,7 +189,6 @@ public class TaskViewThumbnail extends View {
/** Binds the thumbnail view to the task */
void rebindToTask(Task t) {
mTask = t;
if (t.thumbnail != null) {
setThumbnail(t.thumbnail);
} else {
@@ -245,7 +198,6 @@ public class TaskViewThumbnail extends View {
/** Unbinds the thumbnail view from the task */
void unbindFromTask() {
mTask = null;
setThumbnail(null);
}
}

View File

@@ -18,19 +18,28 @@ package com.android.systemui.recents.views;
import android.animation.ValueAnimator;
import android.graphics.RectF;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.animation.Interpolator;
/* The transform state for a task view */
public class TaskViewTransform {
// TODO: Move this out of the transform
public int startDelay = 0;
public int translationX = 0;
public int translationY = 0;
public float translationZ = 0;
public float scale = 1f;
public float alpha = 1f;
// Clip and thumbnail scale are untransformed layout-space properties
// The bottom clip is only used for freeform workspace tasks
public int clipBottom = 0;
public int clipRight = 0;
public float thumbnailScale = 1f;
public boolean visible = false;
float p = 0f;
@@ -42,18 +51,6 @@ public class TaskViewTransform {
// Do nothing
}
public TaskViewTransform(TaskViewTransform o) {
startDelay = o.startDelay;
translationX = o.translationX;
translationY = o.translationY;
translationZ = o.translationZ;
scale = o.scale;
alpha = o.alpha;
visible = o.visible;
rect.set(o.rect);
p = o.p;
}
/**
* Resets the current transform.
*/
@@ -64,6 +61,9 @@ public class TaskViewTransform {
translationZ = 0;
scale = 1f;
alpha = 1f;
clipBottom = 0;
clipRight = 0;
thumbnailScale = 1f;
visible = false;
rect.setEmpty();
p = 0f;
@@ -87,7 +87,7 @@ public class TaskViewTransform {
}
/** Applies this transform to a view. */
public void applyToTaskView(View v, int duration, Interpolator interp, boolean allowLayers,
public void applyToTaskView(TaskView v, int duration, Interpolator interp, boolean allowLayers,
boolean allowShadows, ValueAnimator.AnimatorUpdateListener updateCallback) {
// Check to see if any properties have changed, and update the task view
if (duration > 0) {
@@ -149,7 +149,7 @@ public class TaskViewTransform {
}
/** Reset the transform on a view. */
public static void reset(View v) {
public static void reset(TaskView v) {
// Cancel any running animations
v.animate().cancel();
v.setTranslationX(0f);
@@ -158,6 +158,9 @@ public class TaskViewTransform {
v.setScaleX(1f);
v.setScaleY(1f);
v.setAlpha(1f);
v.getViewBounds().setClipRight(0, false /* forceUpdate */);
v.getViewBounds().setClipBottom(0, false /* forceUpdate */);
v.mThumbnailView.setBitmapScale(1f);
}
@Override