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