All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.wm.WindowChangeAnimationSpec Maven / Gradle / Ivy

/*
 * 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.server.wm;

import static com.android.server.wm.AnimationAdapter.STATUS_BAR_TRANSITION_DURATION;
import static com.android.server.wm.AnimationSpecProto.WINDOW;
import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;

import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.SystemClock;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ClipRectAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;
import android.view.animation.TranslateAnimation;

import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;

import java.io.PrintWriter;

/**
 * Animation spec for changing window animations.
 */
public class WindowChangeAnimationSpec implements AnimationSpec {

    private final ThreadLocal mThreadLocalTmps = ThreadLocal.withInitial(TmpValues::new);
    private final boolean mIsAppAnimation;
    private final Rect mStartBounds;
    private final Rect mEndBounds;
    private final Rect mTmpRect = new Rect();

    private Animation mAnimation;
    private final boolean mIsThumbnail;

    static final int ANIMATION_DURATION = AppTransition.DEFAULT_APP_TRANSITION_DURATION;

    public WindowChangeAnimationSpec(Rect startBounds, Rect endBounds, DisplayInfo displayInfo,
            float durationScale, boolean isAppAnimation, boolean isThumbnail) {
        mStartBounds = new Rect(startBounds);
        mEndBounds = new Rect(endBounds);
        mIsAppAnimation = isAppAnimation;
        mIsThumbnail = isThumbnail;
        createBoundsInterpolator((int) (ANIMATION_DURATION * durationScale), displayInfo);
    }

    @Override
    public boolean getShowWallpaper() {
        return false;
    }

    @Override
    public long getDuration() {
        return mAnimation.getDuration();
    }

    /**
     * This animator behaves slightly differently depending on whether the window is growing
     * or shrinking:
     * If growing, it will do a clip-reveal after quicker fade-out/scale of the smaller (old)
     * snapshot.
     * If shrinking, it will do an opposite clip-reveal on the old snapshot followed by a quicker
     * fade-out of the bigger (old) snapshot while simultaneously shrinking the new window into
     * place.
     * @param duration
     * @param displayInfo
     */
    private void createBoundsInterpolator(long duration, DisplayInfo displayInfo) {
        boolean growing = mEndBounds.width() - mStartBounds.width()
                + mEndBounds.height() - mStartBounds.height() >= 0;
        float scalePart = 0.7f;
        long scalePeriod = (long) (duration * scalePart);
        float startScaleX = scalePart * ((float) mStartBounds.width()) / mEndBounds.width()
                + (1.f - scalePart);
        float startScaleY = scalePart * ((float) mStartBounds.height()) / mEndBounds.height()
                + (1.f - scalePart);
        if (mIsThumbnail) {
            AnimationSet animSet = new AnimationSet(true);
            Animation anim = new AlphaAnimation(1.f, 0.f);
            anim.setDuration(scalePeriod);
            if (!growing) {
                anim.setStartOffset(duration - scalePeriod);
            }
            animSet.addAnimation(anim);
            float endScaleX = 1.f / startScaleX;
            float endScaleY = 1.f / startScaleY;
            anim = new ScaleAnimation(endScaleX, endScaleX, endScaleY, endScaleY);
            anim.setDuration(duration);
            animSet.addAnimation(anim);
            mAnimation = animSet;
            mAnimation.initialize(mStartBounds.width(), mStartBounds.height(),
                    mEndBounds.width(), mEndBounds.height());
        } else {
            AnimationSet animSet = new AnimationSet(true);
            final Animation scaleAnim = new ScaleAnimation(startScaleX, 1, startScaleY, 1);
            scaleAnim.setDuration(scalePeriod);
            if (!growing) {
                scaleAnim.setStartOffset(duration - scalePeriod);
            }
            animSet.addAnimation(scaleAnim);
            final Animation translateAnim = new TranslateAnimation(mStartBounds.left,
                    mEndBounds.left, mStartBounds.top, mEndBounds.top);
            translateAnim.setDuration(duration);
            animSet.addAnimation(translateAnim);
            Rect startClip = new Rect(mStartBounds);
            Rect endClip = new Rect(mEndBounds);
            startClip.offsetTo(0, 0);
            endClip.offsetTo(0, 0);
            final Animation clipAnim = new ClipRectAnimation(startClip, endClip);
            clipAnim.setDuration(duration);
            animSet.addAnimation(clipAnim);
            mAnimation = animSet;
            mAnimation.initialize(mStartBounds.width(), mStartBounds.height(),
                    displayInfo.appWidth, displayInfo.appHeight);
        }
    }

    @Override
    public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
        final TmpValues tmp = mThreadLocalTmps.get();
        if (mIsThumbnail) {
            mAnimation.getTransformation(currentPlayTime, tmp.mTransformation);
            t.setMatrix(leash, tmp.mTransformation.getMatrix(), tmp.mFloats);
            t.setAlpha(leash, tmp.mTransformation.getAlpha());
        } else {
            mAnimation.getTransformation(currentPlayTime, tmp.mTransformation);
            final Matrix matrix = tmp.mTransformation.getMatrix();
            t.setMatrix(leash, matrix, tmp.mFloats);

            // The following applies an inverse scale to the clip-rect so that it crops "after" the
            // scale instead of before.
            tmp.mVecs[1] = tmp.mVecs[2] = 0;
            tmp.mVecs[0] = tmp.mVecs[3] = 1;
            matrix.mapVectors(tmp.mVecs);
            tmp.mVecs[0] = 1.f / tmp.mVecs[0];
            tmp.mVecs[3] = 1.f / tmp.mVecs[3];
            final Rect clipRect = tmp.mTransformation.getClipRect();
            mTmpRect.left = (int) (clipRect.left * tmp.mVecs[0] + 0.5f);
            mTmpRect.right = (int) (clipRect.right * tmp.mVecs[0] + 0.5f);
            mTmpRect.top = (int) (clipRect.top * tmp.mVecs[3] + 0.5f);
            mTmpRect.bottom = (int) (clipRect.bottom * tmp.mVecs[3] + 0.5f);
            t.setWindowCrop(leash, mTmpRect);
        }
    }

    @Override
    public long calculateStatusBarTransitionStartTime() {
        long uptime = SystemClock.uptimeMillis();
        return Math.max(uptime, uptime + ((long) (((float) mAnimation.getDuration()) * 0.99f))
                - STATUS_BAR_TRANSITION_DURATION);
    }

    @Override
    public boolean canSkipFirstFrame() {
        return false;
    }

    @Override
    public boolean needsEarlyWakeup() {
        return mIsAppAnimation;
    }

    @Override
    public void dump(PrintWriter pw, String prefix) {
        pw.print(prefix); pw.println(mAnimation.getDuration());
    }

    @Override
    public void dumpDebugInner(ProtoOutputStream proto) {
        final long token = proto.start(WINDOW);
        proto.write(ANIMATION, mAnimation.toString());
        proto.end(token);
    }

    private static class TmpValues {
        final Transformation mTransformation = new Transformation();
        final float[] mFloats = new float[9];
        final float[] mVecs = new float[4];
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy