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

Commit 36548847 authored by Jorim Jaggi's avatar Jorim Jaggi Committed by Android (Google) Code Review
Browse files

Merge "Fix clip reveal animation in docked window case" into nyc-dev

parents ed8f6cff 8fe7e0a8
Loading
Loading
Loading
Loading
+98 −40
Original line number Diff line number Diff line
@@ -46,7 +46,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Debug;
@@ -64,8 +63,6 @@ import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
import android.view.animation.ClipRectAnimation;
import android.view.animation.ClipRectLRAnimation;
import android.view.animation.ClipRectTBAnimation;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.view.animation.ScaleAnimation;
@@ -74,10 +71,11 @@ import android.view.animation.TranslateAnimation;
import com.android.internal.util.DumpUtils.Dump;
import com.android.server.AttributeCache;
import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.animation.ClipRectLRAnimation;
import com.android.server.wm.animation.ClipRectTBAnimation;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@@ -136,6 +134,16 @@ public class AppTransition implements Dump {
    private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f;

    static final int DEFAULT_APP_TRANSITION_DURATION = 336;

    /** Interpolator to be used for animations that respond directly to a touch */
    static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
            new PathInterpolator(0.3f, 0f, 0.1f, 1f);

    /**
     * Maximum duration for the clip reveal animation. This is used when there is a lot of movement
     * involved, to make it more understandable.
     */
    private static final int MAX_CLIP_REVEAL_TRANSITION_DURATION = 420;
    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 336;
    private static final int THUMBNAIL_APP_TRANSITION_ALPHA_DURATION = 336;
    private static final long APP_TRANSITION_TIMEOUT_MS = 5000;
@@ -200,13 +208,10 @@ public class AppTransition implements Dump {
    private final Interpolator mFastOutLinearInInterpolator;
    private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);

    /** Interpolator to be used for animations that respond directly to a touch */
    private final Interpolator mTouchResponseInterpolator =
            new PathInterpolator(0.3f, 0f, 0.1f, 1f);

    private final int mClipRevealTranslationY;

    private int mCurrentUserId = 0;
    private long mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;

    private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
    private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
@@ -636,50 +641,101 @@ public class AppTransition implements Dump {
                bitmap, new Rect(left, top, left + width, top + height));
    }

    private Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame) {
    long getLastClipRevealTransitionDuration() {
        return mLastClipRevealTransitionDuration;
    }

    /**
     * Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that
     * the start rect is outside of the target rect, and there is a lot of movement going on.
     *
     * @param cutOff whether the start rect was not fully contained by the end rect
     * @param translationX the total translation the surface moves in x direction
     * @param translationY the total translation the surfaces moves in y direction
     * @param displayFrame our display frame
     *
     * @return the duration of the clip reveal animation, in milliseconds
     */
    private long calculateClipRevealTransitionDuration(boolean cutOff, float translationX,
            float translationY, Rect displayFrame) {
        if (!cutOff) {
            return DEFAULT_APP_TRANSITION_DURATION;
        }
        final float fraction = Math.max(Math.abs(translationX) / displayFrame.width(),
                Math.abs(translationY) / displayFrame.height());
        return (long) (DEFAULT_APP_TRANSITION_DURATION + fraction *
                (MAX_CLIP_REVEAL_TRANSITION_DURATION - DEFAULT_APP_TRANSITION_DURATION));
    }

    private Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame,
            Rect displayFrame) {
        final Animation anim;
        if (enter) {
            // Reveal will expand and move faster in horizontal direction

            final int appWidth = appFrame.width();
            final int appHeight = appFrame.height();

            // mTmpRect will contain an area around the launcher icon that was pressed. We will
            // clip reveal from that area in the final area of the app.
            getDefaultNextAppTransitionStartRect(mTmpRect);

            float t = 0f;
            if (appHeight > 0) {
                t = (float) mTmpRect.left / appHeight;
                t = (float) mTmpRect.top / displayFrame.height();
            }
            int translationY = mClipRevealTranslationY + (int)(appHeight / 7f * t);

            int translationY = mClipRevealTranslationY + (int)(displayFrame.height() / 7f * t);
            int translationX = 0;
            int translationYCorrection = translationY;
            int centerX = mTmpRect.centerX();
            int centerY = mTmpRect.centerY();
            int halfWidth = mTmpRect.width() / 2;
            int halfHeight = mTmpRect.height() / 2;
            int clipStartX = centerX - halfWidth - appFrame.left;
            int clipStartY = centerY - halfHeight - appFrame.top;
            boolean cutOff = false;

            // If the starting rectangle is fully or partially outside of the target rectangle, we
            // need to start the clipping at the edge and then achieve the rest with translation
            // and extending the clip rect from that edge.
            if (appFrame.top > centerY - halfHeight) {
                translationY = (centerY - halfHeight) - appFrame.top;
                translationYCorrection = 0;
                clipStartY = 0;
                cutOff = true;
            }
            if (appFrame.left > centerX - halfWidth) {
                translationX = (centerX - halfWidth) - appFrame.left;
                clipStartX = 0;
                cutOff = true;
            }
            if (appFrame.right < centerX + halfWidth) {
                translationX = (centerX + halfWidth) - appFrame.right;
                clipStartX = appWidth - mTmpRect.width();
                cutOff = true;
            }
            final long duration = calculateClipRevealTransitionDuration(cutOff, translationX,
                    translationY, displayFrame);

            // Clip third of the from size of launch icon, expand to full width/height
            Animation clipAnimLR = new ClipRectLRAnimation(
                    centerX - halfWidth, centerX + halfWidth, 0, appWidth);
                    clipStartX, clipStartX + mTmpRect.width(), 0, appWidth);
            clipAnimLR.setInterpolator(mClipHorizontalInterpolator);
            clipAnimLR.setDuration((long) (DEFAULT_APP_TRANSITION_DURATION / 2.5f));

            Animation clipAnimTB = new ClipRectTBAnimation(centerY - halfHeight - translationY,
                    centerY + halfHeight/ 2 - translationY, 0, appHeight);
            clipAnimTB.setInterpolator(mTouchResponseInterpolator);
            clipAnimTB.setDuration(DEFAULT_APP_TRANSITION_DURATION);

            // We might be animating entrance of a docked task, so we need the translate to account
            // for the app frame in which the window will reside. Every other calculation here
            // is performed as if the window started at 0,0.
            translationY -= appFrame.top;
            TranslateAnimation translate = new TranslateAnimation(-appFrame.left, 0, translationY,
                    0);
            translate.setInterpolator(mLinearOutSlowInInterpolator);
            translate.setDuration(DEFAULT_APP_TRANSITION_DURATION);
            clipAnimLR.setDuration((long) (duration / 2.5f));

            TranslateAnimation translate = new TranslateAnimation(translationX, 0, translationY, 0);
            translate.setInterpolator(cutOff ? TOUCH_RESPONSE_INTERPOLATOR
                    : mLinearOutSlowInInterpolator);
            translate.setDuration(duration);

            Animation clipAnimTB = new ClipRectTBAnimation(
                    clipStartY, clipStartY + mTmpRect.height(),
                    0, appHeight,
                    translationYCorrection, 0,
                    mLinearOutSlowInInterpolator);
            clipAnimTB.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
            clipAnimTB.setDuration(duration);

            // Quick fade-in from icon to app window
            final int alphaDuration = DEFAULT_APP_TRANSITION_DURATION / 4;
            final long alphaDuration = duration / 4;
            AlphaAnimation alpha = new AlphaAnimation(0.5f, 1);
            alpha.setDuration(alphaDuration);
            alpha.setInterpolator(mLinearOutSlowInInterpolator);
@@ -692,6 +748,7 @@ public class AppTransition implements Dump {
            set.setZAdjustment(Animation.ZORDER_TOP);
            set.initialize(appWidth, appHeight, appWidth, appHeight);
            anim = set;
            mLastClipRevealTransitionDuration = duration;
        } else {
            final long duration;
            switch (transit) {
@@ -798,7 +855,7 @@ public class AppTransition implements Dump {
            // Animation up from the thumbnail to the full screen
            Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW,
                    mTmpRect.left + (thumbWidth / 2f), mTmpRect.top + (thumbHeight / 2f));
            scale.setInterpolator(mTouchResponseInterpolator);
            scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
            Animation alpha = new AlphaAnimation(1f, 0f);
            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
@@ -806,7 +863,7 @@ public class AppTransition implements Dump {
            final float toX = appRect.left + appRect.width() / 2 -
                    (mTmpRect.left + thumbWidth / 2);
            Animation translate = new TranslateAnimation(0, toX, 0, toY);
            translate.setInterpolator(mTouchResponseInterpolator);
            translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);

            // This AnimationSet uses the Interpolators assigned above.
@@ -819,7 +876,7 @@ public class AppTransition implements Dump {
            // Animation down from the full screen to the thumbnail
            Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f,
                    mTmpRect.left + (thumbWidth / 2f), mTmpRect.top + (thumbHeight / 2f));
            scale.setInterpolator(mTouchResponseInterpolator);
            scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
            Animation alpha = new AlphaAnimation(0f, 1f);
            alpha.setInterpolator(mThumbnailFadeInInterpolator);
@@ -827,7 +884,7 @@ public class AppTransition implements Dump {
            final float toX = appRect.left + appRect.width() / 2 -
                    (mTmpRect.left + thumbWidth / 2);
            Animation translate = new TranslateAnimation(toX, 0, toY, 0);
            translate.setInterpolator(mTouchResponseInterpolator);
            translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);

            // This AnimationSet uses the Interpolators assigned above.
@@ -839,7 +896,7 @@ public class AppTransition implements Dump {

        }
        return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
                mTouchResponseInterpolator);
                TOUCH_RESPONSE_INTERPOLATOR);
    }

    /**
@@ -971,7 +1028,7 @@ public class AppTransition implements Dump {
        int duration = Math.max(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION,
                THUMBNAIL_APP_TRANSITION_DURATION);
        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
                mTouchResponseInterpolator);
                TOUCH_RESPONSE_INTERPOLATOR);
    }

    private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame,
@@ -1223,8 +1280,9 @@ public class AppTransition implements Dump {
     *                      bigger.
     */
    Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
            int orientation, Rect frame, Rect insets, @Nullable Rect surfaceInsets,
            boolean isVoiceInteraction, boolean freeform, int taskId) {
            int orientation, Rect frame, Rect displayFrame, Rect insets,
            @Nullable Rect surfaceInsets, boolean isVoiceInteraction, boolean freeform,
            int taskId) {
        Animation a;
        if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
                || transit == TRANSIT_TASK_OPEN
@@ -1269,7 +1327,7 @@ public class AppTransition implements Dump {
                    + " transit=" + appTransitionToString(transit)
                    + " Callers=" + Debug.getCallers(3));
        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
            a = createClipRevealAnimationLocked(transit, enter, frame);
            a = createClipRevealAnimationLocked(transit, enter, frame, displayFrame);
            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
                    "applyAnimation:"
                            + " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
+17 −6
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.view.IDockedStackListener;
import android.view.SurfaceControl;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;

import com.android.server.wm.DimLayer.DimLayerUser;

@@ -39,6 +40,8 @@ import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

@@ -49,8 +52,6 @@ public class DockedStackDividerController implements DimLayerUser {

    private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;

    private static final long MINIMIZED_DOCK_ANIMATION_DURATION = 400;

    private final WindowManagerService mService;
    private final DisplayContent mDisplayContent;
    private final int mDividerWindowWidth;
@@ -71,6 +72,7 @@ public class DockedStackDividerController implements DimLayerUser {
    private long mAnimationStartTime;
    private float mAnimationStart;
    private float mAnimationTarget;
    private long mAnimationDuration;
    private final Interpolator mMinimizedDockInterpolator;

    DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
@@ -331,6 +333,10 @@ public class DockedStackDividerController implements DimLayerUser {
        notifyDockedStackMinimizedChanged(minimized, 0);
    }

    private boolean isAnimationMaximizing() {
        return mAnimationTarget == 0f;
    }

    public boolean animate(long now) {
        if (!mAnimating) {
            return false;
@@ -339,12 +345,17 @@ public class DockedStackDividerController implements DimLayerUser {
        if (!mAnimationStarted) {
            mAnimationStarted = true;
            mAnimationStartTime = now;
            final long transitionDuration = isAnimationMaximizing()
                    ? mService.mAppTransition.getLastClipRevealTransitionDuration()
                    : DEFAULT_APP_TRANSITION_DURATION;
            mAnimationDuration = (long)
                    (transitionDuration * mService.getTransitionAnimationScaleLocked());
            notifyDockedStackMinimizedChanged(mMinimizedDock,
                    MINIMIZED_DOCK_ANIMATION_DURATION);
                    mAnimationDuration);
        }
        float t = Math.min(1f, (float) (now - mAnimationStartTime)
                / MINIMIZED_DOCK_ANIMATION_DURATION);
        t = mMinimizedDockInterpolator.getInterpolation(t);
        float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
        t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
                .getInterpolation(t);
        final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
        if (stack != null) {
            final float amount = t * mAnimationTarget + (1 - t) * mAnimationStart;
+4 −2
Original line number Diff line number Diff line
@@ -2968,6 +2968,8 @@ public class WindowManagerService extends IWindowManager.Stub
            // Determine the visible rect to calculate the thumbnail clip
            final WindowState win = atoken.findMainWindow();
            final Rect frame = new Rect(0, 0, width, height);
            final Rect displayFrame = new Rect(0, 0,
                    displayInfo.logicalWidth, displayInfo.logicalHeight);
            final Rect insets = new Rect();
            Rect surfaceInsets = null;
            final boolean freeform = win != null && win.inFreeformWorkspace();
@@ -2995,8 +2997,8 @@ public class WindowManagerService extends IWindowManager.Stub
                    + " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter
                    + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets);
            Animation a = mAppTransition.loadAnimation(lp, transit, enter,
                    mCurConfiguration.orientation, frame, insets, surfaceInsets, isVoiceInteraction,
                    freeform, atoken.mTask.mTaskId);
                    mCurConfiguration.orientation, frame, displayFrame, insets, surfaceInsets,
                    isVoiceInteraction, freeform, atoken.mTask.mTaskId);
            if (a != null) {
                if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + atoken);
                final int containingWidth = frame.width();
+5 −3
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 * Copyright (C) 2016 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.
@@ -11,12 +11,14 @@
 * 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.
 * limitations under the License
 */

package android.view.animation;
package com.android.server.wm.animation;

import android.graphics.Rect;
import android.view.animation.ClipRectAnimation;
import android.view.animation.Transformation;

/**
 * Special case of ClipRectAnimation that animates only the left/right
+86 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 * Copyright (C) 2016 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.
@@ -11,27 +11,60 @@
 * 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.
 * limitations under the License
 */

package android.view.animation;
package com.android.server.wm.animation;

import android.graphics.Rect;
import android.view.animation.ClipRectAnimation;
import android.view.animation.Interpolator;
import android.view.animation.Transformation;
import android.view.animation.TranslateAnimation;

/**
 * Special case of ClipRectAnimation that animates only the top/bottom
 * dimensions of the clip, picking up the other dimensions from whatever is
 * set on the transform already.
 *
 * @hide
 * set on the transform already. In addition to that, information about a vertical translation
 * animation can be specified so this animation simulates as the clip would be applied after instead
 * of before applying the translation.
 */
public class ClipRectTBAnimation extends ClipRectAnimation {

    private final int mFromTranslateY;
    private final int mToTranslateY;
    private final Interpolator mTranslateInterpolator;
    private float mNormalizedTime;

    /**
     * Constructor. Passes in 0 for Left/Right parameters of ClipRectAnimation
     */
    public ClipRectTBAnimation(int fromT, int fromB, int toT, int toB) {
    public ClipRectTBAnimation(int fromT, int fromB, int toT, int toB,
            int fromTranslateY, int toTranslateY, Interpolator translateInterpolator) {
        super(0, fromT, 0, fromB, 0, toT, 0, toB);
        mFromTranslateY = fromTranslateY;
        mToTranslateY = toTranslateY;
        mTranslateInterpolator = translateInterpolator;
    }

    @Override
    public boolean getTransformation(long currentTime, Transformation outTransformation) {

        // Hack: Because translation animation has a different interpolator, we need to duplicate
        // code from Animation here and use it to calculate/store the uninterpolated normalized
        // time.
        final long startOffset = getStartOffset();
        final long duration = getDuration();
        float normalizedTime;
        if (duration != 0) {
            normalizedTime = ((float) (currentTime - (getStartTime() + startOffset))) /
                    (float) duration;
        } else {
            // time is a step-change with a zero duration
            normalizedTime = currentTime < getStartTime() ? 0.0f : 1.0f;
        }
        mNormalizedTime = normalizedTime;
        return super.getTransformation(currentTime, outTransformation);
    }

    /**
@@ -40,10 +73,14 @@ public class ClipRectTBAnimation extends ClipRectAnimation {
     */
    @Override
    protected void applyTransformation(float it, Transformation tr) {
        float translationT = mTranslateInterpolator.getInterpolation(mNormalizedTime);
        int translation =
                (int) (mFromTranslateY + (mToTranslateY - mFromTranslateY) * translationT);
        Rect oldClipRect = tr.getClipRect();
        tr.setClipRect(oldClipRect.left, mFromRect.top + (int) ((mToRect.top - mFromRect.top) * it),
        tr.setClipRect(oldClipRect.left,
                mFromRect.top - translation + (int) ((mToRect.top - mFromRect.top) * it),
                oldClipRect.right,
                mFromRect.bottom + (int) ((mToRect.bottom - mFromRect.bottom) * it));
                mFromRect.bottom - translation + (int) ((mToRect.bottom - mFromRect.bottom) * it));
    }

}
Loading