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    // 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
118    public AppWindowAnimator(final AppWindowToken atoken, WindowManagerService service) {
119        mAppToken = atoken;
120        mService = service;
121        mAnimator = mService.mAnimator;
122    }
123
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.hasContentToDisplay() ? 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
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.hasContentToDisplay() ? 1 : 0);
176    }
177
178    void setNullAnimation() {
179        animation = null;
180        usingTransferredAnimation = false;
181    }
182
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
197    public boolean isAnimating() {
198        return animation != null || mAppToken.inPendingTransaction;
199    }
200
201    public int getTransit() {
202        return mTransit;
203    }
204
205    int getTransitFlags() {
206        return mTransitFlags;
207    }
208
209    public void clearThumbnail() {
210        if (thumbnail != null) {
211            thumbnail.hide();
212            mService.mWindowPlacerLocked.destroyAfterTransaction(thumbnail);
213            thumbnail = null;
214        }
215        deferThumbnailDestruction = false;
216    }
217
218    int getStackClip() {
219        return mStackClip;
220    }
221
222    void transferCurrentAnimation(
223            AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator) {
224
225        if (animation != null) {
226            toAppAnimator.animation = animation;
227            toAppAnimator.animating = animating;
228            toAppAnimator.animLayerAdjustment = animLayerAdjustment;
229            setNullAnimation();
230            animLayerAdjustment = 0;
231            toAppAnimator.updateLayers();
232            updateLayers();
233            toAppAnimator.usingTransferredAnimation = true;
234            toAppAnimator.mTransit = mTransit;
235        }
236        if (transferWinAnimator != null) {
237            mAllAppWinAnimators.remove(transferWinAnimator);
238            toAppAnimator.mAllAppWinAnimators.add(transferWinAnimator);
239            toAppAnimator.hasTransformation = transferWinAnimator.mAppAnimator.hasTransformation;
240            if (toAppAnimator.hasTransformation) {
241                toAppAnimator.transformation.set(transferWinAnimator.mAppAnimator.transformation);
242            } else {
243                toAppAnimator.transformation.clear();
244            }
245            transferWinAnimator.mAppAnimator = toAppAnimator;
246        }
247    }
248
249    private void updateLayers() {
250        mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
251        thumbnailLayer = mAppToken.getHighestAnimLayer();
252    }
253
254    private void stepThumbnailAnimation(long currentTime) {
255        thumbnailTransformation.clear();
256        final long animationFrameTime = getAnimationFrameTime(thumbnailAnimation, currentTime);
257        thumbnailAnimation.getTransformation(animationFrameTime, thumbnailTransformation);
258
259        ScreenRotationAnimation screenRotationAnimation =
260                mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
261        final boolean screenAnimation = screenRotationAnimation != null
262                && screenRotationAnimation.isAnimating();
263        if (screenAnimation) {
264            thumbnailTransformation.postCompose(screenRotationAnimation.getEnterTransformation());
265        }
266        // cache often used attributes locally
267        final float tmpFloats[] = mService.mTmpFloats;
268        thumbnailTransformation.getMatrix().getValues(tmpFloats);
269        if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
270                "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
271                + ", " + tmpFloats[Matrix.MTRANS_Y]);
272        thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
273        if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
274                "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
275                + " layer=" + thumbnailLayer
276                + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
277                + "," + tmpFloats[Matrix.MSKEW_Y]
278                + "][" + tmpFloats[Matrix.MSKEW_X]
279                + "," + tmpFloats[Matrix.MSCALE_Y] + "]");
280        thumbnail.setAlpha(thumbnailTransformation.getAlpha());
281        if (thumbnailForceAboveLayer > 0) {
282            thumbnail.setLayer(thumbnailForceAboveLayer + 1);
283        } else {
284            // The thumbnail is layered below the window immediately above this
285            // token's anim layer.
286            thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
287                    - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
288        }
289        thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
290                tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
291        thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
292    }
293
294    /**
295     * Sometimes we need to synchronize the first frame of animation with some external event, e.g.
296     * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
297     * and keep producing the first frame of the animation.
298     */
299    private long getAnimationFrameTime(Animation animation, long currentTime) {
300        if (mProlongAnimation == PROLONG_ANIMATION_AT_START) {
301            animation.setStartTime(currentTime);
302            return currentTime + 1;
303        }
304        return currentTime;
305    }
306
307    private boolean stepAnimation(long currentTime) {
308        if (animation == null) {
309            return false;
310        }
311        transformation.clear();
312        final long animationFrameTime = getAnimationFrameTime(animation, currentTime);
313        boolean hasMoreFrames = animation.getTransformation(animationFrameTime, transformation);
314        if (!hasMoreFrames) {
315            if (deferThumbnailDestruction && !deferFinalFrameCleanup) {
316                // We are deferring the thumbnail destruction, so extend the animation for one more
317                // (dummy) frame before we clean up
318                deferFinalFrameCleanup = true;
319                hasMoreFrames = true;
320            } else {
321                if (false && DEBUG_ANIM) Slog.v(TAG,
322                        "Stepped animation in " + mAppToken + ": more=" + hasMoreFrames +
323                        ", xform=" + transformation + ", mProlongAnimation=" + mProlongAnimation);
324                deferFinalFrameCleanup = false;
325                if (mProlongAnimation == PROLONG_ANIMATION_AT_END) {
326                    hasMoreFrames = true;
327                } else {
328                    setNullAnimation();
329                    clearThumbnail();
330                    if (DEBUG_ANIM) Slog.v(TAG, "Finished animation in " + mAppToken + " @ "
331                            + currentTime);
332                }
333            }
334        }
335        hasTransformation = hasMoreFrames;
336        return hasMoreFrames;
337    }
338
339    private long getStartTimeCorrection() {
340        if (mSkipFirstFrame) {
341
342            // If the transition is an animation in which the first frame doesn't change the screen
343            // contents at all, we can just skip it and start at the second frame. So we shift the
344            // start time of the animation forward by minus the frame duration.
345            return -Choreographer.getInstance().getFrameIntervalNanos() / TimeUtils.NANOS_PER_MS;
346        } else {
347            return 0;
348        }
349    }
350
351    // This must be called while inside a transaction.
352    boolean stepAnimationLocked(long currentTime) {
353        if (mService.okToDisplay()) {
354            // We will run animations as long as the display isn't frozen.
355
356            if (animation == sDummyAnimation) {
357                // This guy is going to animate, but not yet.  For now count
358                // it as not animating for purposes of scheduling transactions;
359                // when it is really time to animate, this will be set to
360                // a real animation and the next call will execute normally.
361                return false;
362            }
363
364            if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed)
365                    && animation != null) {
366                if (!animating) {
367                    if (DEBUG_ANIM) Slog.v(TAG,
368                        "Starting animation in " + mAppToken +
369                        " @ " + currentTime + " scale="
370                        + mService.getTransitionAnimationScaleLocked()
371                        + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating);
372                    long correction = getStartTimeCorrection();
373                    animation.setStartTime(currentTime + correction);
374                    animating = true;
375                    if (thumbnail != null) {
376                        thumbnail.show();
377                        thumbnailAnimation.setStartTime(currentTime + correction);
378                    }
379                    mSkipFirstFrame = false;
380                }
381                if (stepAnimation(currentTime)) {
382                    // animation isn't over, step any thumbnail and that's
383                    // it for now.
384                    if (thumbnail != null) {
385                        stepThumbnailAnimation(currentTime);
386                    }
387                    return true;
388                }
389            }
390        } else if (animation != null) {
391            // If the display is frozen, and there is a pending animation,
392            // clear it and make sure we run the cleanup code.
393            animating = true;
394            animation = null;
395        }
396
397        hasTransformation = false;
398
399        if (!animating && animation == null) {
400            return false;
401        }
402
403        mAppToken.setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "AppWindowToken");
404
405        clearAnimation();
406        animating = false;
407        if (animLayerAdjustment != 0) {
408            animLayerAdjustment = 0;
409            updateLayers();
410        }
411        if (mService.mInputMethodTarget != null
412                && mService.mInputMethodTarget.mAppToken == mAppToken) {
413            mAppToken.getDisplayContent().computeImeTarget(true /* updateImeTarget */);
414        }
415
416        if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + mAppToken
417                + ": reportedVisible=" + mAppToken.reportedVisible
418                + " okToDisplay=" + mService.okToDisplay()
419                + " startingDisplayed=" + mAppToken.startingDisplayed);
420
421        transformation.clear();
422
423        final int numAllAppWinAnimators = mAllAppWinAnimators.size();
424        for (int i = 0; i < numAllAppWinAnimators; i++) {
425            mAllAppWinAnimators.get(i).mWin.onExitAnimationDone();
426        }
427        mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token);
428        return false;
429    }
430
431    // This must be called while inside a transaction.
432    boolean showAllWindowsLocked() {
433        boolean isAnimating = false;
434        final int NW = mAllAppWinAnimators.size();
435        for (int i=0; i<NW; i++) {
436            WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i);
437            if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + winAnimator);
438            winAnimator.mWin.performShowLocked();
439            isAnimating |= winAnimator.isAnimationSet();
440        }
441        return isAnimating;
442    }
443
444    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
445        pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
446        pw.print(prefix); pw.print("mAnimator="); pw.println(mAnimator);
447        pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen);
448                pw.print(" allDrawn="); pw.print(allDrawn);
449                pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment);
450        if (lastFreezeDuration != 0) {
451            pw.print(prefix); pw.print("lastFreezeDuration=");
452                    TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println();
453        }
454        if (animating || animation != null) {
455            pw.print(prefix); pw.print("animating="); pw.println(animating);
456            pw.print(prefix); pw.print("animation="); pw.println(animation);
457            pw.print(prefix); pw.print("mTransit="); pw.println(mTransit);
458            pw.print(prefix); pw.print("mTransitFlags="); pw.println(mTransitFlags);
459        }
460        if (hasTransformation) {
461            pw.print(prefix); pw.print("XForm: ");
462                    transformation.printShortString(pw);
463                    pw.println();
464        }
465        if (thumbnail != null) {
466            pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
467                    pw.print(" layer="); pw.println(thumbnailLayer);
468            pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
469            pw.print(prefix); pw.print("thumbnailTransformation=");
470                    pw.println(thumbnailTransformation.toShortString());
471        }
472        for (int i=0; i<mAllAppWinAnimators.size(); i++) {
473            WindowStateAnimator wanim = mAllAppWinAnimators.get(i);
474            pw.print(prefix); pw.print("App Win Anim #"); pw.print(i);
475                    pw.print(": "); pw.println(wanim);
476        }
477    }
478
479    void startProlongAnimation(int prolongType) {
480        mProlongAnimation = prolongType;
481        mClearProlongedAnimation = false;
482    }
483
484    void endProlongedAnimation() {
485        mProlongAnimation = PROLONG_ANIMATION_DISABLED;
486    }
487
488    // This is an animation that does nothing: it just immediately finishes
489    // itself every time it is called.  It is used as a stub animation in cases
490    // where we want to synchronize multiple things that may be animating.
491    static final class DummyAnimation extends Animation {
492        @Override
493        public boolean getTransformation(long currentTime, Transformation outTransformation) {
494            return false;
495        }
496    }
497
498}
499