blob: 548e23a4d9849f6be3782b35825882ee454cac59 [file] [log] [blame]
Jorim Jaggi21c39a72017-10-20 15:47:51 +02001/*
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
17package com.android.server.wm;
18
Jorim Jaggif5f9e122017-10-24 18:21:09 +020019import static com.android.server.wm.AnimationAdapter.STATUS_BAR_TRANSITION_DURATION;
chaviw23012112017-12-20 15:29:04 -080020import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
21import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
Yi Jin6c6e9ca2018-03-20 16:53:35 -070022import static com.android.server.wm.AnimationSpecProto.WINDOW;
23import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
Jorim Jaggif5f9e122017-10-24 18:21:09 +020024
Jorim Jaggi21c39a72017-10-20 15:47:51 +020025import android.graphics.Point;
chaviw23012112017-12-20 15:29:04 -080026import android.graphics.Rect;
Jorim Jaggif5f9e122017-10-24 18:21:09 +020027import android.os.SystemClock;
Jorim Jaggif75d1612018-02-27 15:05:21 +010028import android.util.proto.ProtoOutputStream;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020029import android.view.SurfaceControl;
30import android.view.SurfaceControl.Transaction;
31import android.view.animation.Animation;
Jorim Jaggif5f9e122017-10-24 18:21:09 +020032import android.view.animation.AnimationSet;
33import android.view.animation.Interpolator;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020034import android.view.animation.Transformation;
Jorim Jaggif5f9e122017-10-24 18:21:09 +020035import android.view.animation.TranslateAnimation;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020036
37import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
38
Jorim Jaggif75d1612018-02-27 15:05:21 +010039import java.io.PrintWriter;
40
Jorim Jaggi21c39a72017-10-20 15:47:51 +020041/**
42 * Animation spec for regular window animations.
43 */
44public class WindowAnimationSpec implements AnimationSpec {
45
46 private Animation mAnimation;
47 private final Point mPosition = new Point();
Jorim Jaggia5e10572017-11-15 14:36:26 +010048 private final ThreadLocal<TmpValues> mThreadLocalTmps = ThreadLocal.withInitial(TmpValues::new);
Jorim Jaggi2e3c31d2017-11-20 19:49:00 +010049 private final boolean mCanSkipFirstFrame;
Jorim Jaggiaa763cd2018-03-22 23:20:36 +010050 private final boolean mIsAppAnimation;
chaviw23012112017-12-20 15:29:04 -080051 private final Rect mStackBounds = new Rect();
52 private int mStackClipMode;
53 private final Rect mTmpRect = new Rect();
Jorim Jaggi21c39a72017-10-20 15:47:51 +020054
Jorim Jaggi2e3c31d2017-11-20 19:49:00 +010055 public WindowAnimationSpec(Animation animation, Point position, boolean canSkipFirstFrame) {
Jorim Jaggiaa763cd2018-03-22 23:20:36 +010056 this(animation, position, null /* stackBounds */, canSkipFirstFrame, STACK_CLIP_NONE,
57 false /* isAppAnimation */);
chaviw23012112017-12-20 15:29:04 -080058 }
59
60 public WindowAnimationSpec(Animation animation, Point position, Rect stackBounds,
Jorim Jaggiaa763cd2018-03-22 23:20:36 +010061 boolean canSkipFirstFrame, int stackClipMode, boolean isAppAnimation) {
Jorim Jaggi21c39a72017-10-20 15:47:51 +020062 mAnimation = animation;
chaviw23012112017-12-20 15:29:04 -080063 if (position != null) {
64 mPosition.set(position.x, position.y);
65 }
Jorim Jaggi2e3c31d2017-11-20 19:49:00 +010066 mCanSkipFirstFrame = canSkipFirstFrame;
Jorim Jaggiaa763cd2018-03-22 23:20:36 +010067 mIsAppAnimation = isAppAnimation;
chaviw23012112017-12-20 15:29:04 -080068 mStackClipMode = stackClipMode;
69 if (stackBounds != null) {
70 mStackBounds.set(stackBounds);
71 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +020072 }
73
74 @Override
75 public boolean getDetachWallpaper() {
76 return mAnimation.getDetachWallpaper();
77 }
78
79 @Override
Jorim Jaggi82c17862018-02-21 17:50:18 +010080 public boolean getShowWallpaper() {
81 return mAnimation.getShowWallpaper();
82 }
83
84 @Override
Jorim Jaggi21c39a72017-10-20 15:47:51 +020085 public int getBackgroundColor() {
86 return mAnimation.getBackgroundColor();
87 }
88
89 @Override
90 public long getDuration() {
91 return mAnimation.computeDurationHint();
92 }
93
94 @Override
95 public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
Jorim Jaggia5e10572017-11-15 14:36:26 +010096 final TmpValues tmp = mThreadLocalTmps.get();
Jorim Jaggi21c39a72017-10-20 15:47:51 +020097 tmp.transformation.clear();
98 mAnimation.getTransformation(currentPlayTime, tmp.transformation);
99 tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
100 t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);
101 t.setAlpha(leash, tmp.transformation.getAlpha());
chaviw23012112017-12-20 15:29:04 -0800102 if (mStackClipMode == STACK_CLIP_NONE) {
103 t.setWindowCrop(leash, tmp.transformation.getClipRect());
104 } else if (mStackClipMode == STACK_CLIP_AFTER_ANIM) {
chaviwf6637d62018-01-08 13:36:23 -0800105 mTmpRect.set(mStackBounds);
106 // Offset stack bounds to stack position so the final crop is in screen space.
107 mTmpRect.offsetTo(mPosition.x, mPosition.y);
108 t.setFinalCrop(leash, mTmpRect);
chaviw23012112017-12-20 15:29:04 -0800109 t.setWindowCrop(leash, tmp.transformation.getClipRect());
110 } else {
chaviw977482a2017-12-28 11:35:53 -0800111 mTmpRect.set(mStackBounds);
112 mTmpRect.intersect(tmp.transformation.getClipRect());
chaviw23012112017-12-20 15:29:04 -0800113 t.setWindowCrop(leash, mTmpRect);
114 }
Jorim Jaggif5f9e122017-10-24 18:21:09 +0200115 }
116
117 @Override
118 public long calculateStatusBarTransitionStartTime() {
119 TranslateAnimation openTranslateAnimation = findTranslateAnimation(mAnimation);
120 if (openTranslateAnimation != null) {
121
122 // Some interpolators are extremely quickly mostly finished, but not completely. For
123 // our purposes, we need to find the fraction for which ther interpolator is mostly
124 // there, and use that value for the calculation.
125 float t = findAlmostThereFraction(openTranslateAnimation.getInterpolator());
126 return SystemClock.uptimeMillis()
127 + openTranslateAnimation.getStartOffset()
128 + (long)(openTranslateAnimation.getDuration() * t)
129 - STATUS_BAR_TRANSITION_DURATION;
130 } else {
131 return SystemClock.uptimeMillis();
132 }
133 }
134
Jorim Jaggi2e3c31d2017-11-20 19:49:00 +0100135 @Override
136 public boolean canSkipFirstFrame() {
137 return mCanSkipFirstFrame;
138 }
139
Jorim Jaggif75d1612018-02-27 15:05:21 +0100140 @Override
Jorim Jaggiaa763cd2018-03-22 23:20:36 +0100141 public boolean needsEarlyWakeup() {
142 return mIsAppAnimation;
143 }
144
145 @Override
Jorim Jaggif75d1612018-02-27 15:05:21 +0100146 public void dump(PrintWriter pw, String prefix) {
147 pw.print(prefix); pw.println(mAnimation);
148 }
149
150 @Override
151 public void writeToProtoInner(ProtoOutputStream proto) {
152 final long token = proto.start(WINDOW);
153 proto.write(ANIMATION, mAnimation.toString());
154 proto.end(token);
155 }
156
Jorim Jaggif5f9e122017-10-24 18:21:09 +0200157 /**
158 * Tries to find a {@link TranslateAnimation} inside the {@code animation}.
159 *
160 * @return the found animation, {@code null} otherwise
161 */
162 private static TranslateAnimation findTranslateAnimation(Animation animation) {
163 if (animation instanceof TranslateAnimation) {
164 return (TranslateAnimation) animation;
165 } else if (animation instanceof AnimationSet) {
166 AnimationSet set = (AnimationSet) animation;
167 for (int i = 0; i < set.getAnimations().size(); i++) {
168 Animation a = set.getAnimations().get(i);
169 if (a instanceof TranslateAnimation) {
170 return (TranslateAnimation) a;
171 }
172 }
173 }
174 return null;
175 }
176
177 /**
178 * Binary searches for a {@code t} such that there exists a {@code -0.01 < eps < 0.01} for which
179 * {@code interpolator(t + eps) > 0.99}.
180 */
181 private static float findAlmostThereFraction(Interpolator interpolator) {
182 float val = 0.5f;
183 float adj = 0.25f;
184 while (adj >= 0.01f) {
185 if (interpolator.getInterpolation(val) < 0.99f) {
186 val += adj;
187 } else {
188 val -= adj;
189 }
190 adj /= 2;
191 }
192 return val;
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200193 }
194
Jorim Jaggia5e10572017-11-15 14:36:26 +0100195 private static class TmpValues {
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200196 final Transformation transformation = new Transformation();
197 final float[] floats = new float[9];
198 }
199}