AppWindowAnimator.java revision e8069dcfcff15e060fc397b9ed5ea8b915b1cfa7
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.Choreographer;
28import android.view.Display;
29import android.view.SurfaceControl;
30import android.view.WindowManagerPolicy;
31import android.view.animation.Animation;
32import android.view.animation.Transformation;
33
34import java.io.PrintWriter;
35import java.util.ArrayList;
36
37public 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
97    public AppWindowAnimator(final AppWindowToken atoken) {
98        mAppToken = atoken;
99        mService = atoken.service;
100        mAnimator = atoken.mAnimator;
101    }
102
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
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
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
160    public boolean isAnimating() {
161        return animation != null || mAppToken.inPendingTransaction;
162    }
163
164    public void clearThumbnail() {
165        if (thumbnail != null) {
166            thumbnail.destroy();
167            thumbnail = null;
168        }
169        deferThumbnailDestruction = false;
170    }
171
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
192    void updateLayers() {
193        final int windowCount = mAppToken.allAppWindows.size();
194        final int adj = animLayerAdjustment;
195        thumbnailLayer = -1;
196        final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
197        for (int i = 0; i < windowCount; i++) {
198            final WindowState w = mAppToken.allAppWindows.get(i);
199            final WindowStateAnimator winAnimator = w.mWinAnimator;
200            winAnimator.mAnimLayer = w.mLayer + adj;
201            if (winAnimator.mAnimLayer > thumbnailLayer) {
202                thumbnailLayer = winAnimator.mAnimLayer;
203            }
204            if (DEBUG_LAYERS) Slog.v(TAG, "Updating layer " + w + ": " + winAnimator.mAnimLayer);
205            if (w == mService.mInputMethodTarget && !mService.mInputMethodTargetWaitingAnim) {
206                mService.setInputMethodAnimLayerAdjustment(adj);
207            }
208            wallpaperController.setAnimLayerAdjustment(w, adj);
209        }
210    }
211
212    private void stepThumbnailAnimation(long currentTime) {
213        thumbnailTransformation.clear();
214        thumbnailAnimation.getTransformation(currentTime, thumbnailTransformation);
215        thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
216
217        ScreenRotationAnimation screenRotationAnimation =
218                mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
219        final boolean screenAnimation = screenRotationAnimation != null
220                && screenRotationAnimation.isAnimating();
221        if (screenAnimation) {
222            thumbnailTransformation.postCompose(screenRotationAnimation.getEnterTransformation());
223        }
224        // cache often used attributes locally
225        final float tmpFloats[] = mService.mTmpFloats;
226        thumbnailTransformation.getMatrix().getValues(tmpFloats);
227        if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
228                "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
229                + ", " + tmpFloats[Matrix.MTRANS_Y], null);
230        thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
231        if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
232                "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
233                + " layer=" + thumbnailLayer
234                + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
235                + "," + tmpFloats[Matrix.MSKEW_Y]
236                + "][" + tmpFloats[Matrix.MSKEW_X]
237                + "," + tmpFloats[Matrix.MSCALE_Y] + "]", null);
238        thumbnail.setAlpha(thumbnailTransformation.getAlpha());
239        if (thumbnailForceAboveLayer > 0) {
240            thumbnail.setLayer(thumbnailForceAboveLayer + 1);
241        } else {
242            // The thumbnail is layered below the window immediately above this
243            // token's anim layer.
244            thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
245                    - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
246        }
247        thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
248                tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
249    }
250
251    private boolean stepAnimation(long currentTime) {
252        if (animation == null) {
253            return false;
254        }
255        transformation.clear();
256        boolean hasMoreFrames = animation.getTransformation(currentTime, transformation);
257        if (!hasMoreFrames) {
258            if (deferThumbnailDestruction && !deferFinalFrameCleanup) {
259                // We are deferring the thumbnail destruction, so extend the animation for one more
260                // (dummy) frame before we clean up
261                deferFinalFrameCleanup = true;
262                hasMoreFrames = true;
263            } else {
264                if (false && DEBUG_ANIM) Slog.v(TAG,
265                        "Stepped animation in " + mAppToken + ": more=" + hasMoreFrames +
266                        ", xform=" + transformation);
267                deferFinalFrameCleanup = false;
268                animation = null;
269                clearThumbnail();
270                if (DEBUG_ANIM) Slog.v(TAG,
271                        "Finished animation in " + mAppToken + " @ " + currentTime);
272            }
273        }
274        hasTransformation = hasMoreFrames;
275        return hasMoreFrames;
276    }
277
278    private long getStartTimeCorrection() {
279        if (mSkipFirstFrame) {
280
281            // If the transition is an animation in which the first frame doesn't change the screen
282            // contents at all, we can just skip it and start at the second frame. So we shift the
283            // start time of the animation forward by minus the frame duration.
284            return -Choreographer.getInstance().getFrameIntervalNanos() / TimeUtils.NANOS_PER_MS;
285        } else {
286            return 0;
287        }
288    }
289
290    // This must be called while inside a transaction.
291    boolean stepAnimationLocked(long currentTime, final int displayId) {
292        if (mService.okToDisplay()) {
293            // We will run animations as long as the display isn't frozen.
294
295            if (animation == sDummyAnimation) {
296                // This guy is going to animate, but not yet.  For now count
297                // it as not animating for purposes of scheduling transactions;
298                // when it is really time to animate, this will be set to
299                // a real animation and the next call will execute normally.
300                return false;
301            }
302
303            if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed)
304                    && animation != null) {
305                if (!animating) {
306                    if (DEBUG_ANIM) Slog.v(TAG,
307                        "Starting animation in " + mAppToken +
308                        " @ " + currentTime + " scale="
309                        + mService.getTransitionAnimationScaleLocked()
310                        + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating);
311                    long correction = getStartTimeCorrection();
312                    animation.setStartTime(currentTime + correction);
313                    animating = true;
314                    if (thumbnail != null) {
315                        thumbnail.show();
316                        thumbnailAnimation.setStartTime(currentTime + correction);
317                    }
318                    mSkipFirstFrame = false;
319                }
320                if (stepAnimation(currentTime)) {
321                    // animation isn't over, step any thumbnail and that's
322                    // it for now.
323                    if (thumbnail != null) {
324                        stepThumbnailAnimation(currentTime);
325                    }
326                    return true;
327                }
328            }
329        } else if (animation != null) {
330            // If the display is frozen, and there is a pending animation,
331            // clear it and make sure we run the cleanup code.
332            animating = true;
333            animation = null;
334        }
335
336        hasTransformation = false;
337
338        if (!animating && animation == null) {
339            return false;
340        }
341
342        mAnimator.setAppLayoutChanges(this, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
343                "AppWindowToken", displayId);
344
345        clearAnimation();
346        animating = false;
347        if (animLayerAdjustment != 0) {
348            animLayerAdjustment = 0;
349            updateLayers();
350        }
351        if (mService.mInputMethodTarget != null
352                && mService.mInputMethodTarget.mAppToken == mAppToken) {
353            mService.moveInputMethodWindowsIfNeededLocked(true);
354        }
355
356        if (DEBUG_ANIM) Slog.v(TAG,
357                "Animation done in " + mAppToken
358                + ": reportedVisible=" + mAppToken.reportedVisible);
359
360        transformation.clear();
361
362        final int numAllAppWinAnimators = mAllAppWinAnimators.size();
363        for (int i = 0; i < numAllAppWinAnimators; i++) {
364            mAllAppWinAnimators.get(i).finishExit();
365        }
366        mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token);
367        return false;
368    }
369
370    boolean showAllWindowsLocked() {
371        boolean isAnimating = false;
372        final int NW = mAllAppWinAnimators.size();
373        for (int i=0; i<NW; i++) {
374            WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i);
375            if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
376                    "performing show on: " + winAnimator);
377            winAnimator.performShowLocked();
378            isAnimating |= winAnimator.isAnimating();
379        }
380        return isAnimating;
381    }
382
383    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
384        pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
385        pw.print(prefix); pw.print("mAnimator="); pw.println(mAnimator);
386        pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen);
387                pw.print(" allDrawn="); pw.print(allDrawn);
388                pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment);
389        if (lastFreezeDuration != 0) {
390            pw.print(prefix); pw.print("lastFreezeDuration=");
391                    TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println();
392        }
393        if (animating || animation != null) {
394            pw.print(prefix); pw.print("animating="); pw.println(animating);
395            pw.print(prefix); pw.print("animation="); pw.println(animation);
396        }
397        if (hasTransformation) {
398            pw.print(prefix); pw.print("XForm: ");
399                    transformation.printShortString(pw);
400                    pw.println();
401        }
402        if (thumbnail != null) {
403            pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
404                    pw.print(" x="); pw.print(thumbnailX);
405                    pw.print(" y="); pw.print(thumbnailY);
406                    pw.print(" layer="); pw.println(thumbnailLayer);
407            pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
408            pw.print(prefix); pw.print("thumbnailTransformation=");
409                    pw.println(thumbnailTransformation.toShortString());
410        }
411        for (int i=0; i<mAllAppWinAnimators.size(); i++) {
412            WindowStateAnimator wanim = mAllAppWinAnimators.get(i);
413            pw.print(prefix); pw.print("App Win Anim #"); pw.print(i);
414                    pw.print(": "); pw.println(wanim);
415        }
416    }
417
418    // This is an animation that does nothing: it just immediately finishes
419    // itself every time it is called.  It is used as a stub animation in cases
420    // where we want to synchronize multiple things that may be animating.
421    static final class DummyAnimation extends Animation {
422        @Override
423        public boolean getTransformation(long currentTime, Transformation outTransformation) {
424            return false;
425        }
426    }
427
428}
429