blob: b435605b654f6bcf37ff3e8978ee8332b046cdfb [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
19import android.util.ArrayMap;
Robert Carrf59b8dd2017-10-02 18:58:36 -070020import android.view.SurfaceControl;
21import android.graphics.Rect;
22
chaviw2fb06bc2018-01-19 17:09:15 -080023import com.android.internal.annotations.VisibleForTesting;
24
Robert Carrf59b8dd2017-10-02 18:58:36 -070025/**
26 * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
27 * black layers of varying opacity at various Z-levels which create the effect of a Dim.
28 */
29class Dimmer {
30 private static final String TAG = "WindowManager";
chaviw2fb06bc2018-01-19 17:09:15 -080031 private static final int DEFAULT_DIM_ANIM_DURATION = 200;
Robert Carrf59b8dd2017-10-02 18:58:36 -070032
chaviw2fb06bc2018-01-19 17:09:15 -080033 private class DimAnimatable implements SurfaceAnimator.Animatable {
34 private final SurfaceControl mDimLayer;
35
36 private DimAnimatable(SurfaceControl dimLayer) {
37 mDimLayer = dimLayer;
38 }
39
40 @Override
41 public SurfaceControl.Transaction getPendingTransaction() {
42 return mHost.getPendingTransaction();
43 }
44
45 @Override
46 public void commitPendingTransaction() {
47 mHost.commitPendingTransaction();
48 }
49
50 @Override
51 public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
52 }
53
54 @Override
55 public void onAnimationLeashDestroyed(SurfaceControl.Transaction t) {
56 }
57
58 @Override
59 public void destroyAfterPendingTransaction(SurfaceControl surface) {
60 mHost.destroyAfterPendingTransaction(surface);
61 }
62
63 @Override
64 public SurfaceControl.Builder makeAnimationLeash() {
65 return mHost.makeAnimationLeash();
66 }
67
68 @Override
69 public SurfaceControl getAnimationLeashParent() {
70 return mHost.getSurfaceControl();
71 }
72
73 @Override
74 public SurfaceControl getSurfaceControl() {
75 return mDimLayer;
76 }
77
78 @Override
79 public SurfaceControl getParentSurfaceControl() {
80 return mHost.getSurfaceControl();
81 }
82
83 @Override
84 public int getSurfaceWidth() {
85 // This will determine the size of the leash created. This should be the size of the
86 // host and not the dim layer since the dim layer may get bigger during animation. If
87 // that occurs, the leash size cannot change so we need to ensure the leash is big
88 // enough that the dim layer can grow.
89 // This works because the mHost will be a Task which has the display bounds.
90 return mHost.getSurfaceWidth();
91 }
92
93 @Override
94 public int getSurfaceHeight() {
95 // See getSurfaceWidth() above for explanation.
96 return mHost.getSurfaceHeight();
97 }
98 }
99
100 @VisibleForTesting
101 class DimState {
102 /**
103 * The layer where property changes should be invoked on.
104 */
105 SurfaceControl mDimLayer;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700106 boolean mDimming;
chaviw2fb06bc2018-01-19 17:09:15 -0800107 boolean isVisible;
108 SurfaceAnimator mSurfaceAnimator;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700109
110 /**
chaviw2fb06bc2018-01-19 17:09:15 -0800111 * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
Robert Carrf59b8dd2017-10-02 18:58:36 -0700112 * details on Dim lifecycle.
113 */
114 boolean mDontReset;
115
chaviw2fb06bc2018-01-19 17:09:15 -0800116 DimState(SurfaceControl dimLayer) {
117 mDimLayer = dimLayer;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700118 mDimming = true;
chaviw2fb06bc2018-01-19 17:09:15 -0800119 mSurfaceAnimator = new SurfaceAnimator(new DimAnimatable(dimLayer), () -> {
120 if (!mDimming) {
121 mDimLayer.destroy();
122 }
123 }, mHost.mService.mAnimator::addAfterPrepareSurfacesRunnable, mHost.mService);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700124 }
chaviw2fb06bc2018-01-19 17:09:15 -0800125 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700126
chaviw2fb06bc2018-01-19 17:09:15 -0800127 @VisibleForTesting
128 ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>();
Robert Carrf59b8dd2017-10-02 18:58:36 -0700129
130 /**
131 * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
132 * host, some controller of it, or one of the hosts children.
133 */
134 private WindowContainer mHost;
135
136 Dimmer(WindowContainer host) {
137 mHost = host;
138 }
139
chaviw2fb06bc2018-01-19 17:09:15 -0800140 private SurfaceControl makeDimLayer() {
141 return mHost.makeChildSurface(null)
Robert Carrf59b8dd2017-10-02 18:58:36 -0700142 .setParent(mHost.getSurfaceControl())
143 .setColorLayer(true)
144 .setName("Dim Layer for - " + mHost.getName())
145 .build();
Robert Carrf59b8dd2017-10-02 18:58:36 -0700146 }
147
148 /**
149 * Retreive the DimState for a given child of the host.
150 */
chaviw2fb06bc2018-01-19 17:09:15 -0800151 private DimState getDimState(WindowContainer container) {
Robert Carrf59b8dd2017-10-02 18:58:36 -0700152 DimState state = mDimLayerUsers.get(container);
153 if (state == null) {
154 final SurfaceControl ctl = makeDimLayer();
155 state = new DimState(ctl);
156 /**
157 * See documentation on {@link #dimAbove} to understand lifecycle management of Dim's
158 * via state resetting for Dim's with containers.
159 */
160 if (container == null) {
161 state.mDontReset = true;
162 }
163 mDimLayerUsers.put(container, state);
164 }
165 return state;
166 }
167
168 private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
169 float alpha) {
170 final DimState d = getDimState(container);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700171 if (container != null) {
chaviw2fb06bc2018-01-19 17:09:15 -0800172 t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700173 } else {
chaviw2fb06bc2018-01-19 17:09:15 -0800174 t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700175 }
chaviw2fb06bc2018-01-19 17:09:15 -0800176 t.setAlpha(d.mDimLayer, alpha);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700177
178 d.mDimming = true;
179 }
180
181 /**
182 * Finish a dim started by dimAbove in the case there was no call to dimAbove.
183 *
184 * @param t A Transaction in which to finish the dim.
185 */
186 void stopDim(SurfaceControl.Transaction t) {
187 DimState d = getDimState(null);
chaviw2fb06bc2018-01-19 17:09:15 -0800188 t.hide(d.mDimLayer);
189 d.isVisible = false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700190 d.mDontReset = false;
191 }
chaviw2fb06bc2018-01-19 17:09:15 -0800192
Robert Carrf59b8dd2017-10-02 18:58:36 -0700193 /**
194 * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
195 * remove this effect. If the Dim can be assosciated with a particular child of the host
196 * consider using the other variant of dimAbove which ties the Dim lifetime to the child
197 * lifetime more explicitly.
198 *
chaviw2fb06bc2018-01-19 17:09:15 -0800199 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700200 * @param alpha The alpha at which to Dim.
201 */
202 void dimAbove(SurfaceControl.Transaction t, float alpha) {
203 dim(t, null, 1, alpha);
204 }
205
206 /**
207 * Place a dim above the given container, which should be a child of the host container.
208 * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
209 * and the child should call dimAbove again to request the Dim to continue.
210 *
chaviw2fb06bc2018-01-19 17:09:15 -0800211 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700212 * @param container The container which to dim above. Should be a child of our host.
chaviw2fb06bc2018-01-19 17:09:15 -0800213 * @param alpha The alpha at which to Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700214 */
215 void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
216 dim(t, container, 1, alpha);
217 }
218
219 /**
220 * Like {@link #dimAbove} but places the dim below the given container.
221 *
chaviw2fb06bc2018-01-19 17:09:15 -0800222 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700223 * @param container The container which to dim below. Should be a child of our host.
chaviw2fb06bc2018-01-19 17:09:15 -0800224 * @param alpha The alpha at which to Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700225 */
226
227 void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
228 dim(t, container, -1, alpha);
229 }
230
231 /**
232 * Mark all dims as pending completion on the next call to {@link #updateDims}
233 *
234 * This is intended for us by the host container, to be called at the beginning of
235 * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
236 * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
237 * a chance to request dims to continue.
238 */
239 void resetDimStates() {
240 for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
241 final DimState state = mDimLayerUsers.valueAt(i);
chaviw2fb06bc2018-01-19 17:09:15 -0800242 if (!state.mDontReset) {
Robert Carrf59b8dd2017-10-02 18:58:36 -0700243 state.mDimming = false;
244 }
245 }
246 }
247
248 /**
249 * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
250 * described in {@link #resetDimStates}.
251 *
chaviw2fb06bc2018-01-19 17:09:15 -0800252 * @param t A transaction in which to update the dims.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700253 * @param bounds The bounds at which to dim.
254 * @return true if any Dims were updated.
255 */
256 boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
257 boolean didSomething = false;
258 for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
259 DimState state = mDimLayerUsers.valueAt(i);
chaviw2fb06bc2018-01-19 17:09:15 -0800260 WindowContainer container = mDimLayerUsers.keyAt(i);
261
Robert Carrf59b8dd2017-10-02 18:58:36 -0700262 // TODO: We want to animate the addition and removal of Dim's instead of immediately
263 // acting. When we do this we need to take care to account for the "Replacing Windows"
264 // case (and seamless dim transfer).
chaviw2fb06bc2018-01-19 17:09:15 -0800265 if (!state.mDimming) {
Robert Carrf59b8dd2017-10-02 18:58:36 -0700266 mDimLayerUsers.removeAt(i);
chaviw2fb06bc2018-01-19 17:09:15 -0800267 startDimExit(container, state.mSurfaceAnimator, t);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700268 } else {
269 didSomething = true;
270 // TODO: Once we use geometry from hierarchy this falls away.
chaviw2fb06bc2018-01-19 17:09:15 -0800271 t.setSize(state.mDimLayer, bounds.width(), bounds.height());
272 t.setPosition(state.mDimLayer, bounds.left, bounds.top);
273 if (!state.isVisible) {
274 state.isVisible = true;
275 t.show(state.mDimLayer);
276 startDimEnter(container, state.mSurfaceAnimator, t);
277 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700278 }
279 }
280 return didSomething;
281 }
chaviw2fb06bc2018-01-19 17:09:15 -0800282
283 private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
284 SurfaceControl.Transaction t) {
285 startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
286 }
287
288 private void startDimExit(WindowContainer container, SurfaceAnimator animator,
289 SurfaceControl.Transaction t) {
290 startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
291 }
292
293 private void startAnim(WindowContainer container, SurfaceAnimator animator,
294 SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
295 animator.startAnimation(t, new LocalAnimationAdapter(
296 new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
297 mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
298 }
299
300 private long getDimDuration(WindowContainer container) {
301 // If there's no container, then there isn't an animation occurring while dimming. Set the
302 // duration to 0 so it immediately dims to the set alpha.
303 if (container == null) {
304 return 0;
305 }
306
307 // Otherwise use the same duration as the animation on the WindowContainer
308 AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
309 return animationAdapter == null ? DEFAULT_DIM_ANIM_DURATION
310 : animationAdapter.getDurationHint();
311 }
312
313 private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
314 private final long mDuration;
315 private final float mFromAlpha;
316 private final float mToAlpha;
317
318 AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
319 mFromAlpha = fromAlpha;
320 mToAlpha = toAlpha;
321 mDuration = duration;
322 }
323
324 @Override
325 public long getDuration() {
326 return mDuration;
327 }
328
329 @Override
330 public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
331 float alpha = ((float) currentPlayTime / getDuration()) * (mToAlpha - mFromAlpha)
332 + mFromAlpha;
333 t.setAlpha(sc, alpha);
334 }
335 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700336}