diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskThumbnailView.java new file mode 100644 index 0000000000..cfb00568d0 --- /dev/null +++ b/quickstep/src/com/android/quickstep/views/FloatingTaskThumbnailView.java @@ -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; + } +} diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java index f2f1c3f5f6..c59dc1893a 100644 --- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java +++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java @@ -15,7 +15,6 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; -import android.widget.ImageView; import androidx.annotation.Nullable; @@ -29,6 +28,8 @@ import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.util.MultiValueUpdateListener; +import com.android.quickstep.util.TaskCornerRadius; +import com.android.systemui.shared.system.QuickStepContract; import java.util.function.Consumer; @@ -50,9 +51,9 @@ public class FloatingTaskView extends FrameLayout { private RectF mStartingPosition; private final StatefulActivity mActivity; private final boolean mIsRtl; - private final Rect mOutline = new Rect(); + private final FullscreenDrawParams mCurrentFullscreenParams; private PagedOrientationHandler mOrientationHandler; - private ImageView mImageView; + private FloatingTaskThumbnailView mThumbnailView; public FloatingTaskView(Context context) { this(context, null); @@ -66,16 +67,17 @@ public class FloatingTaskView extends FrameLayout { super(context, attrs, defStyleAttr); mActivity = BaseActivity.fromContext(context); mIsRtl = Utilities.isRtl(getResources()); + mCurrentFullscreenParams = new FullscreenDrawParams(context); } @Override protected void onFinishInflate() { super.onFinishInflate(); - mImageView = findViewById(R.id.thumbnail); - mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); - mImageView.setLayerType(LAYER_TYPE_HARDWARE, null); + 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, @@ -86,13 +88,11 @@ public class FloatingTaskView extends FrameLayout { (InsettableFrameLayout.LayoutParams) getLayoutParams(); mSplitPlaceholderView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height)); - positionOut.round(mOutline); setPivotX(0); setPivotY(0); // Copy bounds of exiting thumbnail into ImageView - mImageView.setImageBitmap(thumbnail); - mImageView.setVisibility(VISIBLE); + mThumbnailView.setThumbnail(thumbnail); RecentsView recentsView = launcher.getOverviewPanel(); mOrientationHandler = recentsView.getPagedOrientationHandler(); @@ -133,27 +133,24 @@ public class FloatingTaskView extends FrameLayout { setLayoutParams(lp); } - // TODO(194414938) set correct corner radii - public void update(RectF position, float progress, float windowRadius) { + public void update(RectF position, 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; + + mCurrentFullscreenParams.updateParams(position, progress, scaleX, scaleY); setTranslationX(dX); setTranslationY(dY); - - float scaleX = position.width() / lp.width; - float scaleY = position.height() / lp.height; setScaleX(scaleX); setScaleY(scaleY); + mSplitPlaceholderView.invalidate(); + float childScaleX = 1f / scaleX; 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.setSecondaryScale(mSplitPlaceholderView.getIconView(), childScaleY); } @@ -181,7 +178,8 @@ public class FloatingTaskView extends FrameLayout { } public void addAnimation(PendingAnimation animation, RectF startingBounds, Rect endBounds, - boolean fadeWithThumbnail) { + boolean fadeWithThumbnail, boolean isInitialSplit) { + mCurrentFullscreenParams.setIsInitialSplit(isInitialSplit); final BaseDragLayer dragLayer = mActivity.getDragLayer(); int[] dragLayerBounds = new int[2]; dragLayer.getLocationOnScreen(dragLayerBounds); @@ -191,22 +189,16 @@ public class FloatingTaskView extends FrameLayout { ValueAnimator transitionAnimator = ValueAnimator.ofFloat(0, 1); animation.add(transitionAnimator); long animDuration = animation.getDuration(); - Rect crop = new Rect(); RectF floatingTaskViewBounds = new RectF(); - final float initialWindowRadius = supportsRoundedCornersOnWindows(getResources()) - ? Math.max(crop.width(), crop.height()) / 2f - : 0f; if (fadeWithThumbnail) { animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT, 0, 1, ACCEL); - animation.addFloat(mImageView, LauncherAnimUtils.VIEW_ALPHA, + animation.addFloat(mThumbnailView, LauncherAnimUtils.VIEW_ALPHA, 1, 0, DEACCEL_3); } 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 mDy = new FloatProp(0, prop.dY, 0, animDuration, LINEAR); final FloatProp mTaskViewScaleX = new FloatProp(1f, prop.finalTaskViewScaleX, 0, @@ -221,7 +213,7 @@ public class FloatingTaskView extends FrameLayout { Utilities.scaleRectFAboutCenter(floatingTaskViewBounds, mTaskViewScaleX.value, mTaskViewScaleY.value); - update(floatingTaskViewBounds, percent, mWindowRadius.value * 1); + update(floatingTaskViewBounds, percent); } }; transitionAnimator.addUpdateListener(listener); @@ -250,4 +242,36 @@ public class FloatingTaskView extends FrameLayout { 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; + } + } } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index cb7e08a255..5e331e2e86 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -2732,7 +2732,7 @@ public abstract class RecentsView { if (success) { @@ -4030,14 +4030,14 @@ public abstract class RecentsView mSplitSelectStateController.setSecondTaskId(task.key.id, aBoolean1 -> RecentsView.this.resetFromSplitSelectionState())); @@ -4110,7 +4110,7 @@ public abstract class RecentsView taskViewsFloat = diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java index cfa482f233..d37dfbf991 100644 --- a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java +++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java @@ -17,9 +17,12 @@ package com.android.quickstep.views; import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.FloatProperty; +import android.util.TypedValue; import android.view.Gravity; import android.widget.FrameLayout; @@ -27,6 +30,10 @@ import androidx.annotation.Nullable; public class SplitPlaceholderView extends FrameLayout { + private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private FloatingTaskView.FullscreenDrawParams mFullscreenParams; + public static final FloatProperty ALPHA_FLOAT = new FloatProperty("SplitViewAlpha") { @Override @@ -46,6 +53,17 @@ public class SplitPlaceholderView extends FrameLayout { public SplitPlaceholderView(Context context, AttributeSet 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 @@ -53,6 +71,10 @@ 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()); @@ -64,4 +86,20 @@ public class SplitPlaceholderView extends FrameLayout { 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); + } + + private static int getThemePrimaryColor(Context context) { + final TypedValue value = new TypedValue(); + context.getTheme().resolveAttribute(android.R.attr.colorPrimary, value, true); + return value.data; + } } diff --git a/res/layout/floating_split_select_view.xml b/res/layout/floating_split_select_view.xml index 8d47f4ef49..e4ca52e371 100644 --- a/res/layout/floating_split_select_view.xml +++ b/res/layout/floating_split_select_view.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - \ No newline at end of file