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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user