From 4ff3615b23f68e16e235f281d443e37ed5d599d6 Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Thu, 17 Mar 2022 15:24:49 -0700 Subject: [PATCH] Update initial staged split UX. - Introduce inset to the staged split view - Update width of the staged split view - Introduce rounded corners to staged split view. It's tricky to animate it in with smooth transition considering the rounded corners. The best way to handle it is to draw content as if part of it is off the screen, so that when it's animated in, the rounded corners can slide in onto the screen correctly. Fixes: 219085340 Test: https://recall.googleplex.com/projects/f46cfe9c-8076-4efe-bf8a-b1cc4f1f5e1b/sessions/09a99b75-3614-4d20-b6d4-a78108f769d8 Change-Id: I24d90cc9e2695d822cb2de8b21e2d5519f2e344d --- .../views/FloatingTaskThumbnailView.java | 13 +--- .../quickstep/views/FloatingTaskView.java | 75 +++++++++++-------- .../android/quickstep/views/RecentsView.java | 16 ++-- .../quickstep/views/SplitPlaceholderView.java | 38 +++++----- res/values/dimens.xml | 4 +- .../touch/LandscapePagedViewHandler.java | 13 +++- .../touch/PagedOrientationHandler.java | 6 +- .../touch/PortraitPagedViewHandler.java | 33 ++++++-- 8 files changed, 118 insertions(+), 80 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskThumbnailView.java index cfb00568d0..98e50f6b93 100644 --- a/quickstep/src/com/android/quickstep/views/FloatingTaskThumbnailView.java +++ b/quickstep/src/com/android/quickstep/views/FloatingTaskThumbnailView.java @@ -42,8 +42,6 @@ public class FloatingTaskThumbnailView extends View { private @Nullable BitmapShader mBitmapShader; private @Nullable Bitmap mBitmap; - private FloatingTaskView.FullscreenDrawParams mFullscreenParams; - public FloatingTaskThumbnailView(Context context) { this(context, null); } @@ -58,7 +56,7 @@ public class FloatingTaskThumbnailView extends View { @Override protected void onDraw(Canvas canvas) { - if (mFullscreenParams == null || mBitmap == null) { + if (mBitmap == null) { return; } @@ -67,9 +65,8 @@ public class FloatingTaskThumbnailView extends View { mMatrix.postScale(scale, scale); mBitmapShader.setLocalMatrix(mMatrix); - canvas.drawRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), - mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleX, - mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleY, mPaint); + FloatingTaskView parent = (FloatingTaskView) getParent(); + parent.drawRoundedRect(canvas, mPaint); } public void setThumbnail(Bitmap bitmap) { @@ -79,8 +76,4 @@ public class FloatingTaskThumbnailView extends View { mPaint.setShader(mBitmapShader); } } - - public void setFullscreenParams(FloatingTaskView.FullscreenDrawParams fullscreenParams) { - mFullscreenParams = fullscreenParams; - } } diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java index c59dc1893a..93a3a9f4bb 100644 --- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java +++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java @@ -3,11 +3,12 @@ package com.android.quickstep.views; import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL_3; import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; @@ -31,15 +32,13 @@ import com.android.quickstep.util.MultiValueUpdateListener; import com.android.quickstep.util.TaskCornerRadius; import com.android.systemui.shared.system.QuickStepContract; -import java.util.function.Consumer; - /** * Create an instance via - * {@link #getFloatingTaskView(StatefulActivity, View, Bitmap, Drawable, RectF, Consumer)} to + * {@link #getFloatingTaskView(StatefulActivity, View, Bitmap, Drawable, RectF)} to * which will have the thumbnail from the provided existing TaskView overlaying the taskview itself. * * Can then animate the taskview using - * {@link #addAnimation(PendingAnimation, RectF, Rect, View, boolean)} + * {@link #addAnimation(PendingAnimation, RectF, Rect, boolean, boolean)} * giving a starting and ending bounds. Currently this is set to use the split placeholder view, * but it could be generified. * @@ -47,13 +46,13 @@ import java.util.function.Consumer; */ public class FloatingTaskView extends FrameLayout { + private FloatingTaskThumbnailView mThumbnailView; private SplitPlaceholderView mSplitPlaceholderView; private RectF mStartingPosition; private final StatefulActivity mActivity; private final boolean mIsRtl; - private final FullscreenDrawParams mCurrentFullscreenParams; + private final FullscreenDrawParams mFullscreenParams; private PagedOrientationHandler mOrientationHandler; - private FloatingTaskThumbnailView mThumbnailView; public FloatingTaskView(Context context) { this(context, null); @@ -67,17 +66,15 @@ public class FloatingTaskView extends FrameLayout { super(context, attrs, defStyleAttr); mActivity = BaseActivity.fromContext(context); mIsRtl = Utilities.isRtl(getResources()); - mCurrentFullscreenParams = new FullscreenDrawParams(context); + mFullscreenParams = new FullscreenDrawParams(context); } @Override protected void onFinishInflate() { super.onFinishInflate(); mThumbnailView = findViewById(R.id.thumbnail); - mThumbnailView.setFullscreenParams(mCurrentFullscreenParams); mSplitPlaceholderView = findViewById(R.id.split_placeholder); mSplitPlaceholderView.setAlpha(0); - mSplitPlaceholderView.setFullscreenParams(mCurrentFullscreenParams); } private void init(StatefulActivity launcher, View originalView, @Nullable Bitmap thumbnail, @@ -97,17 +94,13 @@ public class FloatingTaskView extends FrameLayout { RecentsView recentsView = launcher.getOverviewPanel(); mOrientationHandler = recentsView.getPagedOrientationHandler(); mSplitPlaceholderView.setIcon(icon, - launcher.getDeviceProfile().overviewTaskIconDrawableSizePx); + mContext.getResources().getDimensionPixelSize(R.dimen.split_placeholder_icon_size)); mSplitPlaceholderView.getIconView().setRotation(mOrientationHandler.getDegreesRotated()); } /** * Configures and returns a an instance of {@link FloatingTaskView} initially matching the * appearance of {@code originalView}. - * - * @param additionalOffsetter optional, to set additional offsets to the FloatingTaskView - * to account for translations. If {@code null} then the - * translation values from originalView will be used */ public static FloatingTaskView getFloatingTaskView(StatefulActivity launcher, View originalView, @Nullable Bitmap thumbnail, Drawable icon, RectF positionOut) { @@ -133,21 +126,22 @@ public class FloatingTaskView extends FrameLayout { setLayoutParams(lp); } - public void update(RectF position, float progress) { + public void update(RectF bounds, float progress) { MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); - float dX = position.left - mStartingPosition.left; - float dY = position.top - lp.topMargin; - float scaleX = position.width() / lp.width; - float scaleY = position.height() / lp.height; + float dX = bounds.left - mStartingPosition.left; + float dY = bounds.top - lp.topMargin; + float scaleX = bounds.width() / lp.width; + float scaleY = bounds.height() / lp.height; - mCurrentFullscreenParams.updateParams(position, progress, scaleX, scaleY); + mFullscreenParams.updateParams(bounds, progress, scaleX, scaleY); setTranslationX(dX); setTranslationY(dY); setScaleX(scaleX); setScaleY(scaleY); mSplitPlaceholderView.invalidate(); + mThumbnailView.invalidate(); float childScaleX = 1f / scaleX; float childScaleY = 1f / scaleY; @@ -178,8 +172,8 @@ public class FloatingTaskView extends FrameLayout { } public void addAnimation(PendingAnimation animation, RectF startingBounds, Rect endBounds, - boolean fadeWithThumbnail, boolean isInitialSplit) { - mCurrentFullscreenParams.setIsInitialSplit(isInitialSplit); + boolean fadeWithThumbnail, boolean isStagedTask) { + mFullscreenParams.setIsStagedTask(isStagedTask); final BaseDragLayer dragLayer = mActivity.getDragLayer(); int[] dragLayerBounds = new int[2]; dragLayer.getLocationOnScreen(dragLayerBounds); @@ -219,6 +213,25 @@ public class FloatingTaskView extends FrameLayout { transitionAnimator.addUpdateListener(listener); } + public void drawRoundedRect(Canvas canvas, Paint paint) { + if (mFullscreenParams == null) { + return; + } + + canvas.drawRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), + mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleX, + mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleY, + paint); + } + + public float getFullscreenScaleX() { + return mFullscreenParams.mScaleX; + } + + public float getFullscreenScaleY() { + return mFullscreenParams.mScaleY; + } + private static class SplitOverlayProperties { private final float finalTaskViewScaleX; @@ -247,9 +260,8 @@ public class FloatingTaskView extends FrameLayout { private final float mCornerRadius; private final float mWindowCornerRadius; - - public boolean mIsInitialSplit = true; - public final RectF mFloatingTaskViewBounds = new RectF(); + public boolean mIsStagedTask; + public final RectF mBounds = new RectF(); public float mCurrentDrawnCornerRadius; public float mScaleX = 1; public float mScaleY = 1; @@ -261,17 +273,16 @@ public class FloatingTaskView extends FrameLayout { mCurrentDrawnCornerRadius = mCornerRadius; } - public void updateParams(RectF floatingTaskViewBounds, float progress, float scaleX, - float scaleY) { - mFloatingTaskViewBounds.set(floatingTaskViewBounds); + public void updateParams(RectF bounds, float progress, float scaleX, float scaleY) { + mBounds.set(bounds); mScaleX = scaleX; mScaleY = scaleY; - mCurrentDrawnCornerRadius = mIsInitialSplit ? 0 : + mCurrentDrawnCornerRadius = mIsStagedTask ? mWindowCornerRadius : Utilities.mapRange(progress, mCornerRadius, mWindowCornerRadius); } - public void setIsInitialSplit(boolean isInitialSplit) { - mIsInitialSplit = isInitialSplit; + public void setIsStagedTask(boolean isStagedTask) { + mIsStagedTask = isStagedTask; } } } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 6bb20fca32..59248c6851 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -425,6 +425,7 @@ public abstract class RecentsView { if (success) { @@ -3996,14 +3999,14 @@ public abstract class RecentsView mSplitSelectStateController.setSecondTask( task, aBoolean1 -> RecentsView.this.resetFromSplitSelectionState())); @@ -4071,10 +4074,9 @@ public abstract class RecentsView ALPHA_FLOAT = new FloatProperty("SplitViewAlpha") { @@ -54,7 +53,7 @@ public class SplitPlaceholderView extends FrameLayout { public SplitPlaceholderView(Context context, AttributeSet attrs) { super(context, attrs); - mPaint.setColor(getThemePrimaryColor(context)); + mPaint.setColor(getThemeBackgroundColor(context)); setWillNotDraw(false); } @@ -64,6 +63,19 @@ public class SplitPlaceholderView extends FrameLayout { drawBackground(canvas); super.dispatchDraw(canvas); + + if (mIconView != null) { + // Center the icon view in the visible area. + getLocalVisibleRect(mTempRect); + FloatingTaskView parent = (FloatingTaskView) getParent(); + FrameLayout.LayoutParams params = + (FrameLayout.LayoutParams) mIconView.getLayoutParams(); + params.leftMargin = Math.round(mTempRect.centerX() / parent.getFullscreenScaleX() + - 1.0f * mIconView.getDrawableWidth() / 2); + params.topMargin = Math.round(mTempRect.centerY() / parent.getFullscreenScaleY() + - 1.0f * mIconView.getDrawableHeight() / 2); + mIconView.setLayoutParams(params); + } } @Nullable @@ -71,10 +83,6 @@ public class SplitPlaceholderView extends FrameLayout { return mIconView; } - public void setFullscreenParams(FloatingTaskView.FullscreenDrawParams fullscreenParams) { - mFullscreenParams = fullscreenParams; - } - public void setIcon(Drawable drawable, int iconSize) { if (mIconView == null) { mIconView = new IconView(getContext()); @@ -83,23 +91,17 @@ public class SplitPlaceholderView extends FrameLayout { mIconView.setDrawable(drawable); mIconView.setDrawableSize(iconSize, iconSize); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(iconSize, iconSize); - params.gravity = Gravity.CENTER; mIconView.setLayoutParams(params); } private void drawBackground(Canvas canvas) { - if (mFullscreenParams == null) { - return; - } - - canvas.drawRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), - mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleX, - mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleY, mPaint); + FloatingTaskView parent = (FloatingTaskView) getParent(); + parent.drawRoundedRect(canvas, mPaint); } - private static int getThemePrimaryColor(Context context) { + private static int getThemeBackgroundColor(Context context) { final TypedValue value = new TypedValue(); - context.getTheme().resolveAttribute(android.R.attr.colorPrimary, value, true); + context.getTheme().resolveAttribute(android.R.attr.colorBackground, value, true); return value.data; } } diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 338e218b9e..537e0e3d3c 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -353,7 +353,9 @@ 0dp 0dp 0dp - 110dp + 72dp + 16dp + 44dp 200dp diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java index 2609e549a5..90d1ea1465 100644 --- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -393,12 +393,21 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { } @Override - public void getInitialSplitPlaceholderBounds(int placeholderHeight, DeviceProfile dp, - @StagePosition int stagePosition, Rect out) { + public void getInitialSplitPlaceholderBounds(int placeholderHeight, int placeholderInset, + DeviceProfile dp, @StagePosition int stagePosition, Rect out) { // In fake land/seascape, the placeholder always needs to go to the "top" of the device, // which is the same bounds as 0 rotation. int width = dp.widthPx; out.set(0, 0, width, placeholderHeight); + out.inset(placeholderInset, 0); + + // Adjust the top to account for content off screen. This will help to animate the view in + // with rounded corners. + int screenWidth = dp.widthPx; + int screenHeight = dp.heightPx; + int totalHeight = (int) (1.0f * screenHeight / 2 * (screenWidth - 2 * placeholderInset) + / screenWidth); + out.top -= (totalHeight - placeholderHeight); } @Override diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java index 6e594e9cc6..854cfd6b46 100644 --- a/src/com/android/launcher3/touch/PagedOrientationHandler.java +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -110,10 +110,10 @@ public interface PagedOrientationHandler { int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect); List getSplitPositionOptions(DeviceProfile dp); /** - * @param splitholderSize height of placeholder view in portrait, width in landscape + * @param placeholderHeight height of placeholder view in portrait, width in landscape */ - void getInitialSplitPlaceholderBounds(int splitholderSize, DeviceProfile dp, - @StagePosition int stagePosition, Rect out); + void getInitialSplitPlaceholderBounds(int placeholderHeight, int placeholderInset, + DeviceProfile dp, @StagePosition int stagePosition, Rect out); /** * @param splitDividerSize height of split screen drag handle in portrait, width in landscape diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java index 2c9afd6506..c2aee6002e 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -429,29 +429,48 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { } @Override - public void getInitialSplitPlaceholderBounds(int placeholderHeight, DeviceProfile dp, - @StagePosition int stagePosition, Rect out) { - int width = dp.widthPx; - out.set(0, 0, width, placeholderHeight); + public void getInitialSplitPlaceholderBounds(int placeholderHeight, int placeholderInset, + DeviceProfile dp, @StagePosition int stagePosition, Rect out) { + int screenWidth = dp.widthPx; + int screenHeight = dp.heightPx; + out.set(0, 0, screenWidth, placeholderHeight); if (!dp.isLandscape) { // portrait, phone or tablet - spans width of screen, nothing else to do + out.inset(placeholderInset, 0); + + // Adjust the top to account for content off screen. This will help to animate the view + // in with rounded corners. + int totalHeight = (int) (1.0f * screenHeight / 2 * (screenWidth - 2 * placeholderInset) + / screenWidth); + out.top -= (totalHeight - placeholderHeight); return; } // Now we rotate the portrait rect depending on what side we want pinned boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT; - int screenHeight = dp.heightPx; - float postRotateScale = (float) screenHeight / width; + float postRotateScale = (float) screenHeight / screenWidth; mTmpMatrix.reset(); mTmpMatrix.postRotate(pinToRight ? 90 : 270); - mTmpMatrix.postTranslate(pinToRight ? width : 0, pinToRight ? 0 : width); + mTmpMatrix.postTranslate(pinToRight ? screenWidth : 0, pinToRight ? 0 : screenWidth); // The placeholder height stays constant after rotation, so we don't change width scale mTmpMatrix.postScale(1, postRotateScale); mTmpRectF.set(out); mTmpMatrix.mapRect(mTmpRectF); + mTmpRectF.inset(0, placeholderInset); mTmpRectF.roundOut(out); + + // Adjust the top to account for content off screen. This will help to animate the view in + // with rounded corners. + int totalWidth = (int) (1.0f * screenWidth / 2 * (screenHeight - 2 * placeholderInset) + / screenHeight); + int width = out.width(); + if (pinToRight) { + out.right += totalWidth - width; + } else { + out.left -= totalWidth - width; + } } @Override