Merge "Improve the staged split animation" into tm-dev am: 2ac459e7f2

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/17031258

Change-Id: I0eb1d994ba1a95ef72498dc38c60df4a1f2719f8
This commit is contained in:
TreeHugger Robot
2022-03-01 07:58:49 +00:00
committed by Automerger Merge Worker
5 changed files with 182 additions and 35 deletions
@@ -0,0 +1,86 @@
/*
* Copyright 2022 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.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
/**
* A child view of {@link com.android.quickstep.views.FloatingTaskView} to draw the thumbnail in a
* rounded corner frame. While the purpose of this class sounds similar to
* {@link TaskThumbnailView}, it doesn't need a lot of complex logic in {@link TaskThumbnailView}
* in relation to moving with {@link RecentsView}.
*/
public class FloatingTaskThumbnailView extends View {
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Matrix mMatrix = new Matrix();
private @Nullable BitmapShader mBitmapShader;
private @Nullable Bitmap mBitmap;
private FloatingTaskView.FullscreenDrawParams mFullscreenParams;
public FloatingTaskThumbnailView(Context context) {
this(context, null);
}
public FloatingTaskThumbnailView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatingTaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
if (mFullscreenParams == null || mBitmap == null) {
return;
}
// Scale down the bitmap to fix x, and crop in y.
float scale = 1.0f * getMeasuredWidth() / mBitmap.getWidth();
mMatrix.postScale(scale, scale);
mBitmapShader.setLocalMatrix(mMatrix);
canvas.drawRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleX,
mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleY, mPaint);
}
public void setThumbnail(Bitmap bitmap) {
mBitmap = bitmap;
if (bitmap != null) {
mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(mBitmapShader);
}
}
public void setFullscreenParams(FloatingTaskView.FullscreenDrawParams fullscreenParams) {
mFullscreenParams = fullscreenParams;
}
}
@@ -15,7 +15,6 @@ import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -29,6 +28,8 @@ import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.views.BaseDragLayer; import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.util.MultiValueUpdateListener; import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.systemui.shared.system.QuickStepContract;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -50,9 +51,9 @@ public class FloatingTaskView extends FrameLayout {
private RectF mStartingPosition; private RectF mStartingPosition;
private final StatefulActivity mActivity; private final StatefulActivity mActivity;
private final boolean mIsRtl; private final boolean mIsRtl;
private final Rect mOutline = new Rect(); private final FullscreenDrawParams mCurrentFullscreenParams;
private PagedOrientationHandler mOrientationHandler; private PagedOrientationHandler mOrientationHandler;
private ImageView mImageView; private FloatingTaskThumbnailView mThumbnailView;
public FloatingTaskView(Context context) { public FloatingTaskView(Context context) {
this(context, null); this(context, null);
@@ -66,16 +67,17 @@ public class FloatingTaskView extends FrameLayout {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
mActivity = BaseActivity.fromContext(context); mActivity = BaseActivity.fromContext(context);
mIsRtl = Utilities.isRtl(getResources()); mIsRtl = Utilities.isRtl(getResources());
mCurrentFullscreenParams = new FullscreenDrawParams(context);
} }
@Override @Override
protected void onFinishInflate() { protected void onFinishInflate() {
super.onFinishInflate(); super.onFinishInflate();
mImageView = findViewById(R.id.thumbnail); mThumbnailView = findViewById(R.id.thumbnail);
mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); mThumbnailView.setFullscreenParams(mCurrentFullscreenParams);
mImageView.setLayerType(LAYER_TYPE_HARDWARE, null);
mSplitPlaceholderView = findViewById(R.id.split_placeholder); mSplitPlaceholderView = findViewById(R.id.split_placeholder);
mSplitPlaceholderView.setAlpha(0); mSplitPlaceholderView.setAlpha(0);
mSplitPlaceholderView.setFullscreenParams(mCurrentFullscreenParams);
} }
private void init(StatefulActivity launcher, View originalView, @Nullable Bitmap thumbnail, private void init(StatefulActivity launcher, View originalView, @Nullable Bitmap thumbnail,
@@ -86,13 +88,11 @@ public class FloatingTaskView extends FrameLayout {
(InsettableFrameLayout.LayoutParams) getLayoutParams(); (InsettableFrameLayout.LayoutParams) getLayoutParams();
mSplitPlaceholderView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height)); mSplitPlaceholderView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
positionOut.round(mOutline);
setPivotX(0); setPivotX(0);
setPivotY(0); setPivotY(0);
// Copy bounds of exiting thumbnail into ImageView // Copy bounds of exiting thumbnail into ImageView
mImageView.setImageBitmap(thumbnail); mThumbnailView.setThumbnail(thumbnail);
mImageView.setVisibility(VISIBLE);
RecentsView recentsView = launcher.getOverviewPanel(); RecentsView recentsView = launcher.getOverviewPanel();
mOrientationHandler = recentsView.getPagedOrientationHandler(); mOrientationHandler = recentsView.getPagedOrientationHandler();
@@ -133,27 +133,24 @@ public class FloatingTaskView extends FrameLayout {
setLayoutParams(lp); setLayoutParams(lp);
} }
// TODO(194414938) set correct corner radii public void update(RectF position, float progress) {
public void update(RectF position, float progress, float windowRadius) {
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
float dX = position.left - mStartingPosition.left; float dX = position.left - mStartingPosition.left;
float dY = position.top - lp.topMargin; float dY = position.top - lp.topMargin;
float scaleX = position.width() / lp.width;
float scaleY = position.height() / lp.height;
mCurrentFullscreenParams.updateParams(position, progress, scaleX, scaleY);
setTranslationX(dX); setTranslationX(dX);
setTranslationY(dY); setTranslationY(dY);
float scaleX = position.width() / lp.width;
float scaleY = position.height() / lp.height;
setScaleX(scaleX); setScaleX(scaleX);
setScaleY(scaleY); setScaleY(scaleY);
mSplitPlaceholderView.invalidate();
float childScaleX = 1f / scaleX; float childScaleX = 1f / scaleX;
float childScaleY = 1f / scaleY; float childScaleY = 1f / scaleY;
invalidate();
// TODO(194414938) seems like this scale value could be fine tuned, some stretchiness
mImageView.setScaleX(1f / scaleX + scaleX * progress);
mImageView.setScaleY(1f / scaleY + scaleY * progress);
mOrientationHandler.setPrimaryScale(mSplitPlaceholderView.getIconView(), childScaleX); mOrientationHandler.setPrimaryScale(mSplitPlaceholderView.getIconView(), childScaleX);
mOrientationHandler.setSecondaryScale(mSplitPlaceholderView.getIconView(), childScaleY); mOrientationHandler.setSecondaryScale(mSplitPlaceholderView.getIconView(), childScaleY);
} }
@@ -181,7 +178,8 @@ public class FloatingTaskView extends FrameLayout {
} }
public void addAnimation(PendingAnimation animation, RectF startingBounds, Rect endBounds, public void addAnimation(PendingAnimation animation, RectF startingBounds, Rect endBounds,
boolean fadeWithThumbnail) { boolean fadeWithThumbnail, boolean isInitialSplit) {
mCurrentFullscreenParams.setIsInitialSplit(isInitialSplit);
final BaseDragLayer dragLayer = mActivity.getDragLayer(); final BaseDragLayer dragLayer = mActivity.getDragLayer();
int[] dragLayerBounds = new int[2]; int[] dragLayerBounds = new int[2];
dragLayer.getLocationOnScreen(dragLayerBounds); dragLayer.getLocationOnScreen(dragLayerBounds);
@@ -191,22 +189,16 @@ public class FloatingTaskView extends FrameLayout {
ValueAnimator transitionAnimator = ValueAnimator.ofFloat(0, 1); ValueAnimator transitionAnimator = ValueAnimator.ofFloat(0, 1);
animation.add(transitionAnimator); animation.add(transitionAnimator);
long animDuration = animation.getDuration(); long animDuration = animation.getDuration();
Rect crop = new Rect();
RectF floatingTaskViewBounds = new RectF(); RectF floatingTaskViewBounds = new RectF();
final float initialWindowRadius = supportsRoundedCornersOnWindows(getResources())
? Math.max(crop.width(), crop.height()) / 2f
: 0f;
if (fadeWithThumbnail) { if (fadeWithThumbnail) {
animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT, animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT,
0, 1, ACCEL); 0, 1, ACCEL);
animation.addFloat(mImageView, LauncherAnimUtils.VIEW_ALPHA, animation.addFloat(mThumbnailView, LauncherAnimUtils.VIEW_ALPHA,
1, 0, DEACCEL_3); 1, 0, DEACCEL_3);
} }
MultiValueUpdateListener listener = new MultiValueUpdateListener() { MultiValueUpdateListener listener = new MultiValueUpdateListener() {
final FloatProp mWindowRadius = new FloatProp(initialWindowRadius,
initialWindowRadius, 0, animDuration, LINEAR);
final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration, LINEAR); final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration, LINEAR);
final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration, LINEAR); final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration, LINEAR);
final FloatProp mTaskViewScaleX = new FloatProp(1f, prop.finalTaskViewScaleX, 0, final FloatProp mTaskViewScaleX = new FloatProp(1f, prop.finalTaskViewScaleX, 0,
@@ -221,7 +213,7 @@ public class FloatingTaskView extends FrameLayout {
Utilities.scaleRectFAboutCenter(floatingTaskViewBounds, mTaskViewScaleX.value, Utilities.scaleRectFAboutCenter(floatingTaskViewBounds, mTaskViewScaleX.value,
mTaskViewScaleY.value); mTaskViewScaleY.value);
update(floatingTaskViewBounds, percent, mWindowRadius.value * 1); update(floatingTaskViewBounds, percent);
} }
}; };
transitionAnimator.addUpdateListener(listener); transitionAnimator.addUpdateListener(listener);
@@ -250,4 +242,36 @@ public class FloatingTaskView extends FrameLayout {
dY = centerY - startTaskViewBounds.centerY(); dY = centerY - startTaskViewBounds.centerY();
} }
} }
public static class FullscreenDrawParams {
private final float mCornerRadius;
private final float mWindowCornerRadius;
public boolean mIsInitialSplit = true;
public final RectF mFloatingTaskViewBounds = new RectF();
public float mCurrentDrawnCornerRadius;
public float mScaleX = 1;
public float mScaleY = 1;
public FullscreenDrawParams(Context context) {
mCornerRadius = TaskCornerRadius.get(context);
mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context);
mCurrentDrawnCornerRadius = mCornerRadius;
}
public void updateParams(RectF floatingTaskViewBounds, float progress, float scaleX,
float scaleY) {
mFloatingTaskViewBounds.set(floatingTaskViewBounds);
mScaleX = scaleX;
mScaleY = scaleY;
mCurrentDrawnCornerRadius = mIsInitialSplit ? 0 :
Utilities.mapRange(progress, mCornerRadius, mWindowCornerRadius);
}
public void setIsInitialSplit(boolean isInitialSplit) {
mIsInitialSplit = isInitialSplit;
}
}
} }
@@ -2732,7 +2732,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
mSplitHiddenTaskView.getIconView().getDrawable(), startingTaskRect); mSplitHiddenTaskView.getIconView().getDrawable(), startingTaskRect);
mFirstFloatingTaskView.setAlpha(1); mFirstFloatingTaskView.setAlpha(1);
mFirstFloatingTaskView.addAnimation(anim, startingTaskRect, mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
mTempRect, true /*fadeWithThumbnail*/); mTempRect, true /* fadeWithThumbnail */, true /* isInitialSplit */);
} else { } else {
mSplitSelectSource.view.setVisibility(INVISIBLE); mSplitSelectSource.view.setVisibility(INVISIBLE);
mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity, mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
@@ -2740,7 +2740,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
mSplitSelectSource.drawable, startingTaskRect); mSplitSelectSource.drawable, startingTaskRect);
mFirstFloatingTaskView.setAlpha(1); mFirstFloatingTaskView.setAlpha(1);
mFirstFloatingTaskView.addAnimation(anim, startingTaskRect, mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
mTempRect, true /*fadeWithThumbnail*/); mTempRect, true /* fadeWithThumbnail */, true /* isInitialSplit */);
} }
anim.addEndListener(success -> { anim.addEndListener(success -> {
if (success) { if (success) {
@@ -4030,14 +4030,14 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
mFirstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds); mFirstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
mFirstFloatingTaskView.addAnimation(pendingAnimation, mFirstFloatingTaskView.addAnimation(pendingAnimation,
new RectF(firstTaskStartingBounds), firstTaskEndingBounds, new RectF(firstTaskStartingBounds), firstTaskEndingBounds,
false /*fadeWithThumbnail*/); false /* fadeWithThumbnail */, false /* isInitialSplit */);
mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity, mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
thumbnailView, thumbnailView.getThumbnail(), thumbnailView, thumbnailView.getThumbnail(),
iconView.getDrawable(), secondTaskStartingBounds); iconView.getDrawable(), secondTaskStartingBounds);
mSecondFloatingTaskView.setAlpha(1); mSecondFloatingTaskView.setAlpha(1);
mSecondFloatingTaskView.addAnimation(pendingAnimation, secondTaskStartingBounds, mSecondFloatingTaskView.addAnimation(pendingAnimation, secondTaskStartingBounds,
secondTaskEndingBounds, true /* fadeWithThumbnail */); secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isInitialSplit */);
pendingAnimation.addEndListener(aBoolean -> pendingAnimation.addEndListener(aBoolean ->
mSplitSelectStateController.setSecondTaskId(task.key.id, mSplitSelectStateController.setSecondTaskId(task.key.id,
aBoolean1 -> RecentsView.this.resetFromSplitSelectionState())); aBoolean1 -> RecentsView.this.resetFromSplitSelectionState()));
@@ -4110,7 +4110,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
mTempRectF.set(mTempRect); mTempRectF.set(mTempRect);
// TODO(194414938) set correct corner radius // TODO(194414938) set correct corner radius
mFirstFloatingTaskView.updateOrientationHandler(mOrientationHandler); mFirstFloatingTaskView.updateOrientationHandler(mOrientationHandler);
mFirstFloatingTaskView.update(mTempRectF, /*progress=*/1f, /*windowRadius=*/0f); mFirstFloatingTaskView.update(mTempRectF, /*progress=*/1f);
PagedOrientationHandler orientationHandler = getPagedOrientationHandler(); PagedOrientationHandler orientationHandler = getPagedOrientationHandler();
Pair<FloatProperty, FloatProperty> taskViewsFloat = Pair<FloatProperty, FloatProperty> taskViewsFloat =
@@ -17,9 +17,12 @@
package com.android.quickstep.views; package com.android.quickstep.views;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.FloatProperty; import android.util.FloatProperty;
import android.util.TypedValue;
import android.view.Gravity; import android.view.Gravity;
import android.widget.FrameLayout; import android.widget.FrameLayout;
@@ -27,6 +30,10 @@ import androidx.annotation.Nullable;
public class SplitPlaceholderView extends FrameLayout { public class SplitPlaceholderView extends FrameLayout {
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private FloatingTaskView.FullscreenDrawParams mFullscreenParams;
public static final FloatProperty<SplitPlaceholderView> ALPHA_FLOAT = public static final FloatProperty<SplitPlaceholderView> ALPHA_FLOAT =
new FloatProperty<SplitPlaceholderView>("SplitViewAlpha") { new FloatProperty<SplitPlaceholderView>("SplitViewAlpha") {
@Override @Override
@@ -46,6 +53,17 @@ public class SplitPlaceholderView extends FrameLayout {
public SplitPlaceholderView(Context context, AttributeSet attrs) { public SplitPlaceholderView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
mPaint.setColor(getThemePrimaryColor(context));
setWillNotDraw(false);
}
@Override
protected void dispatchDraw(Canvas canvas) {
// Call this before super call to draw below the children.
drawBackground(canvas);
super.dispatchDraw(canvas);
} }
@Nullable @Nullable
@@ -53,6 +71,10 @@ public class SplitPlaceholderView extends FrameLayout {
return mIconView; return mIconView;
} }
public void setFullscreenParams(FloatingTaskView.FullscreenDrawParams fullscreenParams) {
mFullscreenParams = fullscreenParams;
}
public void setIcon(Drawable drawable, int iconSize) { public void setIcon(Drawable drawable, int iconSize) {
if (mIconView == null) { if (mIconView == null) {
mIconView = new IconView(getContext()); mIconView = new IconView(getContext());
@@ -64,4 +86,20 @@ public class SplitPlaceholderView extends FrameLayout {
params.gravity = Gravity.CENTER; params.gravity = Gravity.CENTER;
mIconView.setLayoutParams(params); 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);
}
private static int getThemePrimaryColor(Context context) {
final TypedValue value = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.colorPrimary, value, true);
return value.data;
}
} }
+1 -2
View File
@@ -4,7 +4,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<ImageView <com.android.quickstep.views.FloatingTaskThumbnailView
android:id="@+id/thumbnail" android:id="@+id/thumbnail"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@@ -14,7 +14,6 @@
android:id="@+id/split_placeholder" android:id="@+id/split_placeholder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/split_placeholder_size" android:layout_height="@dimen/split_placeholder_size"
android:background="?android:colorPrimary"
android:visibility="gone" /> android:visibility="gone" />
</com.android.quickstep.views.FloatingTaskView> </com.android.quickstep.views.FloatingTaskView>