Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4ff3615b authored by Tracy Zhou's avatar Tracy Zhou
Browse files

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
parent 025bf08e
Loading
Loading
Loading
Loading
+3 −10
Original line number Diff line number Diff line
@@ -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;
    }
}
+43 −32
Original line number Diff line number Diff line
@@ -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;
        }
    }
}
+9 −7
Original line number Diff line number Diff line
@@ -425,6 +425,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
    private final int mScrollHapticMinGapMillis;
    private final RecentsModel mModel;
    private final int mSplitPlaceholderSize;
    private final int mSplitPlaceholderInset;
    private final ClearAllButton mClearAllButton;
    private final Rect mClearAllButtonDeadZoneRect = new Rect();
    private final Rect mTaskViewDeadZoneRect = new Rect();
@@ -697,6 +698,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
        setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
        mSplitPlaceholderSize = getResources().getDimensionPixelSize(
                R.dimen.split_placeholder_size);
        mSplitPlaceholderInset = getResources().getDimensionPixelSize(
                R.dimen.split_placeholder_inset);
        mSquaredTouchSlop = squaredTouchSlop(context);

        mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
@@ -2697,7 +2700,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
     */
    private void createInitialSplitSelectAnimation(PendingAnimation anim) {
        mOrientationHandler.getInitialSplitPlaceholderBounds(mSplitPlaceholderSize,
                mActivity.getDeviceProfile(),
                mSplitPlaceholderInset, mActivity.getDeviceProfile(),
                mSplitSelectStateController.getActiveSplitStagePosition(), mTempRect);

        RectF startingTaskRect = new RectF();
@@ -2709,7 +2712,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
                    mSplitHiddenTaskView.getIconView().getDrawable(), startingTaskRect);
            mFirstFloatingTaskView.setAlpha(1);
            mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
                    mTempRect, true /* fadeWithThumbnail */, true /* isInitialSplit */);
                    mTempRect, true /* fadeWithThumbnail */, true /* isStagedTask */);
        } else {
            mSplitSelectSource.view.setVisibility(INVISIBLE);
            mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
@@ -2717,7 +2720,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
                    mSplitSelectSource.drawable, startingTaskRect);
            mFirstFloatingTaskView.setAlpha(1);
            mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
                    mTempRect, true /* fadeWithThumbnail */, true /* isInitialSplit */);
                    mTempRect, true /* fadeWithThumbnail */, true /* isStagedTask */);
        }
        anim.addEndListener(success -> {
            if (success) {
@@ -3996,14 +3999,14 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
        mFirstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
        mFirstFloatingTaskView.addAnimation(pendingAnimation,
                new RectF(firstTaskStartingBounds), firstTaskEndingBounds,
                false /* fadeWithThumbnail */, false /* isInitialSplit */);
                false /* fadeWithThumbnail */, true /* isStagedTask */);

        mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
                thumbnailView, thumbnailView.getThumbnail(),
                iconView.getDrawable(), secondTaskStartingBounds);
        mSecondFloatingTaskView.setAlpha(1);
        mSecondFloatingTaskView.addAnimation(pendingAnimation, secondTaskStartingBounds,
                secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isInitialSplit */);
                secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */);
        pendingAnimation.addEndListener(aBoolean ->
                mSplitSelectStateController.setSecondTask(
                        task, aBoolean1 -> RecentsView.this.resetFromSplitSelectionState()));
@@ -4071,10 +4074,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T

    protected void onRotateInSplitSelectionState() {
        mOrientationHandler.getInitialSplitPlaceholderBounds(mSplitPlaceholderSize,
                mActivity.getDeviceProfile(),
                mSplitPlaceholderInset, mActivity.getDeviceProfile(),
                mSplitSelectStateController.getActiveSplitStagePosition(), mTempRect);
        mTempRectF.set(mTempRect);
        // TODO(194414938) set correct corner radius
        mFirstFloatingTaskView.updateOrientationHandler(mOrientationHandler);
        mFirstFloatingTaskView.update(mTempRectF, /*progress=*/1f);

+20 −18
Original line number Diff line number Diff line
@@ -19,11 +19,11 @@ package com.android.quickstep.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
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;

import androidx.annotation.Nullable;
@@ -31,8 +31,7 @@ import androidx.annotation.Nullable;
public class SplitPlaceholderView extends FrameLayout {

    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private FloatingTaskView.FullscreenDrawParams mFullscreenParams;
    private final Rect mTempRect = new Rect();

    public static final FloatProperty<SplitPlaceholderView> ALPHA_FLOAT =
            new FloatProperty<SplitPlaceholderView>("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;
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -353,7 +353,9 @@
    <dimen name="overview_grid_side_margin">0dp</dimen>
    <dimen name="overview_grid_row_spacing">0dp</dimen>
    <dimen name="overview_page_spacing">0dp</dimen>
    <dimen name="split_placeholder_size">110dp</dimen>
    <dimen name="split_placeholder_size">72dp</dimen>
    <dimen name="split_placeholder_inset">16dp</dimen>
    <dimen name="split_placeholder_icon_size">44dp</dimen>
    <dimen name="task_menu_width_grid">200dp</dimen>


Loading