• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server.wm;
18 
19 import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS;
20 import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
21 import static com.android.server.wm.AlphaAnimationSpecProto.TO;
22 import static com.android.server.wm.AnimationSpecProto.ALPHA;
23 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
24 
25 import android.graphics.Rect;
26 import android.util.Log;
27 import android.util.proto.ProtoOutputStream;
28 import android.view.Surface;
29 import android.view.SurfaceControl;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 import com.android.server.wm.SurfaceAnimator.AnimationType;
33 
34 import java.io.PrintWriter;
35 
36 /**
37  * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
38  * black layers of varying opacity at various Z-levels which create the effect of a Dim.
39  */
40 class Dimmer {
41     private static final String TAG = "WindowManager";
42     // This is in milliseconds.
43     private static final int DEFAULT_DIM_ANIM_DURATION = 200;
44 
45     private class DimAnimatable implements SurfaceAnimator.Animatable {
46         private SurfaceControl mDimLayer;
47 
DimAnimatable(SurfaceControl dimLayer)48         private DimAnimatable(SurfaceControl dimLayer) {
49             mDimLayer = dimLayer;
50         }
51 
52         @Override
getPendingTransaction()53         public SurfaceControl.Transaction getPendingTransaction() {
54             return mHost.getSyncTransaction();
55         }
56 
57         @Override
commitPendingTransaction()58         public void commitPendingTransaction() {
59             mHost.commitPendingTransaction();
60         }
61 
62         @Override
onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash)63         public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
64         }
65 
66         @Override
onAnimationLeashLost(SurfaceControl.Transaction t)67         public void onAnimationLeashLost(SurfaceControl.Transaction t) {
68         }
69 
70         @Override
makeAnimationLeash()71         public SurfaceControl.Builder makeAnimationLeash() {
72             return mHost.makeAnimationLeash();
73         }
74 
75         @Override
getAnimationLeashParent()76         public SurfaceControl getAnimationLeashParent() {
77             return mHost.getSurfaceControl();
78         }
79 
80         @Override
getSurfaceControl()81         public SurfaceControl getSurfaceControl() {
82             return mDimLayer;
83         }
84 
85         @Override
getParentSurfaceControl()86         public SurfaceControl getParentSurfaceControl() {
87             return mHost.getSurfaceControl();
88         }
89 
90         @Override
getSurfaceWidth()91         public int getSurfaceWidth() {
92             // This will determine the size of the leash created. This should be the size of the
93             // host and not the dim layer since the dim layer may get bigger during animation. If
94             // that occurs, the leash size cannot change so we need to ensure the leash is big
95             // enough that the dim layer can grow.
96             // This works because the mHost will be a Task which has the display bounds.
97             return mHost.getSurfaceWidth();
98         }
99 
100         @Override
getSurfaceHeight()101         public int getSurfaceHeight() {
102             // See getSurfaceWidth() above for explanation.
103             return mHost.getSurfaceHeight();
104         }
105 
removeSurface()106         void removeSurface() {
107             if (mDimLayer != null && mDimLayer.isValid()) {
108                 getPendingTransaction().remove(mDimLayer);
109             }
110             mDimLayer = null;
111         }
112     }
113 
114     @VisibleForTesting
115     class DimState {
116         /**
117          * The layer where property changes should be invoked on.
118          */
119         SurfaceControl mDimLayer;
120         boolean mDimming;
121         boolean isVisible;
122         SurfaceAnimator mSurfaceAnimator;
123 
124         /**
125          * Determines whether the dim layer should animate before destroying.
126          */
127         boolean mAnimateExit = true;
128 
129         /**
130          * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
131          * details on Dim lifecycle.
132          */
133         boolean mDontReset;
134 
DimState(SurfaceControl dimLayer)135         DimState(SurfaceControl dimLayer) {
136             mDimLayer = dimLayer;
137             mDimming = true;
138             final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer);
139             mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, (type, anim) -> {
140                 if (!mDimming) {
141                     dimAnimatable.removeSurface();
142                 }
143             }, mHost.mWmService);
144         }
145     }
146 
147     /**
148      * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
149      * host, some controller of it, or one of the hosts children.
150      */
151     private WindowContainer mHost;
152     private WindowContainer mLastRequestedDimContainer;
153     @VisibleForTesting
154     DimState mDimState;
155 
156     private final SurfaceAnimatorStarter mSurfaceAnimatorStarter;
157 
158     @VisibleForTesting
159     interface SurfaceAnimatorStarter {
startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t, AnimationAdapter anim, boolean hidden, @AnimationType int type)160         void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
161                 AnimationAdapter anim, boolean hidden, @AnimationType int type);
162     }
163 
Dimmer(WindowContainer host)164     Dimmer(WindowContainer host) {
165         this(host, SurfaceAnimator::startAnimation);
166     }
167 
Dimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter)168     Dimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter) {
169         mHost = host;
170         mSurfaceAnimatorStarter = surfaceAnimatorStarter;
171     }
172 
makeDimLayer()173     private SurfaceControl makeDimLayer() {
174         return mHost.makeChildSurface(null)
175                 .setParent(mHost.getSurfaceControl())
176                 .setColorLayer()
177                 .setName("Dim Layer for - " + mHost.getName())
178                 .setCallsite("Dimmer.makeDimLayer")
179                 .build();
180     }
181 
182     /**
183      * Retrieve the DimState, creating one if it doesn't exist.
184      */
getDimState(WindowContainer container)185     private DimState getDimState(WindowContainer container) {
186         if (mDimState == null) {
187             try {
188                 final SurfaceControl ctl = makeDimLayer();
189                 mDimState = new DimState(ctl);
190                 /**
191                  * See documentation on {@link #dimAbove} to understand lifecycle management of
192                  * Dim's via state resetting for Dim's with containers.
193                  */
194                 if (container == null) {
195                     mDimState.mDontReset = true;
196                 }
197             } catch (Surface.OutOfResourcesException e) {
198                 Log.w(TAG, "OutOfResourcesException creating dim surface");
199             }
200         }
201 
202         mLastRequestedDimContainer = container;
203         return mDimState;
204     }
205 
dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer, float alpha, int blurRadius)206     private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
207             float alpha, int blurRadius) {
208         final DimState d = getDimState(container);
209 
210         if (d == null) {
211             return;
212         }
213 
214         if (container != null) {
215             // The dim method is called from WindowState.prepareSurfaces(), which is always called
216             // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
217             // relative to the highest Z layer with a dim.
218             t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
219         } else {
220             t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
221         }
222         t.setAlpha(d.mDimLayer, alpha);
223         t.setBackgroundBlurRadius(d.mDimLayer, blurRadius);
224 
225         d.mDimming = true;
226     }
227 
228     /**
229      * Finish a dim started by dimAbove in the case there was no call to dimAbove.
230      *
231      * @param t A Transaction in which to finish the dim.
232      */
stopDim(SurfaceControl.Transaction t)233     void stopDim(SurfaceControl.Transaction t) {
234         if (mDimState != null) {
235             t.hide(mDimState.mDimLayer);
236             mDimState.isVisible = false;
237             mDimState.mDontReset = false;
238         }
239     }
240 
241     /**
242      * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
243      * remove this effect. If the Dim can be assosciated with a particular child of the host
244      * consider using the other variant of dimAbove which ties the Dim lifetime to the child
245      * lifetime more explicitly.
246      *
247      * @param t     A transaction in which to apply the Dim.
248      * @param alpha The alpha at which to Dim.
249      */
dimAbove(SurfaceControl.Transaction t, float alpha)250     void dimAbove(SurfaceControl.Transaction t, float alpha) {
251         dim(t, null, 1, alpha, 0);
252     }
253 
254     /**
255      * Place a dim above the given container, which should be a child of the host container.
256      * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
257      * and the child should call dimAbove again to request the Dim to continue.
258      *
259      * @param t         A transaction in which to apply the Dim.
260      * @param container The container which to dim above. Should be a child of our host.
261      * @param alpha     The alpha at which to Dim.
262      */
dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha)263     void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
264         dim(t, container, 1, alpha, 0);
265     }
266 
267     /**
268      * Like {@link #dimAbove} but places the dim below the given container.
269      *
270      * @param t          A transaction in which to apply the Dim.
271      * @param container  The container which to dim below. Should be a child of our host.
272      * @param alpha      The alpha at which to Dim.
273      * @param blurRadius The amount of blur added to the Dim.
274      */
275 
dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha, int blurRadius)276     void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha,
277                   int blurRadius) {
278         dim(t, container, -1, alpha, blurRadius);
279     }
280 
281     /**
282      * Mark all dims as pending completion on the next call to {@link #updateDims}
283      *
284      * This is intended for us by the host container, to be called at the beginning of
285      * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
286      * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
287      * a chance to request dims to continue.
288      */
resetDimStates()289     void resetDimStates() {
290         if (mDimState != null && !mDimState.mDontReset) {
291             mDimState.mDimming = false;
292         }
293     }
294 
dontAnimateExit()295     void dontAnimateExit() {
296         if (mDimState != null) {
297             mDimState.mAnimateExit = false;
298         }
299     }
300 
301     /**
302      * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
303      * described in {@link #resetDimStates}.
304      *
305      * @param t      A transaction in which to update the dims.
306      * @param bounds The bounds at which to dim.
307      * @return true if any Dims were updated.
308      */
updateDims(SurfaceControl.Transaction t, Rect bounds)309     boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
310         if (mDimState == null) {
311             return false;
312         }
313 
314         if (!mDimState.mDimming) {
315             if (!mDimState.mAnimateExit) {
316                 if (mDimState.mDimLayer.isValid()) {
317                     t.remove(mDimState.mDimLayer);
318                 }
319             } else {
320                 startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
321             }
322             mDimState = null;
323             return false;
324         } else {
325             // TODO: Once we use geometry from hierarchy this falls away.
326             t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
327             t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
328             if (!mDimState.isVisible) {
329                 mDimState.isVisible = true;
330                 t.show(mDimState.mDimLayer);
331                 startDimEnter(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
332             }
333             return true;
334         }
335     }
336 
startDimEnter(WindowContainer container, SurfaceAnimator animator, SurfaceControl.Transaction t)337     private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
338             SurfaceControl.Transaction t) {
339         startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
340     }
341 
startDimExit(WindowContainer container, SurfaceAnimator animator, SurfaceControl.Transaction t)342     private void startDimExit(WindowContainer container, SurfaceAnimator animator,
343             SurfaceControl.Transaction t) {
344         startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
345     }
346 
startAnim(WindowContainer container, SurfaceAnimator animator, SurfaceControl.Transaction t, float startAlpha, float endAlpha)347     private void startAnim(WindowContainer container, SurfaceAnimator animator,
348             SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
349         mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
350                 new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
351                 mHost.mWmService.mSurfaceAnimationRunner), false /* hidden */,
352                 ANIMATION_TYPE_DIMMER);
353     }
354 
getDimDuration(WindowContainer container)355     private long getDimDuration(WindowContainer container) {
356         // If there's no container, then there isn't an animation occurring while dimming. Set the
357         // duration to 0 so it immediately dims to the set alpha.
358         if (container == null) {
359             return 0;
360         }
361 
362         // Otherwise use the same duration as the animation on the WindowContainer
363         AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
364         return animationAdapter == null ? DEFAULT_DIM_ANIM_DURATION
365                 : animationAdapter.getDurationHint();
366     }
367 
368     private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
369         private final long mDuration;
370         private final float mFromAlpha;
371         private final float mToAlpha;
372 
AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration)373         AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
374             mFromAlpha = fromAlpha;
375             mToAlpha = toAlpha;
376             mDuration = duration;
377         }
378 
379         @Override
getDuration()380         public long getDuration() {
381             return mDuration;
382         }
383 
384         @Override
apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime)385         public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
386             final float fraction = getFraction(currentPlayTime);
387             final float alpha = fraction * (mToAlpha - mFromAlpha) + mFromAlpha;
388             t.setAlpha(sc, alpha);
389         }
390 
391         @Override
dump(PrintWriter pw, String prefix)392         public void dump(PrintWriter pw, String prefix) {
393             pw.print(prefix); pw.print("from="); pw.print(mFromAlpha);
394             pw.print(" to="); pw.print(mToAlpha);
395             pw.print(" duration="); pw.println(mDuration);
396         }
397 
398         @Override
dumpDebugInner(ProtoOutputStream proto)399         public void dumpDebugInner(ProtoOutputStream proto) {
400             final long token = proto.start(ALPHA);
401             proto.write(FROM, mFromAlpha);
402             proto.write(TO, mToAlpha);
403             proto.write(DURATION_MS, mDuration);
404             proto.end(token);
405         }
406     }
407 }
408