[go: nahoru, domu]

1package com.android.server.wm;
2
3import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
4import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
5import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
6import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
7
8import android.graphics.Rect;
9import android.util.ArrayMap;
10import android.util.Slog;
11import android.util.TypedValue;
12
13import com.android.server.wm.DimLayer.DimLayerUser;
14
15import java.io.PrintWriter;
16
17/**
18 * Centralizes the control of dim layers used for
19 * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
20 * as well as other use cases (such as dimming above a dead window).
21 */
22class DimLayerController {
23    private static final String TAG_LOCAL = "DimLayerController";
24    private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
25
26    /** Amount of time in milliseconds to animate the dim surface from one value to another,
27     * when no window animation is driving it. */
28    private static final int DEFAULT_DIM_DURATION = 200;
29
30    /**
31     * The default amount of dim applied over a dead window
32     */
33    private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
34
35    // Shared dim layer for fullscreen users. {@link DimLayerState#dimLayer} will point to this
36    // instead of creating a new object per fullscreen task on a display.
37    private DimLayer mSharedFullScreenDimLayer;
38
39    private ArrayMap<DimLayer.DimLayerUser, DimLayerState> mState = new ArrayMap<>();
40
41    private DisplayContent mDisplayContent;
42
43    private Rect mTmpBounds = new Rect();
44
45    DimLayerController(DisplayContent displayContent) {
46        mDisplayContent = displayContent;
47    }
48
49    /** Updates the dim layer bounds, recreating it if needed. */
50    void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
51        final DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
52        final boolean previousFullscreen = state.dimLayer != null
53                && state.dimLayer == mSharedFullScreenDimLayer;
54        DimLayer newDimLayer;
55        final int displayId = mDisplayContent.getDisplayId();
56        if (dimLayerUser.dimFullscreen()) {
57            if (previousFullscreen && mSharedFullScreenDimLayer != null) {
58                // Update the bounds for fullscreen in case of rotation.
59                mSharedFullScreenDimLayer.setBoundsForFullscreen();
60                return;
61            }
62            // Use shared fullscreen dim layer
63            newDimLayer = mSharedFullScreenDimLayer;
64            if (newDimLayer == null) {
65                if (state.dimLayer != null) {
66                    // Re-purpose the previous dim layer.
67                    newDimLayer = state.dimLayer;
68                } else {
69                    // Create new full screen dim layer.
70                    newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
71                            getDimLayerTag(dimLayerUser));
72                }
73                dimLayerUser.getDimBounds(mTmpBounds);
74                newDimLayer.setBounds(mTmpBounds);
75                mSharedFullScreenDimLayer = newDimLayer;
76            } else if (state.dimLayer != null) {
77                state.dimLayer.destroySurface();
78            }
79        } else {
80            newDimLayer = (state.dimLayer == null || previousFullscreen)
81                    ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
82                            getDimLayerTag(dimLayerUser))
83                    : state.dimLayer;
84            dimLayerUser.getDimBounds(mTmpBounds);
85            newDimLayer.setBounds(mTmpBounds);
86        }
87        state.dimLayer = newDimLayer;
88    }
89
90    private static String getDimLayerTag(DimLayerUser dimLayerUser) {
91        return TAG_LOCAL + "/" + dimLayerUser.toShortString();
92    }
93
94    private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
95        if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
96                + dimLayerUser.toShortString());
97        DimLayerState state = mState.get(dimLayerUser);
98        if (state == null) {
99            state = new DimLayerState();
100            mState.put(dimLayerUser, state);
101        }
102        return state;
103    }
104
105    private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
106        DimLayerState state = mState.get(dimLayerUser);
107        if (state == null) {
108            if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: "
109                    + dimLayerUser.toShortString());
110            return;
111        }
112        state.continueDimming = true;
113    }
114
115    boolean isDimming() {
116        for (int i = mState.size() - 1; i >= 0; i--) {
117            DimLayerState state = mState.valueAt(i);
118            if (state.dimLayer != null && state.dimLayer.isDimming()) {
119                return true;
120            }
121        }
122        return false;
123    }
124
125    void resetDimming() {
126        for (int i = mState.size() - 1; i >= 0; i--) {
127            mState.valueAt(i).continueDimming = false;
128        }
129    }
130
131    private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
132        DimLayerState state = mState.get(dimLayerUser);
133        return state != null && state.continueDimming;
134    }
135
136    void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
137            WindowStateAnimator newWinAnimator, boolean aboveApp) {
138        // Only set dim params on the highest dimmed layer.
139        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
140        DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
141        state.dimAbove = aboveApp;
142        if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
143                + " dimLayerUser=" + dimLayerUser.toShortString()
144                + " newWinAnimator=" + newWinAnimator
145                + " state.animator=" + state.animator);
146        if (newWinAnimator.getShown() && (state.animator == null
147                || !state.animator.getShown()
148                || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) {
149            state.animator = newWinAnimator;
150            if (state.animator.mWin.mAppToken == null && !dimLayerUser.dimFullscreen()) {
151                // Dim should cover the entire screen for system windows.
152                mDisplayContent.getLogicalDisplayRect(mTmpBounds);
153            } else {
154                dimLayerUser.getDimBounds(mTmpBounds);
155            }
156            state.dimLayer.setBounds(mTmpBounds);
157        }
158    }
159
160    void stopDimmingIfNeeded() {
161        if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size());
162        for (int i = mState.size() - 1; i >= 0; i--) {
163            DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i);
164            stopDimmingIfNeeded(dimLayerUser);
165        }
166    }
167
168    private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) {
169        // No need to check if state is null, we know the key has a value.
170        DimLayerState state = mState.get(dimLayerUser);
171        if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded,"
172                + " dimLayerUser=" + dimLayerUser.toShortString()
173                + " state.continueDimming=" + state.continueDimming
174                + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
175        if (state.animator != null && state.animator.mWin.mWillReplaceWindow) {
176            return;
177        }
178
179        if (!state.continueDimming && state.dimLayer.isDimming()) {
180            state.animator = null;
181            dimLayerUser.getDimBounds(mTmpBounds);
182            state.dimLayer.setBounds(mTmpBounds);
183        }
184    }
185
186    boolean animateDimLayers() {
187        int fullScreen = -1;
188        int fullScreenAndDimming = -1;
189        boolean result = false;
190
191        for (int i = mState.size() - 1; i >= 0; i--) {
192            DimLayer.DimLayerUser user = mState.keyAt(i);
193            DimLayerState state = mState.valueAt(i);
194            // We have to check that we are actually the shared fullscreen layer
195            // for this path. If we began as non fullscreen and became fullscreen
196            // (e.g. Docked stack closing), then we may not be the shared layer
197            // and we have to make sure we always animate the layer.
198            if (user.dimFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) {
199                fullScreen = i;
200                if (mState.valueAt(i).continueDimming) {
201                    fullScreenAndDimming = i;
202                }
203            } else {
204                // We always want to animate the non fullscreen windows, they don't share their
205                // dim layers.
206                result |= animateDimLayers(user);
207            }
208        }
209        // For the shared, full screen dim layer, we prefer the animation that is causing it to
210        // appear.
211        if (fullScreenAndDimming != -1) {
212            result |= animateDimLayers(mState.keyAt(fullScreenAndDimming));
213        } else if (fullScreen != -1) {
214            // If there is no animation for the full screen dim layer to appear, we can use any of
215            // the animators that will cause it to disappear.
216            result |= animateDimLayers(mState.keyAt(fullScreen));
217        }
218        return result;
219    }
220
221    private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
222        DimLayerState state = mState.get(dimLayerUser);
223        if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
224                + " dimLayerUser=" + dimLayerUser.toShortString()
225                + " state.animator=" + state.animator
226                + " state.continueDimming=" + state.continueDimming);
227        final int dimLayer;
228        final float dimAmount;
229        if (state.animator == null) {
230            dimLayer = state.dimLayer.getLayer();
231            dimAmount = 0;
232        } else {
233            if (state.dimAbove) {
234                dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM;
235                dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW;
236            } else {
237                dimLayer = state.animator.mAnimLayer - LAYER_OFFSET_DIM;
238                dimAmount = state.animator.mWin.mAttrs.dimAmount;
239            }
240        }
241        final float targetAlpha = state.dimLayer.getTargetAlpha();
242        if (targetAlpha != dimAmount) {
243            if (state.animator == null) {
244                state.dimLayer.hide(DEFAULT_DIM_DURATION);
245            } else {
246                long duration = (state.animator.mAnimating && state.animator.mAnimation != null)
247                        ? state.animator.mAnimation.computeDurationHint()
248                        : DEFAULT_DIM_DURATION;
249                if (targetAlpha > dimAmount) {
250                    duration = getDimLayerFadeDuration(duration);
251                }
252                state.dimLayer.show(dimLayer, dimAmount, duration);
253            }
254        } else if (state.dimLayer.getLayer() != dimLayer) {
255            state.dimLayer.setLayer(dimLayer);
256        }
257        if (state.dimLayer.isAnimating()) {
258            if (!mDisplayContent.mService.okToDisplay()) {
259                // Jump to the end of the animation.
260                state.dimLayer.show();
261            } else {
262                return state.dimLayer.stepAnimation();
263            }
264        }
265        return false;
266    }
267
268    boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
269        DimLayerState state = mState.get(dimLayerUser);
270        return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
271    }
272
273    private long getDimLayerFadeDuration(long duration) {
274        TypedValue tv = new TypedValue();
275        mDisplayContent.mService.mContext.getResources().getValue(
276                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
277        if (tv.type == TypedValue.TYPE_FRACTION) {
278            duration = (long) tv.getFraction(duration, duration);
279        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
280            duration = tv.data;
281        }
282        return duration;
283    }
284
285    void close() {
286        for (int i = mState.size() - 1; i >= 0; i--) {
287            DimLayerState state = mState.valueAt(i);
288            state.dimLayer.destroySurface();
289        }
290        mState.clear();
291        mSharedFullScreenDimLayer = null;
292    }
293
294    void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
295        DimLayerState state = mState.get(dimLayerUser);
296        if (state != null) {
297            // Destroy the surface, unless it's the shared fullscreen dim.
298            if (state.dimLayer != mSharedFullScreenDimLayer) {
299                state.dimLayer.destroySurface();
300            }
301            mState.remove(dimLayerUser);
302        }
303    }
304
305    void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
306        applyDim(dimLayerUser, animator, false /* aboveApp */);
307    }
308
309    void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
310        applyDim(dimLayerUser, animator, true /* aboveApp */);
311    }
312
313    void applyDim(
314            DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) {
315        if (dimLayerUser == null) {
316            Slog.e(TAG, "Trying to apply dim layer for: " + this
317                    + ", but no dim layer user found.");
318            return;
319        }
320        if (!getContinueDimming(dimLayerUser)) {
321            setContinueDimming(dimLayerUser);
322            if (!isDimming(dimLayerUser, animator)) {
323                if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
324                startDimmingIfNeeded(dimLayerUser, animator, aboveApp);
325            }
326        }
327    }
328
329    private static class DimLayerState {
330        // The particular window requesting a dim layer. If null, hide dimLayer.
331        WindowStateAnimator animator;
332        // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
333        // end then stop any dimming.
334        boolean continueDimming;
335        DimLayer dimLayer;
336        boolean dimAbove;
337    }
338
339    void dump(String prefix, PrintWriter pw) {
340        pw.println(prefix + "DimLayerController");
341        final String doubleSpace = "  ";
342        final String prefixPlusDoubleSpace = prefix + doubleSpace;
343
344        for (int i = 0, n = mState.size(); i < n; i++) {
345            pw.println(prefixPlusDoubleSpace + mState.keyAt(i).toShortString());
346            DimLayerState state = mState.valueAt(i);
347            pw.println(prefixPlusDoubleSpace + doubleSpace + "dimLayer="
348                    + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" : state.dimLayer)
349                    + ", animator=" + state.animator + ", continueDimming=" + state.continueDimming);
350            if (state.dimLayer != null) {
351                state.dimLayer.printTo(prefixPlusDoubleSpace + doubleSpace, pw);
352            }
353        }
354    }
355}
356