1 /*
2  * Copyright (C) 2014 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.WindowManagerService.DEBUG_ANIM;
20 import static com.android.server.wm.WindowManagerService.DEBUG_LAYERS;
21 import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
22 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
23 
24 import android.graphics.Matrix;
25 import android.util.Slog;
26 import android.util.TimeUtils;
27 import android.view.Choreographer;
28 import android.view.Display;
29 import android.view.SurfaceControl;
30 import android.view.WindowManagerPolicy;
31 import android.view.animation.Animation;
32 import android.view.animation.Transformation;
33 
34 import java.io.PrintWriter;
35 import java.util.ArrayList;
36 
37 public class AppWindowAnimator {
38     static final String TAG = "AppWindowAnimator";
39 
40     final AppWindowToken mAppToken;
41     final WindowManagerService mService;
42     final WindowAnimator mAnimator;
43 
44     boolean animating;
45     boolean wasAnimating;
46     Animation animation;
47     boolean hasTransformation;
48     final Transformation transformation = new Transformation();
49 
50     // Have we been asked to have this token keep the screen frozen?
51     // Protect with mAnimator.
52     boolean freezingScreen;
53 
54     /**
55      * How long we last kept the screen frozen.
56      */
57     int lastFreezeDuration;
58 
59     // Offset to the window of all layers in the token, for use by
60     // AppWindowToken animations.
61     int animLayerAdjustment;
62 
63     // Propagated from AppWindowToken.allDrawn, to determine when
64     // the state changes.
65     boolean allDrawn;
66 
67     // Special surface for thumbnail animation.  If deferThumbnailDestruction is enabled, then we
68     // will make sure that the thumbnail is destroyed after the other surface is completed.  This
69     // requires that the duration of the two animations are the same.
70     SurfaceControl thumbnail;
71     int thumbnailTransactionSeq;
72     int thumbnailX;
73     int thumbnailY;
74     int thumbnailLayer;
75     int thumbnailForceAboveLayer;
76     Animation thumbnailAnimation;
77     final Transformation thumbnailTransformation = new Transformation();
78     // This flag indicates that the destruction of the thumbnail surface is synchronized with
79     // another animation, so defer the destruction of this thumbnail surface for a single frame
80     // after the secondary animation completes.
81     boolean deferThumbnailDestruction;
82     // This flag is set if the animator has deferThumbnailDestruction set and has reached the final
83     // frame of animation.  It will extend the animation by one frame and then clean up afterwards.
84     boolean deferFinalFrameCleanup;
85 
86     /** WindowStateAnimator from mAppAnimator.allAppWindows as of last performLayout */
87     ArrayList<WindowStateAnimator> mAllAppWinAnimators = new ArrayList<>();
88 
89     /** True if the current animation was transferred from another AppWindowAnimator.
90      *  See {@link #transferCurrentAnimation}*/
91     boolean usingTransferredAnimation = false;
92 
93     private boolean mSkipFirstFrame = false;
94 
95     static final Animation sDummyAnimation = new DummyAnimation();
96 
AppWindowAnimator(final AppWindowToken atoken)97     public AppWindowAnimator(final AppWindowToken atoken) {
98         mAppToken = atoken;
99         mService = atoken.service;
100         mAnimator = atoken.mAnimator;
101     }
102 
setAnimation(Animation anim, int width, int height, boolean skipFirstFrame)103     public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame) {
104         if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken
105                 + ": " + anim + " wxh=" + width + "x" + height
106                 + " isVisible=" + mAppToken.isVisible());
107         animation = anim;
108         animating = false;
109         if (!anim.isInitialized()) {
110             anim.initialize(width, height, width, height);
111         }
112         anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
113         anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
114         int zorder = anim.getZAdjustment();
115         int adj = 0;
116         if (zorder == Animation.ZORDER_TOP) {
117             adj = TYPE_LAYER_OFFSET;
118         } else if (zorder == Animation.ZORDER_BOTTOM) {
119             adj = -TYPE_LAYER_OFFSET;
120         }
121 
122         if (animLayerAdjustment != adj) {
123             animLayerAdjustment = adj;
124             updateLayers();
125         }
126         // Start out animation gone if window is gone, or visible if window is visible.
127         transformation.clear();
128         transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
129         hasTransformation = true;
130 
131         this.mSkipFirstFrame = skipFirstFrame;
132 
133         if (!mAppToken.appFullscreen) {
134             anim.setBackgroundColor(0);
135         }
136     }
137 
setDummyAnimation()138     public void setDummyAnimation() {
139         if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting dummy animation in " + mAppToken
140                 + " isVisible=" + mAppToken.isVisible());
141         animation = sDummyAnimation;
142         hasTransformation = true;
143         transformation.clear();
144         transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
145     }
146 
clearAnimation()147     public void clearAnimation() {
148         if (animation != null) {
149             animation = null;
150             animating = true;
151         }
152         clearThumbnail();
153         if (mAppToken.deferClearAllDrawn) {
154             mAppToken.allDrawn = false;
155             mAppToken.deferClearAllDrawn = false;
156         }
157         usingTransferredAnimation = false;
158     }
159 
isAnimating()160     public boolean isAnimating() {
161         return animation != null || mAppToken.inPendingTransaction;
162     }
163 
clearThumbnail()164     public void clearThumbnail() {
165         if (thumbnail != null) {
166             thumbnail.destroy();
167             thumbnail = null;
168         }
169         deferThumbnailDestruction = false;
170     }
171 
transferCurrentAnimation( AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator)172     void transferCurrentAnimation(
173             AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator) {
174 
175         if (animation != null) {
176             toAppAnimator.animation = animation;
177             animation = null;
178             toAppAnimator.animating = animating;
179             toAppAnimator.animLayerAdjustment = animLayerAdjustment;
180             animLayerAdjustment = 0;
181             toAppAnimator.updateLayers();
182             updateLayers();
183             toAppAnimator.usingTransferredAnimation = true;
184         }
185         if (transferWinAnimator != null) {
186             mAllAppWinAnimators.remove(transferWinAnimator);
187             toAppAnimator.mAllAppWinAnimators.add(transferWinAnimator);
188             transferWinAnimator.mAppAnimator = toAppAnimator;
189         }
190     }
191 
updateLayers()192     void updateLayers() {
193         final int windowCount = mAppToken.allAppWindows.size();
194         final int adj = animLayerAdjustment;
195         thumbnailLayer = -1;
196         for (int i = 0; i < windowCount; i++) {
197             final WindowState w = mAppToken.allAppWindows.get(i);
198             final WindowStateAnimator winAnimator = w.mWinAnimator;
199             winAnimator.mAnimLayer = w.mLayer + adj;
200             if (winAnimator.mAnimLayer > thumbnailLayer) {
201                 thumbnailLayer = winAnimator.mAnimLayer;
202             }
203             if (DEBUG_LAYERS) Slog.v(TAG, "Updating layer " + w + ": " + winAnimator.mAnimLayer);
204             if (w == mService.mInputMethodTarget && !mService.mInputMethodTargetWaitingAnim) {
205                 mService.setInputMethodAnimLayerAdjustment(adj);
206             }
207             if (w == mService.mWallpaperTarget && mService.mLowerWallpaperTarget == null) {
208                 mService.setWallpaperAnimLayerAdjustmentLocked(adj);
209             }
210         }
211     }
212 
stepThumbnailAnimation(long currentTime)213     private void stepThumbnailAnimation(long currentTime) {
214         thumbnailTransformation.clear();
215         thumbnailAnimation.getTransformation(currentTime, thumbnailTransformation);
216         thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
217 
218         ScreenRotationAnimation screenRotationAnimation =
219                 mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
220         final boolean screenAnimation = screenRotationAnimation != null
221                 && screenRotationAnimation.isAnimating();
222         if (screenAnimation) {
223             thumbnailTransformation.postCompose(screenRotationAnimation.getEnterTransformation());
224         }
225         // cache often used attributes locally
226         final float tmpFloats[] = mService.mTmpFloats;
227         thumbnailTransformation.getMatrix().getValues(tmpFloats);
228         if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
229                 "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
230                 + ", " + tmpFloats[Matrix.MTRANS_Y], null);
231         thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
232         if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
233                 "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
234                 + " layer=" + thumbnailLayer
235                 + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
236                 + "," + tmpFloats[Matrix.MSKEW_Y]
237                 + "][" + tmpFloats[Matrix.MSKEW_X]
238                 + "," + tmpFloats[Matrix.MSCALE_Y] + "]", null);
239         thumbnail.setAlpha(thumbnailTransformation.getAlpha());
240         if (thumbnailForceAboveLayer > 0) {
241             thumbnail.setLayer(thumbnailForceAboveLayer + 1);
242         } else {
243             // The thumbnail is layered below the window immediately above this
244             // token's anim layer.
245             thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
246                     - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
247         }
248         thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
249                 tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
250     }
251 
stepAnimation(long currentTime)252     private boolean stepAnimation(long currentTime) {
253         if (animation == null) {
254             return false;
255         }
256         transformation.clear();
257         boolean hasMoreFrames = animation.getTransformation(currentTime, transformation);
258         if (!hasMoreFrames) {
259             if (deferThumbnailDestruction && !deferFinalFrameCleanup) {
260                 // We are deferring the thumbnail destruction, so extend the animation for one more
261                 // (dummy) frame before we clean up
262                 deferFinalFrameCleanup = true;
263                 hasMoreFrames = true;
264             } else {
265                 if (false && DEBUG_ANIM) Slog.v(TAG,
266                         "Stepped animation in " + mAppToken + ": more=" + hasMoreFrames +
267                         ", xform=" + transformation);
268                 deferFinalFrameCleanup = false;
269                 animation = null;
270                 clearThumbnail();
271                 if (DEBUG_ANIM) Slog.v(TAG,
272                         "Finished animation in " + mAppToken + " @ " + currentTime);
273             }
274         }
275         hasTransformation = hasMoreFrames;
276         return hasMoreFrames;
277     }
278 
getStartTimeCorrection()279     private long getStartTimeCorrection() {
280         if (mSkipFirstFrame) {
281 
282             // If the transition is an animation in which the first frame doesn't change the screen
283             // contents at all, we can just skip it and start at the second frame. So we shift the
284             // start time of the animation forward by minus the frame duration.
285             return -Choreographer.getInstance().getFrameIntervalNanos() / TimeUtils.NANOS_PER_MS;
286         } else {
287             return 0;
288         }
289     }
290 
291     // This must be called while inside a transaction.
stepAnimationLocked(long currentTime, final int displayId)292     boolean stepAnimationLocked(long currentTime, final int displayId) {
293         if (mService.okToDisplay()) {
294             // We will run animations as long as the display isn't frozen.
295 
296             if (animation == sDummyAnimation) {
297                 // This guy is going to animate, but not yet.  For now count
298                 // it as not animating for purposes of scheduling transactions;
299                 // when it is really time to animate, this will be set to
300                 // a real animation and the next call will execute normally.
301                 return false;
302             }
303 
304             if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed)
305                     && animation != null) {
306                 if (!animating) {
307                     if (DEBUG_ANIM) Slog.v(TAG,
308                         "Starting animation in " + mAppToken +
309                         " @ " + currentTime + " scale="
310                         + mService.getTransitionAnimationScaleLocked()
311                         + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating);
312                     long correction = getStartTimeCorrection();
313                     animation.setStartTime(currentTime + correction);
314                     animating = true;
315                     if (thumbnail != null) {
316                         thumbnail.show();
317                         thumbnailAnimation.setStartTime(currentTime + correction);
318                     }
319                     mSkipFirstFrame = false;
320                 }
321                 if (stepAnimation(currentTime)) {
322                     // animation isn't over, step any thumbnail and that's
323                     // it for now.
324                     if (thumbnail != null) {
325                         stepThumbnailAnimation(currentTime);
326                     }
327                     return true;
328                 }
329             }
330         } else if (animation != null) {
331             // If the display is frozen, and there is a pending animation,
332             // clear it and make sure we run the cleanup code.
333             animating = true;
334             animation = null;
335         }
336 
337         hasTransformation = false;
338 
339         if (!animating && animation == null) {
340             return false;
341         }
342 
343         mAnimator.setAppLayoutChanges(this, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
344                 "AppWindowToken", displayId);
345 
346         clearAnimation();
347         animating = false;
348         if (animLayerAdjustment != 0) {
349             animLayerAdjustment = 0;
350             updateLayers();
351         }
352         if (mService.mInputMethodTarget != null
353                 && mService.mInputMethodTarget.mAppToken == mAppToken) {
354             mService.moveInputMethodWindowsIfNeededLocked(true);
355         }
356 
357         if (DEBUG_ANIM) Slog.v(TAG,
358                 "Animation done in " + mAppToken
359                 + ": reportedVisible=" + mAppToken.reportedVisible);
360 
361         transformation.clear();
362 
363         final int numAllAppWinAnimators = mAllAppWinAnimators.size();
364         for (int i = 0; i < numAllAppWinAnimators; i++) {
365             mAllAppWinAnimators.get(i).finishExit();
366         }
367         mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token);
368         return false;
369     }
370 
showAllWindowsLocked()371     boolean showAllWindowsLocked() {
372         boolean isAnimating = false;
373         final int NW = mAllAppWinAnimators.size();
374         for (int i=0; i<NW; i++) {
375             WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i);
376             if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
377                     "performing show on: " + winAnimator);
378             winAnimator.performShowLocked();
379             isAnimating |= winAnimator.isAnimating();
380         }
381         return isAnimating;
382     }
383 
dump(PrintWriter pw, String prefix, boolean dumpAll)384     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
385         pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
386         pw.print(prefix); pw.print("mAnimator="); pw.println(mAnimator);
387         pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen);
388                 pw.print(" allDrawn="); pw.print(allDrawn);
389                 pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment);
390         if (lastFreezeDuration != 0) {
391             pw.print(prefix); pw.print("lastFreezeDuration=");
392                     TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println();
393         }
394         if (animating || animation != null) {
395             pw.print(prefix); pw.print("animating="); pw.println(animating);
396             pw.print(prefix); pw.print("animation="); pw.println(animation);
397         }
398         if (hasTransformation) {
399             pw.print(prefix); pw.print("XForm: ");
400                     transformation.printShortString(pw);
401                     pw.println();
402         }
403         if (thumbnail != null) {
404             pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
405                     pw.print(" x="); pw.print(thumbnailX);
406                     pw.print(" y="); pw.print(thumbnailY);
407                     pw.print(" layer="); pw.println(thumbnailLayer);
408             pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
409             pw.print(prefix); pw.print("thumbnailTransformation=");
410                     pw.println(thumbnailTransformation.toShortString());
411         }
412         for (int i=0; i<mAllAppWinAnimators.size(); i++) {
413             WindowStateAnimator wanim = mAllAppWinAnimators.get(i);
414             pw.print(prefix); pw.print("App Win Anim #"); pw.print(i);
415                     pw.print(": "); pw.println(wanim);
416         }
417     }
418 
419     // This is an animation that does nothing: it just immediately finishes
420     // itself every time it is called.  It is used as a stub animation in cases
421     // where we want to synchronize multiple things that may be animating.
422     static final class DummyAnimation extends Animation {
423         @Override
getTransformation(long currentTime, Transformation outTransformation)424         public boolean getTransformation(long currentTime, Transformation outTransformation) {
425             return false;
426         }
427     }
428 
429 }
430