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