• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.internal.protolog.WmProtoLogGroups.WM_DEBUG_DIMMER;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.graphics.Rect;
24 import android.util.Log;
25 import android.view.Surface;
26 import android.view.SurfaceControl;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.internal.protolog.ProtoLog;
30 import com.android.window.flags.Flags;
31 
32 
33 /**
34  * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
35  * black layers of varying opacity at various Z-levels which create the effect of a Dim.
36  */
37 class Dimmer {
38 
39     /**
40      * The {@link WindowContainer} that our Dims are bounded to. We may be dimming on behalf of the
41      * host, some controller of it, or one of the hosts children.
42      */
43     private final WindowContainer<?> mHost;
44 
45     private static final String TAG = "WindowManagerDimmer";
46 
47     DimState mDimState;
48     final DimmerAnimationHelper.AnimationAdapterFactory mAnimationAdapterFactory;
49 
50     /**
51      * Controls the dim behaviour
52      */
53     protected class DimState {
54         /** Related objects */
55         SurfaceControl mDimSurface;
56         final WindowContainer<?> mHostContainer;
57         // The last container to request to dim
58         private WindowState mLastDimmingWindow;
59         /** Animation */
60         private final DimmerAnimationHelper mAnimationHelper;
61         boolean mSkipAnimation = false;
62         // Determines whether the dim layer should animate before destroying.
63         boolean mAnimateExit = true;
64         /** Surface visibility and bounds */
65         private boolean mIsVisible = false;
66         // TODO(b/64816140): Remove after confirming dimmer layer always matches its container.
67         final Rect mDimBounds = new Rect();
68 
DimState()69         DimState() {
70             mHostContainer = mHost;
71             mAnimationHelper = new DimmerAnimationHelper(mHost, mAnimationAdapterFactory);
72             try {
73                 mDimSurface = makeDimLayer();
74                 EventLogTags.writeWmDimCreated(mHost.getName(), mDimSurface.getLayerId());
75             } catch (Surface.OutOfResourcesException e) {
76                 Log.w(TAG, "OutOfResourcesException creating dim surface");
77             }
78         }
79 
ensureVisible(@onNull SurfaceControl.Transaction t)80         void ensureVisible(@NonNull SurfaceControl.Transaction t) {
81             if (!mIsVisible) {
82                 t.show(mDimSurface);
83                 t.setAlpha(mDimSurface, 0f);
84                 mIsVisible = true;
85             }
86         }
87 
adjustSurfaceLayout(@onNull SurfaceControl.Transaction t)88         void adjustSurfaceLayout(@NonNull SurfaceControl.Transaction t) {
89             // TODO: Once we use geometry from hierarchy this falls away.
90             t.setPosition(mDimSurface, mDimBounds.left, mDimBounds.top);
91             t.setWindowCrop(mDimSurface, mDimBounds.width(), mDimBounds.height());
92         }
93 
94         /**
95          * Set the parameters to prepare the dim to change its appearance
96          */
prepareLookChange(float alpha, int blurRadius)97         void prepareLookChange(float alpha, int blurRadius) {
98             mAnimationHelper.setRequestedAppearance(alpha, blurRadius);
99         }
100 
101         /**
102          * Prepare the dim for the exit animation
103          */
exit(@onNull SurfaceControl.Transaction t)104         void exit(@NonNull SurfaceControl.Transaction t) {
105             EventLogTags.writeWmDimExit(mDimState.mDimSurface.getLayerId(),
106                     mDimState.mLastDimmingWindow != null
107                             ? mDimState.mLastDimmingWindow.getName() : "-",
108                     mDimState.mHostContainer.isVisible() ? 1 : 0,
109                     mAnimateExit ? 0 : 1);
110             if (!mAnimateExit) {
111                 remove(t);
112             } else {
113                 mAnimationHelper.setExitParameters();
114                 setReady(t);
115             }
116         }
117 
remove(@onNull SurfaceControl.Transaction t)118         void remove(@NonNull SurfaceControl.Transaction t) {
119             EventLogTags.writeWmDimCancelAnim(mDimSurface.getLayerId(), "ready to remove");
120             mAnimationHelper.stopCurrentAnimation(mDimSurface);
121             if (mDimSurface.isValid()) {
122                 EventLogTags.writeWmDimRemoved(mDimSurface.getLayerId());
123                 t.remove(mDimSurface);
124                 ProtoLog.d(WM_DEBUG_DIMMER,
125                         "Removing dim surface %s on transaction %s", this, t);
126             } else {
127                 Log.w(TAG, "Tried to remove " + mDimSurface + " multiple times\n");
128             }
129         }
130 
131         @Override
toString()132         public String toString() {
133             return "Dimmer#DimState with host=" + mHostContainer + ", surface=" + mDimSurface;
134         }
135 
136 
reasonForRemoving()137         String reasonForRemoving() {
138             return mLastDimmingWindow != null ? mLastDimmingWindow
139                     + " is dimming but host " + mHostContainer + " is not visibleRequested"
140                     : " no one is dimming";
141         }
142 
143         /**
144          * Set the parameters to prepare the dim to be relative parented to the dimming container
145          */
prepareReparent(@ullable WindowContainer<?> geometryParent, @NonNull WindowState relativeParent)146         void prepareReparent(@Nullable WindowContainer<?> geometryParent,
147                 @NonNull WindowState relativeParent) {
148             mAnimationHelper.setRequestedRelativeParent(relativeParent);
149             mAnimationHelper.setRequestedGeometryParent(geometryParent);
150         }
151 
152         /**
153          * Call when all the changes have been requested to have them applied
154          * @param t The transaction in which to apply the changes
155          */
setReady(@onNull SurfaceControl.Transaction t)156         void setReady(@NonNull SurfaceControl.Transaction t) {
157             mAnimationHelper.applyChanges(t, this);
158         }
159 
160         /**
161          * Whether anyone is currently requesting the dim
162          */
isDimming()163         boolean isDimming() {
164             return mLastDimmingWindow != null
165                     && (mHostContainer.isVisibleRequested() || !Flags.useTasksDimOnly());
166         }
167 
makeDimLayer()168         private SurfaceControl makeDimLayer() {
169             return mHost.makeChildSurface(null)
170                     .setParent(mHost.getSurfaceControl())
171                     .setColorLayer()
172                     .setName("Dim Layer for - " + mHost.getName())
173                     .setCallsite("DimLayer.makeDimLayer")
174                     .build();
175         }
176     }
177 
Dimmer(@onNull WindowContainer<?> host)178     protected Dimmer(@NonNull WindowContainer<?> host) {
179         this(host, new DimmerAnimationHelper.AnimationAdapterFactory());
180     }
181 
182     @VisibleForTesting
Dimmer(@onNull WindowContainer host, @NonNull DimmerAnimationHelper.AnimationAdapterFactory animationFactory)183     Dimmer(@NonNull WindowContainer host,
184                  @NonNull DimmerAnimationHelper.AnimationAdapterFactory animationFactory) {
185         mHost = host;
186         mAnimationAdapterFactory = animationFactory;
187     }
188 
hostIsTask()189     public boolean hostIsTask() {
190         return mHost.asTask() != null;
191     }
192 
193     /**
194      * Mark all dims as pending completion on the next call to {@link #updateDims}
195      *
196      * Called before iterating on mHost's children, first step of dimming.
197      * This is intended for us by the host container, to be called at the beginning of
198      * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
199      * chain {@link WindowContainer#prepareSurfaces} down to its children to give them
200      * a chance to request dims to continue.
201      */
resetDimStates()202     void resetDimStates() {
203         if (mDimState != null) {
204             mDimState.mLastDimmingWindow = null;
205         }
206     }
207 
208     /**
209      * Set the aspect of the dim layer, and request to keep dimming.
210      * For each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset, and the
211      * child should call setAppearance again to request the Dim to continue.
212      * If multiple containers call this method, only the changes relative to the topmost will be
213      * applied.
214      * The creation of the dim layer is delayed if the requested dim and blur are 0.
215      * @param dimmingContainer  Container requesting the dim
216      * @param alpha      Dim amount
217      * @param blurRadius Blur amount
218      */
adjustAppearance(@onNull WindowState dimmingContainer, float alpha, int blurRadius)219     protected void adjustAppearance(@NonNull WindowState dimmingContainer,
220                                     float alpha, int blurRadius) {
221         if (!mHost.isVisibleRequested()) {
222             // If the host is already going away, there is no point in keeping dimming
223             return;
224         }
225 
226         if (mDimState != null || (alpha != 0 || blurRadius != 0)) {
227             final DimState d = obtainDimState(dimmingContainer);
228             d.prepareLookChange(alpha, blurRadius);
229         }
230     }
231 
232     /**
233      * Position the dim relatively to the dimming container.
234      * Normally called together with #setAppearance, it can be called alone to keep the dim parented
235      * to a visible container until the next dimming container is ready.
236      * If multiple containers call this method, only the changes relative to the topmost will be
237      * applied.
238      *
239      * For each call to {@link WindowContainer#prepareSurfaces()} the DimState will be reset, and
240      * the child of the host should call adjustRelativeLayer and {@link Dimmer#adjustAppearance} to
241      * continue dimming. Indeed, this method won't be able to keep dimming or get a new DimState
242      * without also adjusting the appearance.
243      * @param geometryParent    The container that defines the geometry of the dim
244      * @param dimmingContainer      The container that is dimming. The dim layer will be rel-z
245      *                              parented below it
246      */
adjustPosition(@ullable WindowContainer<?> geometryParent, @NonNull WindowState dimmingContainer)247     public void adjustPosition(@Nullable WindowContainer<?> geometryParent,
248                                     @NonNull WindowState dimmingContainer) {
249         if (mDimState != null) {
250             mDimState.prepareReparent(geometryParent, dimmingContainer);
251         }
252     }
253 
254     /**
255      * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
256      * described in {@link #resetDimStates}.
257      *
258      * @param t      A transaction in which to update the dims.
259      * @return true if any Dims were updated.
260      */
updateDims(@onNull SurfaceControl.Transaction t)261     boolean updateDims(@NonNull SurfaceControl.Transaction t) {
262         if (mDimState == null) {
263             return false;
264         }
265         if (!mDimState.isDimming()) {
266             // No one is dimming, fade out and remove the dim
267             mDimState.exit(t);
268             mDimState = null;
269             return false;
270         } else {
271             // Someone is dimming, show the requested changes
272             if (!Flags.useTasksDimOnly()) {
273                 mDimState.adjustSurfaceLayout(t);
274             }
275             if (!mDimState.mIsVisible && mDimState.mLastDimmingWindow != null
276                     && mDimState.mLastDimmingWindow.mActivityRecord != null
277                     && mDimState.mLastDimmingWindow.mActivityRecord.mStartingData != null) {
278                 // Skip enter animation while starting window is on top of its activity
279                 mDimState.mSkipAnimation = true;
280             }
281             mDimState.setReady(t);
282             return true;
283         }
284     }
285 
hasDimState()286     boolean hasDimState() {
287         return mDimState != null;
288     }
289 
isDimming()290     boolean isDimming() {
291         return mDimState != null && mDimState.isDimming();
292     }
293 
294     @NonNull
obtainDimState(@onNull WindowState window)295     private DimState obtainDimState(@NonNull WindowState window) {
296         if (mDimState == null) {
297             mDimState = new DimState();
298         }
299         mDimState.mLastDimmingWindow = window;
300         return mDimState;
301     }
302 
303     /** Returns non-null bounds if the dimmer is showing. */
304     @VisibleForTesting
getDimLayer()305     SurfaceControl getDimLayer() {
306         return mDimState != null ? mDimState.mDimSurface : null;
307     }
308 
getDimBounds()309     Rect getDimBounds() {
310         return mDimState != null ? mDimState.mDimBounds : null;
311     }
312 
dontAnimateExit()313     void dontAnimateExit() {
314         if (mDimState != null) {
315             mDimState.mAnimateExit = false;
316         }
317     }
318 }
319