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