blob: b8431b1d8ad8acf91c0dc5f9219af4d842935203 [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;
Jorim Jaggif75d1612018-02-27 15:05:21 +010025import android.util.proto.ProtoOutputStream;
26import android.view.SurfaceControl;
Robert Carrf59b8dd2017-10-02 18:58:36 -070027
chaviw2fb06bc2018-01-19 17:09:15 -080028import com.android.internal.annotations.VisibleForTesting;
29
Jorim Jaggif75d1612018-02-27 15:05:21 +010030import java.io.PrintWriter;
31
Robert Carrf59b8dd2017-10-02 18:58:36 -070032/**
33 * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
34 * black layers of varying opacity at various Z-levels which create the effect of a Dim.
35 */
36class Dimmer {
37 private static final String TAG = "WindowManager";
Kweku Adams21b8d262018-03-30 12:19:58 -070038 // This is in milliseconds.
chaviw2fb06bc2018-01-19 17:09:15 -080039 private static final int DEFAULT_DIM_ANIM_DURATION = 200;
Robert Carrf59b8dd2017-10-02 18:58:36 -070040
chaviw2fb06bc2018-01-19 17:09:15 -080041 private class DimAnimatable implements SurfaceAnimator.Animatable {
42 private final SurfaceControl mDimLayer;
43
44 private DimAnimatable(SurfaceControl dimLayer) {
45 mDimLayer = dimLayer;
46 }
47
48 @Override
49 public SurfaceControl.Transaction getPendingTransaction() {
50 return mHost.getPendingTransaction();
51 }
52
53 @Override
54 public void commitPendingTransaction() {
55 mHost.commitPendingTransaction();
56 }
57
58 @Override
59 public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
60 }
61
62 @Override
63 public void onAnimationLeashDestroyed(SurfaceControl.Transaction t) {
64 }
65
66 @Override
chaviw2fb06bc2018-01-19 17:09:15 -080067 public SurfaceControl.Builder makeAnimationLeash() {
68 return mHost.makeAnimationLeash();
69 }
70
71 @Override
72 public SurfaceControl getAnimationLeashParent() {
73 return mHost.getSurfaceControl();
74 }
75
76 @Override
77 public SurfaceControl getSurfaceControl() {
78 return mDimLayer;
79 }
80
81 @Override
82 public SurfaceControl getParentSurfaceControl() {
83 return mHost.getSurfaceControl();
84 }
85
86 @Override
87 public int getSurfaceWidth() {
88 // This will determine the size of the leash created. This should be the size of the
89 // host and not the dim layer since the dim layer may get bigger during animation. If
90 // that occurs, the leash size cannot change so we need to ensure the leash is big
91 // enough that the dim layer can grow.
92 // This works because the mHost will be a Task which has the display bounds.
93 return mHost.getSurfaceWidth();
94 }
95
96 @Override
97 public int getSurfaceHeight() {
98 // See getSurfaceWidth() above for explanation.
99 return mHost.getSurfaceHeight();
100 }
101 }
102
103 @VisibleForTesting
104 class DimState {
105 /**
106 * The layer where property changes should be invoked on.
107 */
108 SurfaceControl mDimLayer;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700109 boolean mDimming;
chaviw2fb06bc2018-01-19 17:09:15 -0800110 boolean isVisible;
111 SurfaceAnimator mSurfaceAnimator;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700112
113 /**
chaviw87ca63a2018-03-26 14:06:17 -0700114 * Determines whether the dim layer should animate before destroying.
115 */
116 boolean mAnimateExit = true;
117
118 /**
chaviw2fb06bc2018-01-19 17:09:15 -0800119 * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
Robert Carrf59b8dd2017-10-02 18:58:36 -0700120 * details on Dim lifecycle.
121 */
122 boolean mDontReset;
123
chaviw2fb06bc2018-01-19 17:09:15 -0800124 DimState(SurfaceControl dimLayer) {
125 mDimLayer = dimLayer;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700126 mDimming = true;
chaviw2fb06bc2018-01-19 17:09:15 -0800127 mSurfaceAnimator = new SurfaceAnimator(new DimAnimatable(dimLayer), () -> {
128 if (!mDimming) {
129 mDimLayer.destroy();
130 }
Chavi Weingartenb736e322018-02-23 00:27:54 +0000131 }, mHost.mService);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700132 }
chaviw2fb06bc2018-01-19 17:09:15 -0800133 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700134
Robert Carrf59b8dd2017-10-02 18:58:36 -0700135 /**
136 * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
137 * host, some controller of it, or one of the hosts children.
138 */
139 private WindowContainer mHost;
chaviwb792a952018-02-02 11:55:21 -0800140 private WindowContainer mLastRequestedDimContainer;
chaviwf20bd222018-02-01 16:06:52 -0800141 @VisibleForTesting
142 DimState mDimState;
143
144 private final SurfaceAnimatorStarter mSurfaceAnimatorStarter;
145
146 @VisibleForTesting
147 interface SurfaceAnimatorStarter {
148 void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
149 AnimationAdapter anim, boolean hidden);
150 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700151
152 Dimmer(WindowContainer host) {
chaviwf20bd222018-02-01 16:06:52 -0800153 this(host, SurfaceAnimator::startAnimation);
154 }
155
156 Dimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter) {
Robert Carrf59b8dd2017-10-02 18:58:36 -0700157 mHost = host;
chaviwf20bd222018-02-01 16:06:52 -0800158 mSurfaceAnimatorStarter = surfaceAnimatorStarter;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700159 }
160
chaviw2fb06bc2018-01-19 17:09:15 -0800161 private SurfaceControl makeDimLayer() {
162 return mHost.makeChildSurface(null)
Robert Carrf59b8dd2017-10-02 18:58:36 -0700163 .setParent(mHost.getSurfaceControl())
164 .setColorLayer(true)
165 .setName("Dim Layer for - " + mHost.getName())
166 .build();
Robert Carrf59b8dd2017-10-02 18:58:36 -0700167 }
168
169 /**
chaviwb792a952018-02-02 11:55:21 -0800170 * Retrieve the DimState, creating one if it doesn't exist.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700171 */
chaviw2fb06bc2018-01-19 17:09:15 -0800172 private DimState getDimState(WindowContainer container) {
chaviwb792a952018-02-02 11:55:21 -0800173 if (mDimState == null) {
Robert Carrf59b8dd2017-10-02 18:58:36 -0700174 final SurfaceControl ctl = makeDimLayer();
chaviwb792a952018-02-02 11:55:21 -0800175 mDimState = new DimState(ctl);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700176 /**
177 * See documentation on {@link #dimAbove} to understand lifecycle management of Dim's
178 * via state resetting for Dim's with containers.
179 */
180 if (container == null) {
chaviwb792a952018-02-02 11:55:21 -0800181 mDimState.mDontReset = true;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700182 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700183 }
chaviwb792a952018-02-02 11:55:21 -0800184
185 mLastRequestedDimContainer = container;
186 return mDimState;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700187 }
188
189 private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
190 float alpha) {
191 final DimState d = getDimState(container);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700192 if (container != null) {
chaviwb792a952018-02-02 11:55:21 -0800193 // The dim method is called from WindowState.prepareSurfaces(), which is always called
194 // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
195 // relative to the highest Z layer with a dim.
chaviw2fb06bc2018-01-19 17:09:15 -0800196 t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700197 } else {
chaviw2fb06bc2018-01-19 17:09:15 -0800198 t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700199 }
chaviw2fb06bc2018-01-19 17:09:15 -0800200 t.setAlpha(d.mDimLayer, alpha);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700201
202 d.mDimming = true;
203 }
204
205 /**
206 * Finish a dim started by dimAbove in the case there was no call to dimAbove.
207 *
208 * @param t A Transaction in which to finish the dim.
209 */
210 void stopDim(SurfaceControl.Transaction t) {
211 DimState d = getDimState(null);
chaviw2fb06bc2018-01-19 17:09:15 -0800212 t.hide(d.mDimLayer);
213 d.isVisible = false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700214 d.mDontReset = false;
215 }
chaviw2fb06bc2018-01-19 17:09:15 -0800216
Robert Carrf59b8dd2017-10-02 18:58:36 -0700217 /**
218 * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
219 * remove this effect. If the Dim can be assosciated with a particular child of the host
220 * consider using the other variant of dimAbove which ties the Dim lifetime to the child
221 * lifetime more explicitly.
222 *
chaviw2fb06bc2018-01-19 17:09:15 -0800223 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700224 * @param alpha The alpha at which to Dim.
225 */
226 void dimAbove(SurfaceControl.Transaction t, float alpha) {
227 dim(t, null, 1, alpha);
228 }
229
230 /**
231 * Place a dim above the given container, which should be a child of the host container.
232 * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
233 * and the child should call dimAbove again to request the Dim to continue.
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 container The container which to dim above. Should be a child of our host.
chaviw2fb06bc2018-01-19 17:09:15 -0800237 * @param alpha The alpha at which to Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700238 */
239 void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
240 dim(t, container, 1, alpha);
241 }
242
243 /**
244 * Like {@link #dimAbove} but places the dim below the given container.
245 *
chaviw2fb06bc2018-01-19 17:09:15 -0800246 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700247 * @param container The container which to dim below. Should be a child of our host.
chaviw2fb06bc2018-01-19 17:09:15 -0800248 * @param alpha The alpha at which to Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700249 */
250
251 void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
252 dim(t, container, -1, alpha);
253 }
254
255 /**
256 * Mark all dims as pending completion on the next call to {@link #updateDims}
257 *
258 * This is intended for us by the host container, to be called at the beginning of
259 * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
260 * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
261 * a chance to request dims to continue.
262 */
263 void resetDimStates() {
chaviwb792a952018-02-02 11:55:21 -0800264 if (mDimState != null && !mDimState.mDontReset) {
265 mDimState.mDimming = false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700266 }
267 }
268
chaviw87ca63a2018-03-26 14:06:17 -0700269 void dontAnimateExit() {
270 if (mDimState != null) {
271 mDimState.mAnimateExit = false;
272 }
273 }
274
Robert Carrf59b8dd2017-10-02 18:58:36 -0700275 /**
276 * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
277 * described in {@link #resetDimStates}.
278 *
chaviw2fb06bc2018-01-19 17:09:15 -0800279 * @param t A transaction in which to update the dims.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700280 * @param bounds The bounds at which to dim.
281 * @return true if any Dims were updated.
282 */
283 boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
chaviwb792a952018-02-02 11:55:21 -0800284 if (mDimState == null) {
285 return false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700286 }
chaviwb792a952018-02-02 11:55:21 -0800287
288 if (!mDimState.mDimming) {
chaviw87ca63a2018-03-26 14:06:17 -0700289 if (!mDimState.mAnimateExit) {
290 t.destroy(mDimState.mDimLayer);
291 } else {
292 startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
293 }
chaviwb792a952018-02-02 11:55:21 -0800294 mDimState = null;
295 return false;
296 } else {
297 // TODO: Once we use geometry from hierarchy this falls away.
298 t.setSize(mDimState.mDimLayer, bounds.width(), bounds.height());
299 t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
300 if (!mDimState.isVisible) {
301 mDimState.isVisible = true;
302 t.show(mDimState.mDimLayer);
303 startDimEnter(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
304 }
305 return true;
306 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700307 }
chaviw2fb06bc2018-01-19 17:09:15 -0800308
309 private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
310 SurfaceControl.Transaction t) {
311 startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
312 }
313
314 private void startDimExit(WindowContainer container, SurfaceAnimator animator,
315 SurfaceControl.Transaction t) {
316 startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
317 }
318
319 private void startAnim(WindowContainer container, SurfaceAnimator animator,
320 SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
chaviwf20bd222018-02-01 16:06:52 -0800321 mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
chaviw2fb06bc2018-01-19 17:09:15 -0800322 new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
323 mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
324 }
325
326 private long getDimDuration(WindowContainer container) {
327 // If there's no container, then there isn't an animation occurring while dimming. Set the
328 // duration to 0 so it immediately dims to the set alpha.
329 if (container == null) {
330 return 0;
331 }
332
333 // Otherwise use the same duration as the animation on the WindowContainer
334 AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
335 return animationAdapter == null ? DEFAULT_DIM_ANIM_DURATION
336 : animationAdapter.getDurationHint();
337 }
338
339 private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
340 private final long mDuration;
341 private final float mFromAlpha;
342 private final float mToAlpha;
343
344 AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
345 mFromAlpha = fromAlpha;
346 mToAlpha = toAlpha;
347 mDuration = duration;
348 }
349
350 @Override
351 public long getDuration() {
352 return mDuration;
353 }
354
355 @Override
356 public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
357 float alpha = ((float) currentPlayTime / getDuration()) * (mToAlpha - mFromAlpha)
358 + mFromAlpha;
359 t.setAlpha(sc, alpha);
360 }
Jorim Jaggif75d1612018-02-27 15:05:21 +0100361
362 @Override
363 public void dump(PrintWriter pw, String prefix) {
364 pw.print(prefix); pw.print("from="); pw.print(mFromAlpha);
365 pw.print(" to="); pw.print(mToAlpha);
366 pw.print(" duration="); pw.println(mDuration);
367 }
368
369 @Override
370 public void writeToProtoInner(ProtoOutputStream proto) {
371 final long token = proto.start(ALPHA);
372 proto.write(FROM, mFromAlpha);
373 proto.write(TO, mToAlpha);
Kweku Adams21b8d262018-03-30 12:19:58 -0700374 proto.write(DURATION_MS, mDuration);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100375 proto.end(token);
376 }
chaviw2fb06bc2018-01-19 17:09:15 -0800377 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700378}