blob: 65c8e96a0774287f4281de51d0d23d7322d7dc72 [file] [log] [blame]
Robert Carrf59b8dd2017-10-02 18:58:36 -07001/*
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
Kweku Adams21b8d262018-03-30 12:19:58 -070019import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS;
Yi Jin6c6e9ca2018-03-20 16:53:35 -070020import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
21import static com.android.server.wm.AlphaAnimationSpecProto.TO;
22import static com.android.server.wm.AnimationSpecProto.ALPHA;
Jorim Jaggif75d1612018-02-27 15:05:21 +010023
Robert Carrf59b8dd2017-10-02 18:58:36 -070024import android.graphics.Rect;
chaviwc5eb8162018-05-25 16:18:42 -070025import android.util.Log;
Jorim Jaggif75d1612018-02-27 15:05:21 +010026import android.util.proto.ProtoOutputStream;
chaviwc5eb8162018-05-25 16:18:42 -070027import android.view.Surface;
Jorim Jaggif75d1612018-02-27 15:05:21 +010028import android.view.SurfaceControl;
Robert Carrf59b8dd2017-10-02 18:58:36 -070029
chaviw2fb06bc2018-01-19 17:09:15 -080030import com.android.internal.annotations.VisibleForTesting;
31
Jorim Jaggif75d1612018-02-27 15:05:21 +010032import java.io.PrintWriter;
33
Robert Carrf59b8dd2017-10-02 18:58:36 -070034/**
35 * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
36 * black layers of varying opacity at various Z-levels which create the effect of a Dim.
37 */
38class Dimmer {
39 private static final String TAG = "WindowManager";
Kweku Adams21b8d262018-03-30 12:19:58 -070040 // This is in milliseconds.
chaviw2fb06bc2018-01-19 17:09:15 -080041 private static final int DEFAULT_DIM_ANIM_DURATION = 200;
Robert Carrf59b8dd2017-10-02 18:58:36 -070042
chaviw2fb06bc2018-01-19 17:09:15 -080043 private class DimAnimatable implements SurfaceAnimator.Animatable {
44 private final SurfaceControl mDimLayer;
45
46 private DimAnimatable(SurfaceControl dimLayer) {
47 mDimLayer = dimLayer;
48 }
49
50 @Override
51 public SurfaceControl.Transaction getPendingTransaction() {
52 return mHost.getPendingTransaction();
53 }
54
55 @Override
56 public void commitPendingTransaction() {
57 mHost.commitPendingTransaction();
58 }
59
60 @Override
61 public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
62 }
63
64 @Override
65 public void onAnimationLeashDestroyed(SurfaceControl.Transaction t) {
66 }
67
68 @Override
chaviw2fb06bc2018-01-19 17:09:15 -080069 public SurfaceControl.Builder makeAnimationLeash() {
70 return mHost.makeAnimationLeash();
71 }
72
73 @Override
74 public SurfaceControl getAnimationLeashParent() {
75 return mHost.getSurfaceControl();
76 }
77
78 @Override
79 public SurfaceControl getSurfaceControl() {
80 return mDimLayer;
81 }
82
83 @Override
84 public SurfaceControl getParentSurfaceControl() {
85 return mHost.getSurfaceControl();
86 }
87
88 @Override
89 public int getSurfaceWidth() {
90 // This will determine the size of the leash created. This should be the size of the
91 // host and not the dim layer since the dim layer may get bigger during animation. If
92 // that occurs, the leash size cannot change so we need to ensure the leash is big
93 // enough that the dim layer can grow.
94 // This works because the mHost will be a Task which has the display bounds.
95 return mHost.getSurfaceWidth();
96 }
97
98 @Override
99 public int getSurfaceHeight() {
100 // See getSurfaceWidth() above for explanation.
101 return mHost.getSurfaceHeight();
102 }
103 }
104
105 @VisibleForTesting
106 class DimState {
107 /**
108 * The layer where property changes should be invoked on.
109 */
110 SurfaceControl mDimLayer;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700111 boolean mDimming;
chaviw2fb06bc2018-01-19 17:09:15 -0800112 boolean isVisible;
113 SurfaceAnimator mSurfaceAnimator;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700114
115 /**
chaviw87ca63a2018-03-26 14:06:17 -0700116 * Determines whether the dim layer should animate before destroying.
117 */
118 boolean mAnimateExit = true;
119
120 /**
chaviw2fb06bc2018-01-19 17:09:15 -0800121 * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
Robert Carrf59b8dd2017-10-02 18:58:36 -0700122 * details on Dim lifecycle.
123 */
124 boolean mDontReset;
125
chaviw2fb06bc2018-01-19 17:09:15 -0800126 DimState(SurfaceControl dimLayer) {
127 mDimLayer = dimLayer;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700128 mDimming = true;
chaviw2fb06bc2018-01-19 17:09:15 -0800129 mSurfaceAnimator = new SurfaceAnimator(new DimAnimatable(dimLayer), () -> {
130 if (!mDimming) {
131 mDimLayer.destroy();
132 }
Chavi Weingartenb736e322018-02-23 00:27:54 +0000133 }, mHost.mService);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700134 }
chaviw2fb06bc2018-01-19 17:09:15 -0800135 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700136
Robert Carrf59b8dd2017-10-02 18:58:36 -0700137 /**
138 * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
139 * host, some controller of it, or one of the hosts children.
140 */
141 private WindowContainer mHost;
chaviwb792a952018-02-02 11:55:21 -0800142 private WindowContainer mLastRequestedDimContainer;
chaviwf20bd222018-02-01 16:06:52 -0800143 @VisibleForTesting
144 DimState mDimState;
145
146 private final SurfaceAnimatorStarter mSurfaceAnimatorStarter;
147
148 @VisibleForTesting
149 interface SurfaceAnimatorStarter {
150 void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
151 AnimationAdapter anim, boolean hidden);
152 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700153
154 Dimmer(WindowContainer host) {
chaviwf20bd222018-02-01 16:06:52 -0800155 this(host, SurfaceAnimator::startAnimation);
156 }
157
158 Dimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter) {
Robert Carrf59b8dd2017-10-02 18:58:36 -0700159 mHost = host;
chaviwf20bd222018-02-01 16:06:52 -0800160 mSurfaceAnimatorStarter = surfaceAnimatorStarter;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700161 }
162
chaviw2fb06bc2018-01-19 17:09:15 -0800163 private SurfaceControl makeDimLayer() {
164 return mHost.makeChildSurface(null)
Robert Carrf59b8dd2017-10-02 18:58:36 -0700165 .setParent(mHost.getSurfaceControl())
166 .setColorLayer(true)
167 .setName("Dim Layer for - " + mHost.getName())
168 .build();
Robert Carrf59b8dd2017-10-02 18:58:36 -0700169 }
170
171 /**
chaviwb792a952018-02-02 11:55:21 -0800172 * Retrieve the DimState, creating one if it doesn't exist.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700173 */
chaviw2fb06bc2018-01-19 17:09:15 -0800174 private DimState getDimState(WindowContainer container) {
chaviwb792a952018-02-02 11:55:21 -0800175 if (mDimState == null) {
chaviwc5eb8162018-05-25 16:18:42 -0700176 try {
177 final SurfaceControl ctl = makeDimLayer();
178 mDimState = new DimState(ctl);
179 /**
180 * See documentation on {@link #dimAbove} to understand lifecycle management of
181 * Dim's via state resetting for Dim's with containers.
182 */
183 if (container == null) {
184 mDimState.mDontReset = true;
185 }
186 } catch (Surface.OutOfResourcesException e) {
187 Log.w(TAG, "OutOfResourcesException creating dim surface");
Robert Carrf59b8dd2017-10-02 18:58:36 -0700188 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700189 }
chaviwb792a952018-02-02 11:55:21 -0800190
191 mLastRequestedDimContainer = container;
192 return mDimState;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700193 }
194
195 private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
196 float alpha) {
197 final DimState d = getDimState(container);
chaviwc5eb8162018-05-25 16:18:42 -0700198
199 if (d == null) {
200 return;
201 }
202
Robert Carrf59b8dd2017-10-02 18:58:36 -0700203 if (container != null) {
chaviwb792a952018-02-02 11:55:21 -0800204 // The dim method is called from WindowState.prepareSurfaces(), which is always called
205 // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
206 // relative to the highest Z layer with a dim.
chaviw2fb06bc2018-01-19 17:09:15 -0800207 t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700208 } else {
chaviw2fb06bc2018-01-19 17:09:15 -0800209 t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700210 }
chaviw2fb06bc2018-01-19 17:09:15 -0800211 t.setAlpha(d.mDimLayer, alpha);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700212
213 d.mDimming = true;
214 }
215
216 /**
217 * Finish a dim started by dimAbove in the case there was no call to dimAbove.
218 *
219 * @param t A Transaction in which to finish the dim.
220 */
221 void stopDim(SurfaceControl.Transaction t) {
chaviwc5eb8162018-05-25 16:18:42 -0700222 if (mDimState != null) {
223 t.hide(mDimState.mDimLayer);
224 mDimState.isVisible = false;
225 mDimState.mDontReset = false;
226 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700227 }
chaviw2fb06bc2018-01-19 17:09:15 -0800228
Robert Carrf59b8dd2017-10-02 18:58:36 -0700229 /**
230 * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
231 * remove this effect. If the Dim can be assosciated with a particular child of the host
232 * consider using the other variant of dimAbove which ties the Dim lifetime to the child
233 * lifetime more explicitly.
234 *
chaviw2fb06bc2018-01-19 17:09:15 -0800235 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700236 * @param alpha The alpha at which to Dim.
237 */
238 void dimAbove(SurfaceControl.Transaction t, float alpha) {
239 dim(t, null, 1, alpha);
240 }
241
242 /**
243 * Place a dim above the given container, which should be a child of the host container.
244 * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
245 * and the child should call dimAbove again to request the Dim to continue.
246 *
chaviw2fb06bc2018-01-19 17:09:15 -0800247 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700248 * @param container The container which to dim above. Should be a child of our host.
chaviw2fb06bc2018-01-19 17:09:15 -0800249 * @param alpha The alpha at which to Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700250 */
251 void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
252 dim(t, container, 1, alpha);
253 }
254
255 /**
256 * Like {@link #dimAbove} but places the dim below the given container.
257 *
chaviw2fb06bc2018-01-19 17:09:15 -0800258 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700259 * @param container The container which to dim below. Should be a child of our host.
chaviw2fb06bc2018-01-19 17:09:15 -0800260 * @param alpha The alpha at which to Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700261 */
262
263 void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
264 dim(t, container, -1, alpha);
265 }
266
267 /**
268 * Mark all dims as pending completion on the next call to {@link #updateDims}
269 *
270 * This is intended for us by the host container, to be called at the beginning of
271 * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
272 * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
273 * a chance to request dims to continue.
274 */
275 void resetDimStates() {
chaviwb792a952018-02-02 11:55:21 -0800276 if (mDimState != null && !mDimState.mDontReset) {
277 mDimState.mDimming = false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700278 }
279 }
280
chaviw87ca63a2018-03-26 14:06:17 -0700281 void dontAnimateExit() {
282 if (mDimState != null) {
283 mDimState.mAnimateExit = false;
284 }
285 }
286
Robert Carrf59b8dd2017-10-02 18:58:36 -0700287 /**
288 * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
289 * described in {@link #resetDimStates}.
290 *
chaviw2fb06bc2018-01-19 17:09:15 -0800291 * @param t A transaction in which to update the dims.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700292 * @param bounds The bounds at which to dim.
293 * @return true if any Dims were updated.
294 */
295 boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
chaviwb792a952018-02-02 11:55:21 -0800296 if (mDimState == null) {
297 return false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700298 }
chaviwb792a952018-02-02 11:55:21 -0800299
300 if (!mDimState.mDimming) {
chaviw87ca63a2018-03-26 14:06:17 -0700301 if (!mDimState.mAnimateExit) {
302 t.destroy(mDimState.mDimLayer);
303 } else {
304 startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
305 }
chaviwb792a952018-02-02 11:55:21 -0800306 mDimState = null;
307 return false;
308 } else {
309 // TODO: Once we use geometry from hierarchy this falls away.
310 t.setSize(mDimState.mDimLayer, bounds.width(), bounds.height());
311 t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
312 if (!mDimState.isVisible) {
313 mDimState.isVisible = true;
314 t.show(mDimState.mDimLayer);
315 startDimEnter(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
316 }
317 return true;
318 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700319 }
chaviw2fb06bc2018-01-19 17:09:15 -0800320
321 private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
322 SurfaceControl.Transaction t) {
323 startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
324 }
325
326 private void startDimExit(WindowContainer container, SurfaceAnimator animator,
327 SurfaceControl.Transaction t) {
328 startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
329 }
330
331 private void startAnim(WindowContainer container, SurfaceAnimator animator,
332 SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
chaviwf20bd222018-02-01 16:06:52 -0800333 mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
chaviw2fb06bc2018-01-19 17:09:15 -0800334 new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
335 mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
336 }
337
338 private long getDimDuration(WindowContainer container) {
339 // If there's no container, then there isn't an animation occurring while dimming. Set the
340 // duration to 0 so it immediately dims to the set alpha.
341 if (container == null) {
342 return 0;
343 }
344
345 // Otherwise use the same duration as the animation on the WindowContainer
346 AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
347 return animationAdapter == null ? DEFAULT_DIM_ANIM_DURATION
348 : animationAdapter.getDurationHint();
349 }
350
351 private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
352 private final long mDuration;
353 private final float mFromAlpha;
354 private final float mToAlpha;
355
356 AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
357 mFromAlpha = fromAlpha;
358 mToAlpha = toAlpha;
359 mDuration = duration;
360 }
361
362 @Override
363 public long getDuration() {
364 return mDuration;
365 }
366
367 @Override
368 public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
369 float alpha = ((float) currentPlayTime / getDuration()) * (mToAlpha - mFromAlpha)
370 + mFromAlpha;
371 t.setAlpha(sc, alpha);
372 }
Jorim Jaggif75d1612018-02-27 15:05:21 +0100373
374 @Override
375 public void dump(PrintWriter pw, String prefix) {
376 pw.print(prefix); pw.print("from="); pw.print(mFromAlpha);
377 pw.print(" to="); pw.print(mToAlpha);
378 pw.print(" duration="); pw.println(mDuration);
379 }
380
381 @Override
382 public void writeToProtoInner(ProtoOutputStream proto) {
383 final long token = proto.start(ALPHA);
384 proto.write(FROM, mFromAlpha);
385 proto.write(TO, mToAlpha);
Kweku Adams21b8d262018-03-30 12:19:58 -0700386 proto.write(DURATION_MS, mDuration);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100387 proto.end(token);
388 }
chaviw2fb06bc2018-01-19 17:09:15 -0800389 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700390}