• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static com.android.server.wm.AnimationAdapter.STATUS_BAR_TRANSITION_DURATION;
20 import static com.android.server.wm.AnimationSpecProto.WINDOW;
21 import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
22 
23 import android.graphics.Matrix;
24 import android.graphics.Rect;
25 import android.os.SystemClock;
26 import android.util.proto.ProtoOutputStream;
27 import android.view.DisplayInfo;
28 import android.view.SurfaceControl;
29 import android.view.SurfaceControl.Transaction;
30 import android.view.animation.AlphaAnimation;
31 import android.view.animation.Animation;
32 import android.view.animation.AnimationSet;
33 import android.view.animation.ClipRectAnimation;
34 import android.view.animation.ScaleAnimation;
35 import android.view.animation.Transformation;
36 import android.view.animation.TranslateAnimation;
37 
38 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
39 
40 import java.io.PrintWriter;
41 
42 /**
43  * Animation spec for changing window animations.
44  */
45 public class WindowChangeAnimationSpec implements AnimationSpec {
46 
47     private final ThreadLocal<TmpValues> mThreadLocalTmps = ThreadLocal.withInitial(TmpValues::new);
48     private final boolean mIsAppAnimation;
49     private final Rect mStartBounds;
50     private final Rect mEndBounds;
51     private final Rect mTmpRect = new Rect();
52 
53     private Animation mAnimation;
54     private final boolean mIsThumbnail;
55 
56     static final int ANIMATION_DURATION = AppTransition.DEFAULT_APP_TRANSITION_DURATION;
57 
WindowChangeAnimationSpec(Rect startBounds, Rect endBounds, DisplayInfo displayInfo, float durationScale, boolean isAppAnimation, boolean isThumbnail)58     public WindowChangeAnimationSpec(Rect startBounds, Rect endBounds, DisplayInfo displayInfo,
59             float durationScale, boolean isAppAnimation, boolean isThumbnail) {
60         mStartBounds = new Rect(startBounds);
61         mEndBounds = new Rect(endBounds);
62         mIsAppAnimation = isAppAnimation;
63         mIsThumbnail = isThumbnail;
64         createBoundsInterpolator((int) (ANIMATION_DURATION * durationScale), displayInfo);
65     }
66 
67     @Override
getShowWallpaper()68     public boolean getShowWallpaper() {
69         return false;
70     }
71 
72     @Override
getDuration()73     public long getDuration() {
74         return mAnimation.getDuration();
75     }
76 
77     /**
78      * This animator behaves slightly differently depending on whether the window is growing
79      * or shrinking:
80      * If growing, it will do a clip-reveal after quicker fade-out/scale of the smaller (old)
81      * snapshot.
82      * If shrinking, it will do an opposite clip-reveal on the old snapshot followed by a quicker
83      * fade-out of the bigger (old) snapshot while simultaneously shrinking the new window into
84      * place.
85      * @param duration
86      * @param displayInfo
87      */
createBoundsInterpolator(long duration, DisplayInfo displayInfo)88     private void createBoundsInterpolator(long duration, DisplayInfo displayInfo) {
89         boolean growing = mEndBounds.width() - mStartBounds.width()
90                 + mEndBounds.height() - mStartBounds.height() >= 0;
91         float scalePart = 0.7f;
92         long scalePeriod = (long) (duration * scalePart);
93         float startScaleX = scalePart * ((float) mStartBounds.width()) / mEndBounds.width()
94                 + (1.f - scalePart);
95         float startScaleY = scalePart * ((float) mStartBounds.height()) / mEndBounds.height()
96                 + (1.f - scalePart);
97         if (mIsThumbnail) {
98             AnimationSet animSet = new AnimationSet(true);
99             Animation anim = new AlphaAnimation(1.f, 0.f);
100             anim.setDuration(scalePeriod);
101             if (!growing) {
102                 anim.setStartOffset(duration - scalePeriod);
103             }
104             animSet.addAnimation(anim);
105             float endScaleX = 1.f / startScaleX;
106             float endScaleY = 1.f / startScaleY;
107             anim = new ScaleAnimation(endScaleX, endScaleX, endScaleY, endScaleY);
108             anim.setDuration(duration);
109             animSet.addAnimation(anim);
110             mAnimation = animSet;
111             mAnimation.initialize(mStartBounds.width(), mStartBounds.height(),
112                     mEndBounds.width(), mEndBounds.height());
113         } else {
114             AnimationSet animSet = new AnimationSet(true);
115             final Animation scaleAnim = new ScaleAnimation(startScaleX, 1, startScaleY, 1);
116             scaleAnim.setDuration(scalePeriod);
117             if (!growing) {
118                 scaleAnim.setStartOffset(duration - scalePeriod);
119             }
120             animSet.addAnimation(scaleAnim);
121             final Animation translateAnim = new TranslateAnimation(mStartBounds.left,
122                     mEndBounds.left, mStartBounds.top, mEndBounds.top);
123             translateAnim.setDuration(duration);
124             animSet.addAnimation(translateAnim);
125             Rect startClip = new Rect(mStartBounds);
126             Rect endClip = new Rect(mEndBounds);
127             startClip.offsetTo(0, 0);
128             endClip.offsetTo(0, 0);
129             final Animation clipAnim = new ClipRectAnimation(startClip, endClip);
130             clipAnim.setDuration(duration);
131             animSet.addAnimation(clipAnim);
132             mAnimation = animSet;
133             mAnimation.initialize(mStartBounds.width(), mStartBounds.height(),
134                     displayInfo.appWidth, displayInfo.appHeight);
135         }
136     }
137 
138     @Override
apply(Transaction t, SurfaceControl leash, long currentPlayTime)139     public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
140         final TmpValues tmp = mThreadLocalTmps.get();
141         if (mIsThumbnail) {
142             mAnimation.getTransformation(currentPlayTime, tmp.mTransformation);
143             t.setMatrix(leash, tmp.mTransformation.getMatrix(), tmp.mFloats);
144             t.setAlpha(leash, tmp.mTransformation.getAlpha());
145         } else {
146             mAnimation.getTransformation(currentPlayTime, tmp.mTransformation);
147             final Matrix matrix = tmp.mTransformation.getMatrix();
148             t.setMatrix(leash, matrix, tmp.mFloats);
149 
150             // The following applies an inverse scale to the clip-rect so that it crops "after" the
151             // scale instead of before.
152             tmp.mVecs[1] = tmp.mVecs[2] = 0;
153             tmp.mVecs[0] = tmp.mVecs[3] = 1;
154             matrix.mapVectors(tmp.mVecs);
155             tmp.mVecs[0] = 1.f / tmp.mVecs[0];
156             tmp.mVecs[3] = 1.f / tmp.mVecs[3];
157             final Rect clipRect = tmp.mTransformation.getClipRect();
158             mTmpRect.left = (int) (clipRect.left * tmp.mVecs[0] + 0.5f);
159             mTmpRect.right = (int) (clipRect.right * tmp.mVecs[0] + 0.5f);
160             mTmpRect.top = (int) (clipRect.top * tmp.mVecs[3] + 0.5f);
161             mTmpRect.bottom = (int) (clipRect.bottom * tmp.mVecs[3] + 0.5f);
162             t.setWindowCrop(leash, mTmpRect);
163         }
164     }
165 
166     @Override
calculateStatusBarTransitionStartTime()167     public long calculateStatusBarTransitionStartTime() {
168         long uptime = SystemClock.uptimeMillis();
169         return Math.max(uptime, uptime + ((long) (((float) mAnimation.getDuration()) * 0.99f))
170                 - STATUS_BAR_TRANSITION_DURATION);
171     }
172 
173     @Override
canSkipFirstFrame()174     public boolean canSkipFirstFrame() {
175         return false;
176     }
177 
178     @Override
needsEarlyWakeup()179     public boolean needsEarlyWakeup() {
180         return mIsAppAnimation;
181     }
182 
183     @Override
dump(PrintWriter pw, String prefix)184     public void dump(PrintWriter pw, String prefix) {
185         pw.print(prefix); pw.println(mAnimation.getDuration());
186     }
187 
188     @Override
dumpDebugInner(ProtoOutputStream proto)189     public void dumpDebugInner(ProtoOutputStream proto) {
190         final long token = proto.start(WINDOW);
191         proto.write(ANIMATION, mAnimation.toString());
192         proto.end(token);
193     }
194 
195     private static class TmpValues {
196         final Transformation mTransformation = new Transformation();
197         final float[] mFloats = new float[9];
198         final float[] mVecs = new float[4];
199     }
200 }
201