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