blob: cc14afce7ff6f61ddc0dd82f6f5d0979eccf886b [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;
chaviw78a3d4692018-11-13 18:06:56 -0800129 final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer);
130 mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, () -> {
chaviw2fb06bc2018-01-19 17:09:15 -0800131 if (!mDimming) {
chaviw78a3d4692018-11-13 18:06:56 -0800132 dimAnimatable.getPendingTransaction().destroy(mDimLayer);
chaviw2fb06bc2018-01-19 17:09:15 -0800133 }
Chavi Weingartenb736e322018-02-23 00:27:54 +0000134 }, mHost.mService);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700135 }
chaviw2fb06bc2018-01-19 17:09:15 -0800136 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700137
Robert Carrf59b8dd2017-10-02 18:58:36 -0700138 /**
139 * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
140 * host, some controller of it, or one of the hosts children.
141 */
142 private WindowContainer mHost;
chaviwb792a952018-02-02 11:55:21 -0800143 private WindowContainer mLastRequestedDimContainer;
chaviwf20bd222018-02-01 16:06:52 -0800144 @VisibleForTesting
145 DimState mDimState;
146
147 private final SurfaceAnimatorStarter mSurfaceAnimatorStarter;
148
149 @VisibleForTesting
150 interface SurfaceAnimatorStarter {
151 void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
152 AnimationAdapter anim, boolean hidden);
153 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700154
155 Dimmer(WindowContainer host) {
chaviwf20bd222018-02-01 16:06:52 -0800156 this(host, SurfaceAnimator::startAnimation);
157 }
158
159 Dimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter) {
Robert Carrf59b8dd2017-10-02 18:58:36 -0700160 mHost = host;
chaviwf20bd222018-02-01 16:06:52 -0800161 mSurfaceAnimatorStarter = surfaceAnimatorStarter;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700162 }
163
chaviw2fb06bc2018-01-19 17:09:15 -0800164 private SurfaceControl makeDimLayer() {
165 return mHost.makeChildSurface(null)
Robert Carrf59b8dd2017-10-02 18:58:36 -0700166 .setParent(mHost.getSurfaceControl())
167 .setColorLayer(true)
168 .setName("Dim Layer for - " + mHost.getName())
169 .build();
Robert Carrf59b8dd2017-10-02 18:58:36 -0700170 }
171
172 /**
chaviwb792a952018-02-02 11:55:21 -0800173 * Retrieve the DimState, creating one if it doesn't exist.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700174 */
chaviw2fb06bc2018-01-19 17:09:15 -0800175 private DimState getDimState(WindowContainer container) {
chaviwb792a952018-02-02 11:55:21 -0800176 if (mDimState == null) {
chaviwc5eb8162018-05-25 16:18:42 -0700177 try {
178 final SurfaceControl ctl = makeDimLayer();
179 mDimState = new DimState(ctl);
180 /**
181 * See documentation on {@link #dimAbove} to understand lifecycle management of
182 * Dim's via state resetting for Dim's with containers.
183 */
184 if (container == null) {
185 mDimState.mDontReset = true;
186 }
187 } catch (Surface.OutOfResourcesException e) {
188 Log.w(TAG, "OutOfResourcesException creating dim surface");
Robert Carrf59b8dd2017-10-02 18:58:36 -0700189 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700190 }
chaviwb792a952018-02-02 11:55:21 -0800191
192 mLastRequestedDimContainer = container;
193 return mDimState;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700194 }
195
196 private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
197 float alpha) {
198 final DimState d = getDimState(container);
chaviwc5eb8162018-05-25 16:18:42 -0700199
200 if (d == null) {
201 return;
202 }
203
Robert Carrf59b8dd2017-10-02 18:58:36 -0700204 if (container != null) {
chaviwb792a952018-02-02 11:55:21 -0800205 // The dim method is called from WindowState.prepareSurfaces(), which is always called
206 // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
207 // relative to the highest Z layer with a dim.
chaviw2fb06bc2018-01-19 17:09:15 -0800208 t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700209 } else {
chaviw2fb06bc2018-01-19 17:09:15 -0800210 t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700211 }
chaviw2fb06bc2018-01-19 17:09:15 -0800212 t.setAlpha(d.mDimLayer, alpha);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700213
214 d.mDimming = true;
215 }
216
217 /**
218 * Finish a dim started by dimAbove in the case there was no call to dimAbove.
219 *
220 * @param t A Transaction in which to finish the dim.
221 */
222 void stopDim(SurfaceControl.Transaction t) {
chaviwc5eb8162018-05-25 16:18:42 -0700223 if (mDimState != null) {
224 t.hide(mDimState.mDimLayer);
225 mDimState.isVisible = false;
226 mDimState.mDontReset = false;
227 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700228 }
chaviw2fb06bc2018-01-19 17:09:15 -0800229
Robert Carrf59b8dd2017-10-02 18:58:36 -0700230 /**
231 * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
232 * remove this effect. If the Dim can be assosciated with a particular child of the host
233 * consider using the other variant of dimAbove which ties the Dim lifetime to the child
234 * lifetime more explicitly.
235 *
chaviw2fb06bc2018-01-19 17:09:15 -0800236 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700237 * @param alpha The alpha at which to Dim.
238 */
239 void dimAbove(SurfaceControl.Transaction t, float alpha) {
240 dim(t, null, 1, alpha);
241 }
242
243 /**
244 * Place a dim above the given container, which should be a child of the host container.
245 * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
246 * and the child should call dimAbove again to request the Dim to continue.
247 *
chaviw2fb06bc2018-01-19 17:09:15 -0800248 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700249 * @param container The container which to dim above. Should be a child of our host.
chaviw2fb06bc2018-01-19 17:09:15 -0800250 * @param alpha The alpha at which to Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700251 */
252 void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
253 dim(t, container, 1, alpha);
254 }
255
256 /**
257 * Like {@link #dimAbove} but places the dim below the given container.
258 *
chaviw2fb06bc2018-01-19 17:09:15 -0800259 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700260 * @param container The container which to dim below. Should be a child of our host.
chaviw2fb06bc2018-01-19 17:09:15 -0800261 * @param alpha The alpha at which to Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700262 */
263
264 void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
265 dim(t, container, -1, alpha);
266 }
267
268 /**
269 * Mark all dims as pending completion on the next call to {@link #updateDims}
270 *
271 * This is intended for us by the host container, to be called at the beginning of
272 * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
273 * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
274 * a chance to request dims to continue.
275 */
276 void resetDimStates() {
chaviwb792a952018-02-02 11:55:21 -0800277 if (mDimState != null && !mDimState.mDontReset) {
278 mDimState.mDimming = false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700279 }
280 }
281
chaviw87ca63a2018-03-26 14:06:17 -0700282 void dontAnimateExit() {
283 if (mDimState != null) {
284 mDimState.mAnimateExit = false;
285 }
286 }
287
Robert Carrf59b8dd2017-10-02 18:58:36 -0700288 /**
289 * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
290 * described in {@link #resetDimStates}.
291 *
chaviw2fb06bc2018-01-19 17:09:15 -0800292 * @param t A transaction in which to update the dims.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700293 * @param bounds The bounds at which to dim.
294 * @return true if any Dims were updated.
295 */
296 boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
chaviwb792a952018-02-02 11:55:21 -0800297 if (mDimState == null) {
298 return false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700299 }
chaviwb792a952018-02-02 11:55:21 -0800300
301 if (!mDimState.mDimming) {
chaviw87ca63a2018-03-26 14:06:17 -0700302 if (!mDimState.mAnimateExit) {
303 t.destroy(mDimState.mDimLayer);
304 } else {
305 startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
306 }
chaviwb792a952018-02-02 11:55:21 -0800307 mDimState = null;
308 return false;
309 } else {
310 // TODO: Once we use geometry from hierarchy this falls away.
311 t.setSize(mDimState.mDimLayer, bounds.width(), bounds.height());
312 t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
Vishnu Naird454442d2018-11-13 13:51:01 -0800313 t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
chaviwb792a952018-02-02 11:55:21 -0800314 if (!mDimState.isVisible) {
315 mDimState.isVisible = true;
316 t.show(mDimState.mDimLayer);
317 startDimEnter(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
318 }
319 return true;
320 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700321 }
chaviw2fb06bc2018-01-19 17:09:15 -0800322
323 private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
324 SurfaceControl.Transaction t) {
325 startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
326 }
327
328 private void startDimExit(WindowContainer container, SurfaceAnimator animator,
329 SurfaceControl.Transaction t) {
330 startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
331 }
332
333 private void startAnim(WindowContainer container, SurfaceAnimator animator,
334 SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
chaviwf20bd222018-02-01 16:06:52 -0800335 mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
chaviw2fb06bc2018-01-19 17:09:15 -0800336 new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
337 mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
338 }
339
340 private long getDimDuration(WindowContainer container) {
341 // If there's no container, then there isn't an animation occurring while dimming. Set the
342 // duration to 0 so it immediately dims to the set alpha.
343 if (container == null) {
344 return 0;
345 }
346
347 // Otherwise use the same duration as the animation on the WindowContainer
348 AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
349 return animationAdapter == null ? DEFAULT_DIM_ANIM_DURATION
350 : animationAdapter.getDurationHint();
351 }
352
353 private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
354 private final long mDuration;
355 private final float mFromAlpha;
356 private final float mToAlpha;
357
358 AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
359 mFromAlpha = fromAlpha;
360 mToAlpha = toAlpha;
361 mDuration = duration;
362 }
363
364 @Override
365 public long getDuration() {
366 return mDuration;
367 }
368
369 @Override
370 public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
371 float alpha = ((float) currentPlayTime / getDuration()) * (mToAlpha - mFromAlpha)
372 + mFromAlpha;
373 t.setAlpha(sc, alpha);
374 }
Jorim Jaggif75d1612018-02-27 15:05:21 +0100375
376 @Override
377 public void dump(PrintWriter pw, String prefix) {
378 pw.print(prefix); pw.print("from="); pw.print(mFromAlpha);
379 pw.print(" to="); pw.print(mToAlpha);
380 pw.print(" duration="); pw.println(mDuration);
381 }
382
383 @Override
384 public void writeToProtoInner(ProtoOutputStream proto) {
385 final long token = proto.start(ALPHA);
386 proto.write(FROM, mFromAlpha);
387 proto.write(TO, mToAlpha);
Kweku Adams21b8d262018-03-30 12:19:58 -0700388 proto.write(DURATION_MS, mDuration);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100389 proto.end(token);
390 }
chaviw2fb06bc2018-01-19 17:09:15 -0800391 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700392}