• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 static com.android.server.wm.AnimationAdapter.STATUS_BAR_TRANSITION_DURATION;
20 import static com.android.server.wm.AnimationSpecProto.WINDOW;
21 import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
22 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
23 
24 import android.graphics.Point;
25 import android.graphics.Rect;
26 import android.os.SystemClock;
27 import android.util.proto.ProtoOutputStream;
28 import android.view.SurfaceControl;
29 import android.view.SurfaceControl.Transaction;
30 import android.view.animation.Animation;
31 import android.view.animation.AnimationSet;
32 import android.view.animation.Interpolator;
33 import android.view.animation.Transformation;
34 import android.view.animation.TranslateAnimation;
35 
36 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
37 
38 import java.io.PrintWriter;
39 
40 /**
41  * Animation spec for regular window animations.
42  */
43 public class WindowAnimationSpec implements AnimationSpec {
44 
45     private Animation mAnimation;
46     private final Point mPosition = new Point();
47     private final ThreadLocal<TmpValues> mThreadLocalTmps = ThreadLocal.withInitial(TmpValues::new);
48     private final boolean mCanSkipFirstFrame;
49     private final boolean mIsAppAnimation;
50     private final Rect mRootTaskBounds = new Rect();
51     private int mRootTaskClipMode;
52     private final Rect mTmpRect = new Rect();
53     private final float mWindowCornerRadius;
54 
WindowAnimationSpec(Animation animation, Point position, boolean canSkipFirstFrame, float windowCornerRadius)55     public WindowAnimationSpec(Animation animation, Point position, boolean canSkipFirstFrame,
56             float windowCornerRadius)  {
57         this(animation, position, null /* rootTaskBounds */, canSkipFirstFrame, ROOT_TASK_CLIP_NONE,
58                 false /* isAppAnimation */, windowCornerRadius);
59     }
60 
WindowAnimationSpec(Animation animation, Point position, Rect rootTaskBounds, boolean canSkipFirstFrame, int rootTaskClipMode, boolean isAppAnimation, float windowCornerRadius)61     public WindowAnimationSpec(Animation animation, Point position, Rect rootTaskBounds,
62             boolean canSkipFirstFrame, int rootTaskClipMode, boolean isAppAnimation,
63             float windowCornerRadius) {
64         mAnimation = animation;
65         if (position != null) {
66             mPosition.set(position.x, position.y);
67         }
68         mWindowCornerRadius = windowCornerRadius;
69         mCanSkipFirstFrame = canSkipFirstFrame;
70         mIsAppAnimation = isAppAnimation;
71         mRootTaskClipMode = rootTaskClipMode;
72         if (rootTaskBounds != null) {
73             mRootTaskBounds.set(rootTaskBounds);
74         }
75     }
76 
77     @Override
getShowWallpaper()78     public boolean getShowWallpaper() {
79         return mAnimation.getShowWallpaper();
80     }
81 
82     @Override
getDuration()83     public long getDuration() {
84         return mAnimation.computeDurationHint();
85     }
86 
87     @Override
apply(Transaction t, SurfaceControl leash, long currentPlayTime)88     public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
89         final TmpValues tmp = mThreadLocalTmps.get();
90         tmp.transformation.clear();
91         mAnimation.getTransformation(currentPlayTime, tmp.transformation);
92         tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
93         t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);
94         t.setAlpha(leash, tmp.transformation.getAlpha());
95 
96         boolean cropSet = false;
97         if (mRootTaskClipMode == ROOT_TASK_CLIP_NONE) {
98             if (tmp.transformation.hasClipRect()) {
99                 t.setWindowCrop(leash, tmp.transformation.getClipRect());
100                 cropSet = true;
101             }
102         } else {
103             mTmpRect.set(mRootTaskBounds);
104             if (tmp.transformation.hasClipRect()) {
105                 mTmpRect.intersect(tmp.transformation.getClipRect());
106             }
107             t.setWindowCrop(leash, mTmpRect);
108             cropSet = true;
109         }
110 
111         // We can only apply rounded corner if a crop is set, as otherwise the value is meaningless,
112         // since it doesn't have anything it's relative to.
113         if (cropSet && mAnimation.hasRoundedCorners() && mWindowCornerRadius > 0) {
114             t.setCornerRadius(leash, mWindowCornerRadius);
115         }
116     }
117 
118     @Override
calculateStatusBarTransitionStartTime()119     public long calculateStatusBarTransitionStartTime() {
120         TranslateAnimation openTranslateAnimation = findTranslateAnimation(mAnimation);
121         if (openTranslateAnimation != null) {
122 
123             // Some interpolators are extremely quickly mostly finished, but not completely. For
124             // our purposes, we need to find the fraction for which ther interpolator is mostly
125             // there, and use that value for the calculation.
126             float t = findAlmostThereFraction(openTranslateAnimation.getInterpolator());
127             return SystemClock.uptimeMillis()
128                     + openTranslateAnimation.getStartOffset()
129                     + (long)(openTranslateAnimation.getDuration() * t)
130                     - STATUS_BAR_TRANSITION_DURATION;
131         } else {
132             return SystemClock.uptimeMillis();
133         }
134     }
135 
136     @Override
canSkipFirstFrame()137     public boolean canSkipFirstFrame() {
138         return mCanSkipFirstFrame;
139     }
140 
141     @Override
needsEarlyWakeup()142     public boolean needsEarlyWakeup() {
143         return mIsAppAnimation;
144     }
145 
146     @Override
dump(PrintWriter pw, String prefix)147     public void dump(PrintWriter pw, String prefix) {
148         pw.print(prefix); pw.println(mAnimation);
149     }
150 
151     @Override
dumpDebugInner(ProtoOutputStream proto)152     public void dumpDebugInner(ProtoOutputStream proto) {
153         final long token = proto.start(WINDOW);
154         proto.write(ANIMATION, mAnimation.toString());
155         proto.end(token);
156     }
157 
158     /**
159      * Tries to find a {@link TranslateAnimation} inside the {@code animation}.
160      *
161      * @return the found animation, {@code null} otherwise
162      */
findTranslateAnimation(Animation animation)163     private static TranslateAnimation findTranslateAnimation(Animation animation) {
164         if (animation instanceof TranslateAnimation) {
165             return (TranslateAnimation) animation;
166         } else if (animation instanceof AnimationSet) {
167             AnimationSet set = (AnimationSet) animation;
168             for (int i = 0; i < set.getAnimations().size(); i++) {
169                 Animation a = set.getAnimations().get(i);
170                 if (a instanceof TranslateAnimation) {
171                     return (TranslateAnimation) a;
172                 }
173             }
174         }
175         return null;
176     }
177 
178     /**
179      * Binary searches for a {@code t} such that there exists a {@code -0.01 < eps < 0.01} for which
180      * {@code interpolator(t + eps) > 0.99}.
181      */
findAlmostThereFraction(Interpolator interpolator)182     private static float findAlmostThereFraction(Interpolator interpolator) {
183         float val = 0.5f;
184         float adj = 0.25f;
185         while (adj >= 0.01f) {
186             if (interpolator.getInterpolation(val) < 0.99f) {
187                 val += adj;
188             } else {
189                 val -= adj;
190             }
191             adj /= 2;
192         }
193         return val;
194     }
195 
196     private static class TmpValues {
197         final Transformation transformation = new Transformation();
198         final float[] floats = new float[9];
199     }
200 }
201