blob: 1f95868de79ec668ed251e99826878293b551c28 [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
Yi Jin6c6e9ca2018-03-20 16:53:35 -070019import static com.android.server.wm.AlphaAnimationSpecProto.DURATION;
20import 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";
chaviw2fb06bc2018-01-19 17:09:15 -080038 private static final int DEFAULT_DIM_ANIM_DURATION = 200;
Robert Carrf59b8dd2017-10-02 18:58:36 -070039
chaviw2fb06bc2018-01-19 17:09:15 -080040 private class DimAnimatable implements SurfaceAnimator.Animatable {
41 private final SurfaceControl mDimLayer;
42
43 private DimAnimatable(SurfaceControl dimLayer) {
44 mDimLayer = dimLayer;
45 }
46
47 @Override
48 public SurfaceControl.Transaction getPendingTransaction() {
49 return mHost.getPendingTransaction();
50 }
51
52 @Override
53 public void commitPendingTransaction() {
54 mHost.commitPendingTransaction();
55 }
56
57 @Override
58 public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
59 }
60
61 @Override
62 public void onAnimationLeashDestroyed(SurfaceControl.Transaction t) {
63 }
64
65 @Override
chaviw2fb06bc2018-01-19 17:09:15 -080066 public SurfaceControl.Builder makeAnimationLeash() {
67 return mHost.makeAnimationLeash();
68 }
69
70 @Override
71 public SurfaceControl getAnimationLeashParent() {
72 return mHost.getSurfaceControl();
73 }
74
75 @Override
76 public SurfaceControl getSurfaceControl() {
77 return mDimLayer;
78 }
79
80 @Override
81 public SurfaceControl getParentSurfaceControl() {
82 return mHost.getSurfaceControl();
83 }
84
85 @Override
86 public int getSurfaceWidth() {
87 // This will determine the size of the leash created. This should be the size of the
88 // host and not the dim layer since the dim layer may get bigger during animation. If
89 // that occurs, the leash size cannot change so we need to ensure the leash is big
90 // enough that the dim layer can grow.
91 // This works because the mHost will be a Task which has the display bounds.
92 return mHost.getSurfaceWidth();
93 }
94
95 @Override
96 public int getSurfaceHeight() {
97 // See getSurfaceWidth() above for explanation.
98 return mHost.getSurfaceHeight();
99 }
100 }
101
102 @VisibleForTesting
103 class DimState {
104 /**
105 * The layer where property changes should be invoked on.
106 */
107 SurfaceControl mDimLayer;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700108 boolean mDimming;
chaviw2fb06bc2018-01-19 17:09:15 -0800109 boolean isVisible;
110 SurfaceAnimator mSurfaceAnimator;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700111
112 /**
chaviw87ca63a2018-03-26 14:06:17 -0700113 * Determines whether the dim layer should animate before destroying.
114 */
115 boolean mAnimateExit = true;
116
117 /**
chaviw2fb06bc2018-01-19 17:09:15 -0800118 * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
Robert Carrf59b8dd2017-10-02 18:58:36 -0700119 * details on Dim lifecycle.
120 */
121 boolean mDontReset;
122
chaviw2fb06bc2018-01-19 17:09:15 -0800123 DimState(SurfaceControl dimLayer) {
124 mDimLayer = dimLayer;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700125 mDimming = true;
chaviw2fb06bc2018-01-19 17:09:15 -0800126 mSurfaceAnimator = new SurfaceAnimator(new DimAnimatable(dimLayer), () -> {
127 if (!mDimming) {
128 mDimLayer.destroy();
129 }
Chavi Weingartenb736e322018-02-23 00:27:54 +0000130 }, mHost.mService);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700131 }
chaviw2fb06bc2018-01-19 17:09:15 -0800132 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700133
Robert Carrf59b8dd2017-10-02 18:58:36 -0700134 /**
135 * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
136 * host, some controller of it, or one of the hosts children.
137 */
138 private WindowContainer mHost;
chaviwb792a952018-02-02 11:55:21 -0800139 private WindowContainer mLastRequestedDimContainer;
chaviwf20bd222018-02-01 16:06:52 -0800140 @VisibleForTesting
141 DimState mDimState;
142
143 private final SurfaceAnimatorStarter mSurfaceAnimatorStarter;
144
145 @VisibleForTesting
146 interface SurfaceAnimatorStarter {
147 void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
148 AnimationAdapter anim, boolean hidden);
149 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700150
151 Dimmer(WindowContainer host) {
chaviwf20bd222018-02-01 16:06:52 -0800152 this(host, SurfaceAnimator::startAnimation);
153 }
154
155 Dimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter) {
Robert Carrf59b8dd2017-10-02 18:58:36 -0700156 mHost = host;
chaviwf20bd222018-02-01 16:06:52 -0800157 mSurfaceAnimatorStarter = surfaceAnimatorStarter;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700158 }
159
chaviw2fb06bc2018-01-19 17:09:15 -0800160 private SurfaceControl makeDimLayer() {
161 return mHost.makeChildSurface(null)
Robert Carrf59b8dd2017-10-02 18:58:36 -0700162 .setParent(mHost.getSurfaceControl())
163 .setColorLayer(true)
164 .setName("Dim Layer for - " + mHost.getName())
165 .build();
Robert Carrf59b8dd2017-10-02 18:58:36 -0700166 }
167
168 /**
chaviwb792a952018-02-02 11:55:21 -0800169 * Retrieve the DimState, creating one if it doesn't exist.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700170 */
chaviw2fb06bc2018-01-19 17:09:15 -0800171 private DimState getDimState(WindowContainer container) {
chaviwb792a952018-02-02 11:55:21 -0800172 if (mDimState == null) {
Robert Carrf59b8dd2017-10-02 18:58:36 -0700173 final SurfaceControl ctl = makeDimLayer();
chaviwb792a952018-02-02 11:55:21 -0800174 mDimState = new DimState(ctl);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700175 /**
176 * See documentation on {@link #dimAbove} to understand lifecycle management of Dim's
177 * via state resetting for Dim's with containers.
178 */
179 if (container == null) {
chaviwb792a952018-02-02 11:55:21 -0800180 mDimState.mDontReset = true;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700181 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700182 }
chaviwb792a952018-02-02 11:55:21 -0800183
184 mLastRequestedDimContainer = container;
185 return mDimState;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700186 }
187
188 private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
189 float alpha) {
190 final DimState d = getDimState(container);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700191 if (container != null) {
chaviwb792a952018-02-02 11:55:21 -0800192 // The dim method is called from WindowState.prepareSurfaces(), which is always called
193 // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
194 // relative to the highest Z layer with a dim.
chaviw2fb06bc2018-01-19 17:09:15 -0800195 t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700196 } else {
chaviw2fb06bc2018-01-19 17:09:15 -0800197 t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700198 }
chaviw2fb06bc2018-01-19 17:09:15 -0800199 t.setAlpha(d.mDimLayer, alpha);
Robert Carrf59b8dd2017-10-02 18:58:36 -0700200
201 d.mDimming = true;
202 }
203
204 /**
205 * Finish a dim started by dimAbove in the case there was no call to dimAbove.
206 *
207 * @param t A Transaction in which to finish the dim.
208 */
209 void stopDim(SurfaceControl.Transaction t) {
210 DimState d = getDimState(null);
chaviw2fb06bc2018-01-19 17:09:15 -0800211 t.hide(d.mDimLayer);
212 d.isVisible = false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700213 d.mDontReset = false;
214 }
chaviw2fb06bc2018-01-19 17:09:15 -0800215
Robert Carrf59b8dd2017-10-02 18:58:36 -0700216 /**
217 * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
218 * remove this effect. If the Dim can be assosciated with a particular child of the host
219 * consider using the other variant of dimAbove which ties the Dim lifetime to the child
220 * lifetime more explicitly.
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 alpha The alpha at which to Dim.
224 */
225 void dimAbove(SurfaceControl.Transaction t, float alpha) {
226 dim(t, null, 1, alpha);
227 }
228
229 /**
230 * Place a dim above the given container, which should be a child of the host container.
231 * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
232 * and the child should call dimAbove again to request the Dim to continue.
233 *
chaviw2fb06bc2018-01-19 17:09:15 -0800234 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700235 * @param container The container which to dim above. Should be a child of our host.
chaviw2fb06bc2018-01-19 17:09:15 -0800236 * @param alpha The alpha at which to Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700237 */
238 void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
239 dim(t, container, 1, alpha);
240 }
241
242 /**
243 * Like {@link #dimAbove} but places the dim below the given container.
244 *
chaviw2fb06bc2018-01-19 17:09:15 -0800245 * @param t A transaction in which to apply the Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700246 * @param container The container which to dim below. Should be a child of our host.
chaviw2fb06bc2018-01-19 17:09:15 -0800247 * @param alpha The alpha at which to Dim.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700248 */
249
250 void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
251 dim(t, container, -1, alpha);
252 }
253
254 /**
255 * Mark all dims as pending completion on the next call to {@link #updateDims}
256 *
257 * This is intended for us by the host container, to be called at the beginning of
258 * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
259 * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
260 * a chance to request dims to continue.
261 */
262 void resetDimStates() {
chaviwb792a952018-02-02 11:55:21 -0800263 if (mDimState != null && !mDimState.mDontReset) {
264 mDimState.mDimming = false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700265 }
266 }
267
chaviw87ca63a2018-03-26 14:06:17 -0700268 void dontAnimateExit() {
269 if (mDimState != null) {
270 mDimState.mAnimateExit = false;
271 }
272 }
273
Robert Carrf59b8dd2017-10-02 18:58:36 -0700274 /**
275 * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
276 * described in {@link #resetDimStates}.
277 *
chaviw2fb06bc2018-01-19 17:09:15 -0800278 * @param t A transaction in which to update the dims.
Robert Carrf59b8dd2017-10-02 18:58:36 -0700279 * @param bounds The bounds at which to dim.
280 * @return true if any Dims were updated.
281 */
282 boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
chaviwb792a952018-02-02 11:55:21 -0800283 if (mDimState == null) {
284 return false;
Robert Carrf59b8dd2017-10-02 18:58:36 -0700285 }
chaviwb792a952018-02-02 11:55:21 -0800286
287 if (!mDimState.mDimming) {
chaviw87ca63a2018-03-26 14:06:17 -0700288 if (!mDimState.mAnimateExit) {
289 t.destroy(mDimState.mDimLayer);
290 } else {
291 startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
292 }
chaviwb792a952018-02-02 11:55:21 -0800293 mDimState = null;
294 return false;
295 } else {
296 // TODO: Once we use geometry from hierarchy this falls away.
297 t.setSize(mDimState.mDimLayer, bounds.width(), bounds.height());
298 t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
299 if (!mDimState.isVisible) {
300 mDimState.isVisible = true;
301 t.show(mDimState.mDimLayer);
302 startDimEnter(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
303 }
304 return true;
305 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700306 }
chaviw2fb06bc2018-01-19 17:09:15 -0800307
308 private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
309 SurfaceControl.Transaction t) {
310 startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
311 }
312
313 private void startDimExit(WindowContainer container, SurfaceAnimator animator,
314 SurfaceControl.Transaction t) {
315 startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
316 }
317
318 private void startAnim(WindowContainer container, SurfaceAnimator animator,
319 SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
chaviwf20bd222018-02-01 16:06:52 -0800320 mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
chaviw2fb06bc2018-01-19 17:09:15 -0800321 new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
322 mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
323 }
324
325 private long getDimDuration(WindowContainer container) {
326 // If there's no container, then there isn't an animation occurring while dimming. Set the
327 // duration to 0 so it immediately dims to the set alpha.
328 if (container == null) {
329 return 0;
330 }
331
332 // Otherwise use the same duration as the animation on the WindowContainer
333 AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
334 return animationAdapter == null ? DEFAULT_DIM_ANIM_DURATION
335 : animationAdapter.getDurationHint();
336 }
337
338 private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
339 private final long mDuration;
340 private final float mFromAlpha;
341 private final float mToAlpha;
342
343 AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
344 mFromAlpha = fromAlpha;
345 mToAlpha = toAlpha;
346 mDuration = duration;
347 }
348
349 @Override
350 public long getDuration() {
351 return mDuration;
352 }
353
354 @Override
355 public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
356 float alpha = ((float) currentPlayTime / getDuration()) * (mToAlpha - mFromAlpha)
357 + mFromAlpha;
358 t.setAlpha(sc, alpha);
359 }
Jorim Jaggif75d1612018-02-27 15:05:21 +0100360
361 @Override
362 public void dump(PrintWriter pw, String prefix) {
363 pw.print(prefix); pw.print("from="); pw.print(mFromAlpha);
364 pw.print(" to="); pw.print(mToAlpha);
365 pw.print(" duration="); pw.println(mDuration);
366 }
367
368 @Override
369 public void writeToProtoInner(ProtoOutputStream proto) {
370 final long token = proto.start(ALPHA);
371 proto.write(FROM, mFromAlpha);
372 proto.write(TO, mToAlpha);
373 proto.write(DURATION, mDuration);
374 proto.end(token);
375 }
chaviw2fb06bc2018-01-19 17:09:15 -0800376 }
Robert Carrf59b8dd2017-10-02 18:58:36 -0700377}