Updating the task swipe-down UI
Bug: 77281732 Change-Id: I1bbc1bbeee74983b3fde5e8218e08689f32ac717
This commit is contained in:
@@ -18,7 +18,6 @@ package com.android.quickstep.fallback;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.support.annotation.AnyThread;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
|||||||
@@ -15,15 +15,23 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.quickstep.util;
|
package com.android.quickstep.util;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Matrix.ScaleToFit;
|
import android.graphics.Matrix.ScaleToFit;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
|
|
||||||
|
import com.android.launcher3.BaseDraggingActivity;
|
||||||
|
import com.android.launcher3.DeviceProfile;
|
||||||
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.Utilities;
|
import com.android.launcher3.Utilities;
|
||||||
|
import com.android.launcher3.dragndrop.DragLayer;
|
||||||
|
import com.android.launcher3.views.BaseDragLayer;
|
||||||
|
import com.android.quickstep.views.TaskThumbnailView;
|
||||||
import com.android.systemui.shared.recents.utilities.RectFEvaluator;
|
import com.android.systemui.shared.recents.utilities.RectFEvaluator;
|
||||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||||
import com.android.systemui.shared.system.TransactionCompat;
|
import com.android.systemui.shared.system.TransactionCompat;
|
||||||
|
import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class to handle window clip animation
|
* Utility class to handle window clip animation
|
||||||
@@ -126,4 +134,73 @@ public class ClipAnimationHelper {
|
|||||||
mTargetRect.offset(offsetX, offsetY);
|
mTargetRect.offset(offsetX, offsetY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void fromTaskThumbnailView(TaskThumbnailView ttv) {
|
||||||
|
BaseDraggingActivity activity = BaseDraggingActivity.fromContext(ttv.getContext());
|
||||||
|
BaseDragLayer dl = activity.getDragLayer();
|
||||||
|
|
||||||
|
int[] pos = new int[2];
|
||||||
|
dl.getLocationOnScreen(pos);
|
||||||
|
mHomeStackBounds.set(0, 0, dl.getWidth(), dl.getHeight());
|
||||||
|
mHomeStackBounds.offset(pos[0], pos[1]);
|
||||||
|
|
||||||
|
if (activity.isInMultiWindowModeCompat()) {
|
||||||
|
// TODO: Fetch multi-window target bounds from system-ui
|
||||||
|
DeviceProfile fullDp = activity.getDeviceProfile().getFullScreenProfile();
|
||||||
|
// Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
|
||||||
|
// account for system insets
|
||||||
|
int taskWidth = fullDp.availableWidthPx;
|
||||||
|
int taskHeight = fullDp.availableHeightPx;
|
||||||
|
int halfDividerSize = activity.getResources()
|
||||||
|
.getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
|
||||||
|
|
||||||
|
Rect insets = new Rect();
|
||||||
|
WindowManagerWrapper.getInstance().getStableInsets(insets);
|
||||||
|
if (fullDp.isLandscape) {
|
||||||
|
taskWidth = taskWidth / 2 - halfDividerSize;
|
||||||
|
} else {
|
||||||
|
taskHeight = taskHeight / 2 - halfDividerSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
mSourceStackBounds.set(0, 0, taskWidth, taskHeight);
|
||||||
|
// Align the task to bottom right (probably not true for seascape).
|
||||||
|
mSourceStackBounds.offset(insets.left + fullDp.availableWidthPx - taskWidth,
|
||||||
|
insets.top + fullDp.availableHeightPx - taskHeight);
|
||||||
|
} else {
|
||||||
|
mSourceStackBounds.set(mHomeStackBounds);
|
||||||
|
mSourceInsets.set(activity.getDeviceProfile().getInsets());
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect targetRect = new Rect();
|
||||||
|
dl.getDescendantRectRelativeToSelf(ttv, targetRect);
|
||||||
|
updateTargetRect(targetRect);
|
||||||
|
|
||||||
|
// Transform the clip relative to the target rect.
|
||||||
|
float scale = mTargetRect.width() / mSourceRect.width();
|
||||||
|
mSourceWindowClipInsets.left = mSourceWindowClipInsets.left * scale;
|
||||||
|
mSourceWindowClipInsets.top = mSourceWindowClipInsets.top * scale;
|
||||||
|
mSourceWindowClipInsets.right = mSourceWindowClipInsets.right * scale;
|
||||||
|
mSourceWindowClipInsets.bottom = mSourceWindowClipInsets.bottom * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawForProgress(TaskThumbnailView ttv, Canvas canvas, float progress) {
|
||||||
|
RectF currentRect;
|
||||||
|
synchronized (mTargetRect) {
|
||||||
|
currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTargetRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.translate(mSourceStackBounds.left - mHomeStackBounds.left,
|
||||||
|
mSourceStackBounds.top - mHomeStackBounds.top);
|
||||||
|
mTmpMatrix.setRectToRect(mTargetRect, currentRect, ScaleToFit.FILL);
|
||||||
|
canvas.concat(mTmpMatrix);
|
||||||
|
canvas.translate(mTargetRect.left, mTargetRect.top);
|
||||||
|
|
||||||
|
float insetProgress = (1 - progress);
|
||||||
|
ttv.drawOnCanvas(canvas,
|
||||||
|
-mSourceWindowClipInsets.left * insetProgress,
|
||||||
|
-mSourceWindowClipInsets.top * insetProgress,
|
||||||
|
ttv.getMeasuredWidth() + mSourceWindowClipInsets.right * insetProgress,
|
||||||
|
ttv.getMeasuredHeight() + mSourceWindowClipInsets.bottom * insetProgress,
|
||||||
|
ttv.getCornerRadius() * progress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.android.quickstep.util;
|
||||||
|
|
||||||
|
import android.animation.TimeInterpolator;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.ColorFilter;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.util.FloatProperty;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import com.android.launcher3.Utilities;
|
||||||
|
import com.android.quickstep.views.RecentsView;
|
||||||
|
import com.android.quickstep.views.TaskThumbnailView;
|
||||||
|
import com.android.quickstep.views.TaskView;
|
||||||
|
|
||||||
|
public class TaskViewDrawable extends Drawable {
|
||||||
|
|
||||||
|
public static FloatProperty<TaskViewDrawable> PROGRESS =
|
||||||
|
new FloatProperty<TaskViewDrawable>("progress") {
|
||||||
|
@Override
|
||||||
|
public void setValue(TaskViewDrawable taskViewDrawable, float v) {
|
||||||
|
taskViewDrawable.setProgress(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Float get(TaskViewDrawable taskViewDrawable) {
|
||||||
|
return taskViewDrawable.mProgress;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final TimeInterpolator ICON_SIZE_INTERPOLATOR =
|
||||||
|
(t) -> (Math.max(t, 0.3f) - 0.3f) / 0.7f;
|
||||||
|
|
||||||
|
private final RecentsView mParent;
|
||||||
|
private final ImageView mIconView;
|
||||||
|
private final int[] mIconPos;
|
||||||
|
|
||||||
|
private final TaskThumbnailView mThumbnailView;
|
||||||
|
|
||||||
|
private final ClipAnimationHelper mClipAnimationHelper;
|
||||||
|
|
||||||
|
private float mProgress = 1;
|
||||||
|
|
||||||
|
public TaskViewDrawable(TaskView tv, RecentsView parent) {
|
||||||
|
mParent = parent;
|
||||||
|
mIconView = tv.getIconView();
|
||||||
|
mIconPos = new int[2];
|
||||||
|
Utilities.getDescendantCoordRelativeToAncestor(mIconView, parent, mIconPos, true);
|
||||||
|
|
||||||
|
mThumbnailView = tv.getThumbnail();
|
||||||
|
mClipAnimationHelper = new ClipAnimationHelper();
|
||||||
|
mClipAnimationHelper.fromTaskThumbnailView(mThumbnailView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgress(float progress) {
|
||||||
|
mProgress = progress;
|
||||||
|
mParent.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(Canvas canvas) {
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(mParent.getScrollX(), mParent.getScrollY());
|
||||||
|
mClipAnimationHelper.drawForProgress(mThumbnailView, canvas, mProgress);
|
||||||
|
canvas.restore();
|
||||||
|
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(mIconPos[0], mIconPos[1]);
|
||||||
|
float scale = ICON_SIZE_INTERPOLATOR.getInterpolation(mProgress);
|
||||||
|
canvas.scale(scale, scale, mIconView.getWidth() / 2, mIconView.getHeight() / 2);
|
||||||
|
mIconView.draw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAlpha(int i) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColorFilter(ColorFilter colorFilter) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOpacity() {
|
||||||
|
return PixelFormat.TRANSLUCENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -130,4 +130,12 @@ public class LauncherRecentsView extends RecentsView<Launcher> {
|
|||||||
protected void getTaskSize(DeviceProfile dp, Rect outRect) {
|
protected void getTaskSize(DeviceProfile dp, Rect outRect) {
|
||||||
LayoutUtils.calculateLauncherTaskSize(getContext(), dp, outRect);
|
LayoutUtils.calculateLauncherTaskSize(getContext(), dp, outRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onTaskLaunched(boolean success) {
|
||||||
|
if (success) {
|
||||||
|
mActivity.getStateManager().goToState(NORMAL, false /* animate */);
|
||||||
|
}
|
||||||
|
super.onTaskLaunched(success);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,10 +66,10 @@ import com.android.launcher3.util.PendingAnimation;
|
|||||||
import com.android.launcher3.util.Themes;
|
import com.android.launcher3.util.Themes;
|
||||||
import com.android.quickstep.OverviewCallbacks;
|
import com.android.quickstep.OverviewCallbacks;
|
||||||
import com.android.quickstep.QuickScrubController;
|
import com.android.quickstep.QuickScrubController;
|
||||||
import com.android.quickstep.RecentsAnimationInterpolator;
|
|
||||||
import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
|
import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
|
||||||
import com.android.quickstep.RecentsModel;
|
import com.android.quickstep.RecentsModel;
|
||||||
import com.android.quickstep.TaskUtils;
|
import com.android.quickstep.TaskUtils;
|
||||||
|
import com.android.quickstep.util.TaskViewDrawable;
|
||||||
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
|
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
|
||||||
import com.android.systemui.shared.recents.model.RecentsTaskLoader;
|
import com.android.systemui.shared.recents.model.RecentsTaskLoader;
|
||||||
import com.android.systemui.shared.recents.model.Task;
|
import com.android.systemui.shared.recents.model.Task;
|
||||||
@@ -79,6 +79,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
|
|||||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of recent tasks.
|
* A list of recent tasks.
|
||||||
@@ -522,7 +523,6 @@ public abstract class RecentsView<T extends BaseActivity>
|
|||||||
mHasVisibleTaskData.clear();
|
mHasVisibleTaskData.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected abstract void onAllTasksRemoved();
|
protected abstract void onAllTasksRemoved();
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
@@ -1053,23 +1053,27 @@ public abstract class RecentsView<T extends BaseActivity>
|
|||||||
return new PendingAnimation(anim);
|
return new PendingAnimation(anim);
|
||||||
}
|
}
|
||||||
|
|
||||||
final RecentsAnimationInterpolator recentsInterpolator = tv.getRecentsInterpolator();
|
tv.setVisibility(INVISIBLE);
|
||||||
ValueAnimator targetViewAnim = ValueAnimator.ofFloat(0, 1);
|
TaskViewDrawable drawable = new TaskViewDrawable(tv, this);
|
||||||
targetViewAnim.addUpdateListener((animation) -> {
|
getOverlay().add(drawable);
|
||||||
float percent = animation.getAnimatedFraction();
|
|
||||||
TaskWindowBounds tw = recentsInterpolator.interpolate(percent);
|
ObjectAnimator drawableAnim =
|
||||||
tv.setScaleX(tw.taskScale);
|
ObjectAnimator.ofFloat(drawable, TaskViewDrawable.PROGRESS, 1, 0);
|
||||||
tv.setScaleY(tw.taskScale);
|
drawableAnim.setInterpolator(LINEAR);
|
||||||
tv.setTranslationX(tw.taskX);
|
|
||||||
tv.setTranslationY(tw.taskY);
|
anim.play(drawableAnim);
|
||||||
});
|
|
||||||
anim.play(targetViewAnim);
|
|
||||||
anim.setDuration(duration);
|
anim.setDuration(duration);
|
||||||
|
|
||||||
|
Consumer<Boolean> onTaskLaunchFinish = (r) -> {
|
||||||
|
onTaskLaunched(r);
|
||||||
|
tv.setVisibility(VISIBLE);
|
||||||
|
getOverlay().remove(drawable);
|
||||||
|
};
|
||||||
|
|
||||||
mPendingAnimation = new PendingAnimation(anim);
|
mPendingAnimation = new PendingAnimation(anim);
|
||||||
mPendingAnimation.addEndListener((onEndListener) -> {
|
mPendingAnimation.addEndListener((onEndListener) -> {
|
||||||
if (onEndListener.isSuccess) {
|
if (onEndListener.isSuccess) {
|
||||||
tv.launchTask(false);
|
tv.launchTask(false, onTaskLaunchFinish, getHandler());
|
||||||
Task task = tv.getTask();
|
Task task = tv.getTask();
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
|
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
|
||||||
@@ -1077,13 +1081,17 @@ public abstract class RecentsView<T extends BaseActivity>
|
|||||||
TaskUtils.getComponentKeyForTask(task.key));
|
TaskUtils.getComponentKeyForTask(task.key));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resetTaskVisuals();
|
onTaskLaunchFinish.accept(false);
|
||||||
}
|
}
|
||||||
mPendingAnimation = null;
|
mPendingAnimation = null;
|
||||||
});
|
});
|
||||||
return mPendingAnimation;
|
return mPendingAnimation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void onTaskLaunched(boolean success) {
|
||||||
|
resetTaskVisuals();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void notifyPageSwitchListener(int prevPage) {
|
protected void notifyPageSwitchListener(int prevPage) {
|
||||||
super.notifyPageSwitchListener(prevPage);
|
super.notifyPageSwitchListener(prevPage);
|
||||||
|
|||||||
@@ -143,22 +143,28 @@ public class TaskThumbnailView extends View {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDraw(Canvas canvas) {
|
protected void onDraw(Canvas canvas) {
|
||||||
|
drawOnCanvas(canvas, 0, 0, getMeasuredWidth(), getMeasuredHeight(), mCornerRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getCornerRadius() {
|
||||||
|
return mCornerRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawOnCanvas(Canvas canvas, float x, float y, float width, float height,
|
||||||
|
float cornerRadius) {
|
||||||
|
// Always draw the background since the snapshots may be translucent
|
||||||
|
canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mBackgroundPaint);
|
||||||
if (mTask == null) {
|
if (mTask == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int width = getMeasuredWidth();
|
|
||||||
int height = getMeasuredHeight();
|
|
||||||
|
|
||||||
// Always draw the background since the snapshots may be translucent
|
|
||||||
canvas.drawRoundRect(0, 0, width, height, mCornerRadius, mCornerRadius, mBackgroundPaint);
|
|
||||||
if (!mTask.isLocked) {
|
if (!mTask.isLocked) {
|
||||||
if (mClipBottom > 0) {
|
if (mClipBottom > 0) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.clipRect(0, 0, width, mClipBottom);
|
canvas.clipRect(x, y, width, mClipBottom);
|
||||||
canvas.drawRoundRect(0, 0, width, height, mCornerRadius, mCornerRadius, mPaint);
|
canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
} else {
|
} else {
|
||||||
canvas.drawRoundRect(0, 0, width, height, mCornerRadius, mCornerRadius, mPaint);
|
canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,10 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback
|
|||||||
return mSnapshotView;
|
return mSnapshotView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ImageView getIconView() {
|
||||||
|
return mIconView;
|
||||||
|
}
|
||||||
|
|
||||||
public void launchTask(boolean animate) {
|
public void launchTask(boolean animate) {
|
||||||
launchTask(animate, null, null);
|
launchTask(animate, null, null);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user