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