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

Commit 5fdd300e authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Fixing task open animation in split screen mode

Removing duplicate logic for task bounds calculation and using
ClipAnimationHelper everywhere

Bug: 78126501
Change-Id: I5ed8b2f9162f0bd6144a849f5b677963776d325c
parent c2c907ed
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.views.RecentsView;
@@ -233,12 +234,16 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag
            return false;
        }

        ClipAnimationHelper helper = new ClipAnimationHelper();
        target.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets, helper)
                .setDuration(RECENTS_LAUNCH_DURATION));

        Animator childStateAnimation = null;
        // Found a visible recents task that matches the opening app, lets launch the app from there
        Animator launcherAnim;
        final AnimatorListenerAdapter windowAnimEndListener;
        if (launcherClosing) {
            launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
            launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView, helper);
            launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
            launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);

@@ -258,9 +263,6 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag
                }
            };
        }

        target.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets)
                .setDuration(RECENTS_LAUNCH_DURATION));
        target.play(launcherAnim);

        // Set the current animation first, before adding windowAnimEndListener. Setting current
+4 −2
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.fallback.RecentsRootView;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.views.RecentsViewContainer;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityOptionsCompat;
@@ -193,13 +194,14 @@ public class RecentsActivity extends BaseDraggingActivity {
            RemoteAnimationTargetCompat[] targets) {
        AnimatorSet target = new AnimatorSet();
        boolean activityClosing = taskIsATargetWithMode(targets, getTaskId(), MODE_CLOSING);
        target.play(getRecentsWindowAnimator(taskView, !activityClosing, targets)
        ClipAnimationHelper helper = new ClipAnimationHelper();
        target.play(getRecentsWindowAnimator(taskView, !activityClosing, targets, helper)
                .setDuration(RECENTS_LAUNCH_DURATION));

        // Found a visible recents task that matches the opening app, lets launch the app from there
        if (activityClosing) {
            Animator adjacentAnimation = mFallbackRecentsView
                    .createAdjacentPageAnimForTaskLaunch(taskView);
                    .createAdjacentPageAnimForTaskLaunch(taskView, helper);
            adjacentAnimation.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
            adjacentAnimation.setDuration(RECENTS_LAUNCH_DURATION);
            adjacentAnimation.addListener(new AnimatorListenerAdapter() {
+0 −117
Original line number Diff line number Diff line
/*
 * 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;

import android.graphics.Rect;

import com.android.launcher3.Utilities;

/**
 * Helper class to interpolate the animation between a task view representation and an actual
 * window.
 */
public class RecentsAnimationInterpolator {

    public static class TaskWindowBounds {
        public float taskScale = 1f;
        public float taskX = 0f;
        public float taskY = 0f;

        public float winScale = 1f;
        public float winX = 0f;
        public float winY = 0f;
        public Rect winCrop = new Rect();

        @Override
        public String toString() {
            return "taskScale=" + taskScale + " taskX=" + taskX + " taskY=" + taskY
                    + " winScale=" + winScale + " winX=" + winX + " winY=" + winY
                    + " winCrop=" + winCrop;
        }
    }

    private TaskWindowBounds mTmpTaskWindowBounds = new TaskWindowBounds();
    private Rect mTmpInsets = new Rect();

    private Rect mWindow;
    private Rect mInsetWindow;
    private Rect mInsets;
    private Rect mTask;
    private Rect mTaskInsets;
    private Rect mThumbnail;

    private float mInitialTaskScale;
    private float mInitialTaskTranslationX;
    private float mFinalTaskScale;
    private Rect mScaledTask;
    private Rect mTargetTask;
    private Rect mSrcWindow;

    public RecentsAnimationInterpolator(Rect window, Rect insets, Rect task, Rect taskInsets,
            float taskScale, float taskTranslationX) {
        mWindow = window;
        mInsets = insets;
        mTask = task;
        mTaskInsets = taskInsets;
        mInsetWindow = new Rect(window);
        Utilities.insetRect(mInsetWindow, insets);

        mThumbnail = new Rect(task);
        Utilities.insetRect(mThumbnail, taskInsets);
        mInitialTaskScale = taskScale;
        mInitialTaskTranslationX = taskTranslationX;
        mFinalTaskScale = (float) mInsetWindow.width() / mThumbnail.width();
        mScaledTask = new Rect(task);
        Utilities.scaleRectAboutCenter(mScaledTask, mFinalTaskScale);
        Rect finalScaledTaskInsets = new Rect(taskInsets);
        Utilities.scaleRect(finalScaledTaskInsets, mFinalTaskScale);
        mTargetTask = new Rect(mInsetWindow);
        mTargetTask.offsetTo(window.left + insets.left - finalScaledTaskInsets.left,
                window.top + insets.top - finalScaledTaskInsets.top);

        float initialWinScale = 1f / mFinalTaskScale;
        Rect scaledWindow = new Rect(mInsetWindow);
        Utilities.scaleRectAboutCenter(scaledWindow, initialWinScale);
        Rect scaledInsets = new Rect(insets);
        Utilities.scaleRect(scaledInsets, initialWinScale);
        mSrcWindow = new Rect(scaledWindow);
        mSrcWindow.offsetTo(mThumbnail.left - scaledInsets.left,
                mThumbnail.top - scaledInsets.top);
    }

    public TaskWindowBounds interpolate(float t) {
        mTmpTaskWindowBounds.taskScale = Utilities.mapRange(t,
                mInitialTaskScale, mFinalTaskScale);
        mTmpTaskWindowBounds.taskX = Utilities.mapRange(t,
                mInitialTaskTranslationX, mTargetTask.left - mScaledTask.left);
        mTmpTaskWindowBounds.taskY = Utilities.mapRange(t,
                0, mTargetTask.top - mScaledTask.top);

        float taskScale = Utilities.mapRange(t, 1, mFinalTaskScale);
        mTmpTaskWindowBounds.winScale = taskScale / mFinalTaskScale;
        mTmpTaskWindowBounds.winX = Utilities.mapRange(t,
                mSrcWindow.left, 0);
        mTmpTaskWindowBounds.winY = Utilities.mapRange(t,
                mSrcWindow.top, 0);

        mTmpInsets.set(mInsets);
        Utilities.scaleRect(mTmpInsets, (1f - t));
        mTmpTaskWindowBounds.winCrop.set(mWindow);
        Utilities.insetRect(mTmpTaskWindowBounds.winCrop, mTmpInsets);

        return mTmpTaskWindowBounds;
    }
}
+41 −52
Original line number Diff line number Diff line
@@ -27,8 +27,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.UserHandle;
import android.util.Log;
import android.view.Surface;
@@ -36,17 +35,17 @@ import android.view.View;

import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.util.ComponentKey;
import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TransactionCompat;

/**
 * Contains helpful methods for retrieving data from {@link Task}s.
@@ -136,74 +135,64 @@ public class TaskUtils {
        return taskView;
    }


    /**
     * @return Animator that controls the window of the opening targets for the recents launch
     * animation.
     */
    public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
            RemoteAnimationTargetCompat[] targets) {
        final RecentsAnimationInterpolator recentsInterpolator = v.getRecentsInterpolator();

        Rect crop = new Rect();
        Matrix matrix = new Matrix();

        ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
            RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
        final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
        appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
        appAnimator.addUpdateListener(new MultiValueUpdateListener() {

            // Defer fading out the view until after the app window gets faded in
            FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
            FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);
            final FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
            final FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);

            boolean isFirstFrame = true;
            final RemoteAnimationTargetSet mTargetSet;

            @Override
            public void onUpdate(float percent) {
                final Surface surface = getSurface(v);
                final long frameNumber = surface != null ? getNextFrameNumber(surface) : -1;
                if (frameNumber == -1) {
                    // Booo, not cool! Our surface got destroyed, so no reason to animate anything.
                    Log.w(TAG, "Failed to animate, surface got destroyed.");
                    return;
                }
                TaskWindowBounds tw = recentsInterpolator.interpolate(percent);
            final RectF mThumbnailRect;
            private Surface mSurface;
            private long mFrameNumber;

            {
                mTargetSet = new RemoteAnimationTargetSet(targets, MODE_OPENING);
                inOutHelper.setTaskTransformCallback((t, app) -> {
                    t.setAlpha(app.leash, mTaskAlpha.value);

                    if (!skipViewChanges) {
                    v.setScaleX(tw.taskScale);
                    v.setScaleY(tw.taskScale);
                    v.setTranslationX(tw.taskX);
                    v.setTranslationY(tw.taskY);
                    v.setAlpha(mViewAlpha.value);
                        t.deferTransactionUntil(app.leash, mSurface, mFrameNumber);
                    }
                });

                matrix.setScale(tw.winScale, tw.winScale);
                matrix.postTranslate(tw.winX, tw.winY);
                crop.set(tw.winCrop);
                inOutHelper.prepareAnimation(true /* isOpening */);
                inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(),
                        mTargetSet.apps.length == 0 ? null : mTargetSet.apps[0]);

                TransactionCompat t = new TransactionCompat();
                if (isFirstFrame) {
                    RemoteAnimationProvider.prepareTargetsForFirstFrame(targets, t, MODE_OPENING);
                    isFirstFrame = false;
                mThumbnailRect = new RectF(inOutHelper.getTargetRect());
                mThumbnailRect.offset(-v.getTranslationX(), -v.getTranslationY());
                Utilities.scaleRectFAboutCenter(mThumbnailRect, 1 / v.getScaleX());
            }
                for (RemoteAnimationTargetCompat target : targets) {
                    if (target.mode == RemoteAnimationTargetCompat.MODE_OPENING) {
                        t.setAlpha(target.leash, mTaskAlpha.value);

                        // TODO: This isn't correct at the beginning of the animation, but better
                        // than nothing.
                        matrix.postTranslate(target.position.x, target.position.y);
                        t.setMatrix(target.leash, matrix);
                        t.setWindowCrop(target.leash, crop);
            @Override
            public void onUpdate(float percent) {
                mSurface = getSurface(v);
                mFrameNumber = mSurface != null ? getNextFrameNumber(mSurface) : -1;
                if (mFrameNumber == -1) {
                    // Booo, not cool! Our surface got destroyed, so no reason to animate anything.
                    Log.w(TAG, "Failed to animate, surface got destroyed.");
                    return;
                }

                RectF taskBounds = inOutHelper.applyTransform(mTargetSet, 1 - percent);
                if (!skipViewChanges) {
                            t.deferTransactionUntil(target.leash, surface, frameNumber);
                        }
                    }
                    float scale = taskBounds.width() / mThumbnailRect.width();
                    v.setScaleX(scale);
                    v.setScaleY(scale);
                    v.setTranslationX(taskBounds.centerX() - mThumbnailRect.centerX());
                    v.setTranslationY(taskBounds.centerY() - mThumbnailRect.centerY());
                    v.setAlpha(mViewAlpha.value);
                }
                t.apply();

                matrix.reset();
            }
        });
        return appAnimator;
+41 −5
Original line number Diff line number Diff line
@@ -20,13 +20,16 @@ import static com.android.launcher3.anim.Interpolators.SCROLL;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;

import android.annotation.TargetApi;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Matrix.ScaleToFit;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
@@ -42,9 +45,12 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TransactionCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;

import java.util.function.BiConsumer;

/**
 * Utility class to handle window clip animation
 */
@TargetApi(Build.VERSION_CODES.P)
public class ClipAnimationHelper {

    // The bounds of the source app in device coordinates
@@ -78,13 +84,21 @@ public class ClipAnimationHelper {
    // Wether or not applyTransform has been called yet since prepareAnimation()
    private boolean mIsFirstFrame = true;

    public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
        mHomeStackBounds.set(homeStackBounds);
    private BiConsumer<TransactionCompat, RemoteAnimationTargetCompat> mTaskTransformCallback =
            (t, a) -> { };

    private void updateSourceStack(RemoteAnimationTargetCompat target) {
        mSourceInsets.set(target.contentInsets);
        mSourceStackBounds.set(target.sourceContainerBounds);

        // TODO: Should sourceContainerBounds already have this offset?
        mSourceStackBounds.offsetTo(target.position.x, target.position.y);

    }

    public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
        mHomeStackBounds.set(homeStackBounds);
        updateSourceStack(target);
    }

    public void updateTargetRect(Rect targetRect) {
@@ -116,7 +130,7 @@ public class ClipAnimationHelper {
        mBoostModeTargetLayers = isOpening ? MODE_OPENING : MODE_CLOSING;
    }

    public void applyTransform(RemoteAnimationTargetSet targetSet, float progress) {
    public RectF applyTransform(RemoteAnimationTargetSet targetSet, float progress) {
        RectF currentRect;
        mTmpRectF.set(mTargetRect);
        Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale);
@@ -153,8 +167,16 @@ public class ClipAnimationHelper {
                    || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
                transaction.setAlpha(app.leash, 1 - progress);
            }

            mTaskTransformCallback.accept(transaction, app);
        }
        transaction.apply();
        return currentRect;
    }

    public void setTaskTransformCallback
            (BiConsumer<TransactionCompat, RemoteAnimationTargetCompat> callback) {
        mTaskTransformCallback = callback;
    }

    public void offsetTarget(float scale, float offsetX, float offsetY) {
@@ -165,6 +187,11 @@ public class ClipAnimationHelper {
    }

    public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) {
        fromTaskThumbnailView(ttv, rv, null);
    }

    public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv,
            @Nullable RemoteAnimationTargetCompat target) {
        BaseDraggingActivity activity = BaseDraggingActivity.fromContext(ttv.getContext());
        BaseDragLayer dl = activity.getDragLayer();

@@ -173,7 +200,9 @@ public class ClipAnimationHelper {
        mHomeStackBounds.set(0, 0, dl.getWidth(), dl.getHeight());
        mHomeStackBounds.offset(pos[0], pos[1]);

        if (rv.shouldUseMultiWindowTaskSizeStrategy()) {
        if (target != null) {
            updateSourceStack(target);
        } else  if (rv.shouldUseMultiWindowTaskSizeStrategy()) {
            updateStackBoundsToMultiWindowTaskSize(activity);
        } else {
            mSourceStackBounds.set(mHomeStackBounds);
@@ -226,7 +255,6 @@ public class ClipAnimationHelper {
                insets.top + fullDp.availableHeightPx - taskHeight);
    }


    public void drawForProgress(TaskThumbnailView ttv, Canvas canvas, float progress) {
        RectF currentRect =  mRectFEvaluator.evaluate(progress, mSourceRect, mTargetRect);
        canvas.translate(mSourceStackBounds.left - mHomeStackBounds.left,
@@ -244,4 +272,12 @@ public class ClipAnimationHelper {
                ttv.getMeasuredHeight() + mSourceWindowClipInsets.bottom * insetProgress,
                ttv.getCornerRadius() * progress);
    }

    public RectF getTargetRect() {
        return mTargetRect;
    }

    public RectF getSourceRect() {
        return mSourceRect;
    }
}
Loading