blob: 4394a99bc5c94e35106434cb724a652b5a89533a [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
Robert Carrf59b8dd2017-10-02 18:58:36 -070019import android.view.SurfaceControl;
20import android.graphics.Rect;
21
chaviw2fb06bc2018-01-19 17:09:15 -080022import com.android.internal.annotations.VisibleForTesting;
23
Robert Carrf59b8dd2017-10-02 18:58:36 -070024/**
25 * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
26 * black layers of varying opacity at various Z-levels which create the effect of a Dim.
27 */
28class Dimmer {
29 private static final String TAG = "WindowManager";
chaviw2fb06bc2018-01-19 17:09:15 -080030 private static final int DEFAULT_DIM_ANIM_DURATION = 200;
Robert Carrf59b8dd2017-10-02 18:58:36 -070031
chaviw2fb06bc2018-01-19 17:09:15 -080032 private class DimAnimatable implements SurfaceAnimator.Animatable {
33 private final SurfaceControl mDimLayer;
34
35 private DimAnimatable(SurfaceControl dimLayer) {
36 mDimLayer = dimLayer;
37 }
38
39 @Override
40 public SurfaceControl.Transaction getPendingTransaction() {
41 return mHost.getPendingTransaction();
42 }
43
44 @Override
45 public void commitPendingTransaction() {
46 mHost.commitPendingTransaction();
47 }
48
49 @Override
50 public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
51 }
52
53 @Override
54 public void onAnimationLeashDestroyed(SurfaceControl.Transaction t) {
55 }
56
57 @Override
58 public void destroyAfterPendingTransaction(SurfaceControl surface) {
59 mHost.destroyAfterPendingTransaction(surface);
60 }
61
62 @Override
63 public SurfaceControl.Builder makeAnimationLeash() {
64 return mHost.makeAnimationLeash();
65 }
66
67 @Override
68 public SurfaceControl getAnimationLeashParent() {
69 return mHost.getSurfaceControl();
70 }
71
72 @Override
73 public SurfaceControl getSurfaceControl() {
74 return mDimLayer;
75 }
76
77 @Override
78 public SurfaceControl getParentSurfaceControl() {
79 return mHost.getSurfaceControl();
80 }
81
82 @Override
83 public int getSurfaceWidth() {
84 // This will determine the size of the leash created. This should be the size of the
85 // host and not the dim layer since the dim layer may get bigger during animation. If
86 // that occurs, the leash size cannot change so we need to ensure the leash is big
87 // enough that the dim layer can grow.
88 // This works because the mHost will be a Task which has the display bounds.
89 return mHost.getSurfaceWidth();
90 }
91
92 @Override
93 public int getSurfaceHeight() {
94 // See getSurfaceWidth() above for explanation.
95 return mHost.getSurfaceHeight();
96 }
97 }
98
99 @VisibleForTesting
100 class DimState {
101 /**
102 * The layer where property changes should be invoked on.
103 */
104 SurfaceControl mDimLayer;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700105 boolean mDimming;
chaviw2fb06bc2018-01-19 17:09:15 -0800106 boolean isVisible;
107 SurfaceAnimator mSurfaceAnimator;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700108
109 /**
chaviw2fb06bc2018-01-19 17:09:15 -0800110 * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
Robert Carrf59b8dd2017-10-02 18:58:36 -0700111 * details on Dim lifecycle.
112 */
113 boolean mDontReset;
114
chaviw2fb06bc2018-01-19 17:09:15 -0800115 DimState(SurfaceControl dimLayer) {
116 mDimLayer = dimLayer;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700117 mDimming = true;
chaviw2fb06bc2018-01-19 17:09:15 -0800118 mSurfaceAnimator = new SurfaceAnimator(new DimAnimatable(dimLayer), () -> {
119 if (!mDimming) {
120 mDimLayer.destroy();
121 }
122 }, mHost.mService.mAnimator::addAfterPrepareSurfacesRunnable, mHost.mService);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700123 }
chaviw2fb06bc2018-01-19 17:09:15 -0800124 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700125
Robert Carrf59b8dd2017-10-02 18:58:36 -0700126 /**
127 * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
128 * host, some controller of it, or one of the hosts children.
129 */
130 private WindowContainer mHost;
chaviwb792a952018-02-02 11:55:21 -0800131 private WindowContainer mLastRequestedDimContainer;
chaviwf20bd222018-02-01 16:06:52 -0800132 @VisibleForTesting
133 DimState mDimState;
134
135 private final SurfaceAnimatorStarter mSurfaceAnimatorStarter;
136
137 @VisibleForTesting
138 interface SurfaceAnimatorStarter {
139 void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
140 AnimationAdapter anim, boolean hidden);
141 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700142
143 Dimmer(WindowContainer host) {
chaviwf20bd222018-02-01 16:06:52 -0800144 this(host, SurfaceAnimator::startAnimation);
145 }
146
147 Dimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter) {
Robert Carrf59b8dd2017-10-02 18:58:36 -0700148 mHost = host;
chaviwf20bd222018-02-01 16:06:52 -0800149 mSurfaceAnimatorStarter = surfaceAnimatorStarter;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700150 }
151
chaviw2fb06bc2018-01-19 17:09:15 -0800152 private SurfaceControl makeDimLayer() {
153 return mHost.makeChildSurface(null)
Robert Carrf59b8dd2017-10-02 18:58:36 -0700154 .setParent(mHost.getSurfaceControl())
155 .setColorLayer(true)
156 .setName("Dim Layer for - " + mHost.getName())
157 .build();
Robert Carrf59b8dd2017-10-02 18:58:36 -0700158 }
159
160 /**
chaviwb792a952018-02-02 11:55:21 -0800161 * Retrieve the DimState, creating one if it doesn't exist.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700162 */
chaviw2fb06bc2018-01-19 17:09:15 -0800163 private DimState getDimState(WindowContainer container) {
chaviwb792a952018-02-02 11:55:21 -0800164 if (mDimState == null) {
Robert Carrf59b8dd2017-10-02 18:58:36 -0700165 final SurfaceControl ctl = makeDimLayer();
chaviwb792a952018-02-02 11:55:21 -0800166 mDimState = new DimState(ctl);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700167 /**
168 * See documentation on {@link #dimAbove} to understand lifecycle management of Dim's
169 * via state resetting for Dim's with containers.
170 */
171 if (container == null) {
chaviwb792a952018-02-02 11:55:21 -0800172 mDimState.mDontReset = true;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700173 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700174 }
chaviwb792a952018-02-02 11:55:21 -0800175
176 mLastRequestedDimContainer = container;
177 return mDimState;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700178 }
179
180 private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
181 float alpha) {
182 final DimState d = getDimState(container);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700183 if (container != null) {
chaviwb792a952018-02-02 11:55:21 -0800184 // The dim method is called from WindowState.prepareSurfaces(), which is always called
185 // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
186 // relative to the highest Z layer with a dim.
chaviw2fb06bc2018-01-19 17:09:15 -0800187 t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700188 } else {
chaviw2fb06bc2018-01-19 17:09:15 -0800189 t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700190 }
chaviw2fb06bc2018-01-19 17:09:15 -0800191 t.setAlpha(d.mDimLayer, alpha);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700192
193 d.mDimming = true;
194 }
195
196 /**
197 * Finish a dim started by dimAbove in the case there was no call to dimAbove.
198 *
199 * @param t A Transaction in which to finish the dim.
200 */
201 void stopDim(SurfaceControl.Transaction t) {
202 DimState d = getDimState(null);
chaviw2fb06bc2018-01-19 17:09:15 -0800203 t.hide(d.mDimLayer);
204 d.isVisible = false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700205 d.mDontReset = false;
206 }
chaviw2fb06bc2018-01-19 17:09:15 -0800207
Robert Carrf59b8dd2017-10-02 18:58:36 -0700208 /**
209 * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
210 * remove this effect. If the Dim can be assosciated with a particular child of the host
211 * consider using the other variant of dimAbove which ties the Dim lifetime to the child
212 * lifetime more explicitly.
213 *
chaviw2fb06bc2018-01-19 17:09:15 -0800214 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700215 * @param alpha The alpha at which to Dim.
216 */
217 void dimAbove(SurfaceControl.Transaction t, float alpha) {
218 dim(t, null, 1, alpha);
219 }
220
221 /**
222 * Place a dim above the given container, which should be a child of the host container.
223 * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
224 * and the child should call dimAbove again to request the Dim to continue.
225 *
chaviw2fb06bc2018-01-19 17:09:15 -0800226 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700227 * @param container The container which to dim above. Should be a child of our host.
chaviw2fb06bc2018-01-19 17:09:15 -0800228 * @param alpha The alpha at which to Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700229 */
230 void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
231 dim(t, container, 1, alpha);
232 }
233
234 /**
235 * Like {@link #dimAbove} but places the dim below the given container.
236 *
chaviw2fb06bc2018-01-19 17:09:15 -0800237 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700238 * @param container The container which to dim below. Should be a child of our host.
chaviw2fb06bc2018-01-19 17:09:15 -0800239 * @param alpha The alpha at which to Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700240 */
241
242 void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
243 dim(t, container, -1, alpha);
244 }
245
246 /**
247 * Mark all dims as pending completion on the next call to {@link #updateDims}
248 *
249 * This is intended for us by the host container, to be called at the beginning of
250 * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
251 * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
252 * a chance to request dims to continue.
253 */
254 void resetDimStates() {
chaviwb792a952018-02-02 11:55:21 -0800255 if (mDimState != null && !mDimState.mDontReset) {
256 mDimState.mDimming = false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700257 }
258 }
259
260 /**
261 * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
262 * described in {@link #resetDimStates}.
263 *
chaviw2fb06bc2018-01-19 17:09:15 -0800264 * @param t A transaction in which to update the dims.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700265 * @param bounds The bounds at which to dim.
266 * @return true if any Dims were updated.
267 */
268 boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
chaviwb792a952018-02-02 11:55:21 -0800269 if (mDimState == null) {
270 return false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700271 }
chaviwb792a952018-02-02 11:55:21 -0800272
273 if (!mDimState.mDimming) {
274 startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
275 mDimState = null;
276 return false;
277 } else {
278 // TODO: Once we use geometry from hierarchy this falls away.
279 t.setSize(mDimState.mDimLayer, bounds.width(), bounds.height());
280 t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
281 if (!mDimState.isVisible) {
282 mDimState.isVisible = true;
283 t.show(mDimState.mDimLayer);
284 startDimEnter(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
285 }
286 return true;
287 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700288 }
chaviw2fb06bc2018-01-19 17:09:15 -0800289
290 private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
291 SurfaceControl.Transaction t) {
292 startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
293 }
294
295 private void startDimExit(WindowContainer container, SurfaceAnimator animator,
296 SurfaceControl.Transaction t) {
297 startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
298 }
299
300 private void startAnim(WindowContainer container, SurfaceAnimator animator,
301 SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
chaviwf20bd222018-02-01 16:06:52 -0800302 mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
chaviw2fb06bc2018-01-19 17:09:15 -0800303 new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
304 mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
305 }
306
307 private long getDimDuration(WindowContainer container) {
308 // If there's no container, then there isn't an animation occurring while dimming. Set the
309 // duration to 0 so it immediately dims to the set alpha.
310 if (container == null) {
311 return 0;
312 }
313
314 // Otherwise use the same duration as the animation on the WindowContainer
315 AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
316 return animationAdapter == null ? DEFAULT_DIM_ANIM_DURATION
317 : animationAdapter.getDurationHint();
318 }
319
320 private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
321 private final long mDuration;
322 private final float mFromAlpha;
323 private final float mToAlpha;
324
325 AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
326 mFromAlpha = fromAlpha;
327 mToAlpha = toAlpha;
328 mDuration = duration;
329 }
330
331 @Override
332 public long getDuration() {
333 return mDuration;
334 }
335
336 @Override
337 public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
338 float alpha = ((float) currentPlayTime / getDuration()) * (mToAlpha - mFromAlpha)
339 + mFromAlpha;
340 t.setAlpha(sc, alpha);
341 }
342 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700343}