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