TaskStack.java revision df88d73092c62a1a3cd2b2056ca63ae2e70cc238
1/*
2 * Copyright (C) 2013 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
19import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
20import static com.android.server.wm.WindowManagerService.TAG;
21
22import android.graphics.Rect;
23import android.os.Debug;
24import android.util.EventLog;
25import android.util.Slog;
26import android.util.TypedValue;
27import com.android.server.EventLogTags;
28
29import java.io.PrintWriter;
30import java.util.ArrayList;
31
32public class TaskStack {
33    /** Amount of time in milliseconds to animate the dim surface from one value to another,
34     * when no window animation is driving it. */
35    private static final int DEFAULT_DIM_DURATION = 200;
36
37    /** Unique identifier */
38    final int mStackId;
39
40    /** The service */
41    private final WindowManagerService mService;
42
43    /** The display this stack sits under. */
44    private DisplayContent mDisplayContent;
45
46    /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
47     * mTaskHistory in the ActivityStack with the same mStackId */
48    private final ArrayList<Task> mTasks = new ArrayList<Task>();
49
50    /** Content limits relative to the DisplayContent this sits in. Empty indicates fullscreen,
51     * Nonempty is size of this TaskStack but is also used to scale if DisplayContent changes. */
52    Rect mBounds = new Rect();
53
54    /** Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} */
55    DimLayer mDimLayer;
56
57    /** The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer. */
58    WindowStateAnimator mDimWinAnimator;
59
60    /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
61    DimLayer mAnimationBackgroundSurface;
62
63    /** The particular window with an Animation with non-zero background color. */
64    WindowStateAnimator mAnimationBackgroundAnimator;
65
66    /** Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
67     * then stop any dimming. */
68    boolean mDimmingTag;
69
70    TaskStack(WindowManagerService service, int stackId) {
71        mService = service;
72        mStackId = stackId;
73        // TODO: remove bounds from log, they are always 0.
74        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top,
75                mBounds.right, mBounds.bottom);
76    }
77
78    DisplayContent getDisplayContent() {
79        return mDisplayContent;
80    }
81
82    ArrayList<Task> getTasks() {
83        return mTasks;
84    }
85
86    private void resizeWindows() {
87        final boolean underStatusBar = mBounds.top == 0;
88
89        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
90        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
91            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
92            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
93                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
94                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
95                    final WindowState win = windows.get(winNdx);
96                    if (!resizingWindows.contains(win)) {
97                        if (WindowManagerService.DEBUG_RESIZE) Slog.d(TAG,
98                                "setBounds: Resizing " + win);
99                        resizingWindows.add(win);
100                    }
101                    win.mUnderStatusBar = underStatusBar;
102                }
103            }
104        }
105    }
106
107    boolean setBounds(Rect bounds) {
108        if (mBounds.equals(bounds)) {
109            return false;
110        }
111
112        mDimLayer.setBounds(bounds);
113        mAnimationBackgroundSurface.setBounds(bounds);
114        mBounds.set(bounds);
115
116        resizeWindows();
117        return true;
118    }
119
120    void getBounds(Rect out) {
121        if (mDisplayContent != null) {
122            if (mBounds.isEmpty()) {
123                mDisplayContent.getLogicalDisplayRect(out);
124            } else {
125                out.set(mBounds);
126            }
127            out.intersect(mDisplayContent.mContentRect);
128        } else {
129            out.set(mBounds);
130        }
131    }
132
133    boolean isFullscreen() {
134        return mBounds.isEmpty();
135    }
136
137    void resizeBounds(float oldWidth, float oldHeight, float newWidth, float newHeight) {
138        if (oldWidth == newWidth && oldHeight == newHeight) {
139            return;
140        }
141        float widthScale = newWidth / oldWidth;
142        float heightScale = newHeight / oldHeight;
143        mBounds.left = (int)(mBounds.left * widthScale + 0.5);
144        mBounds.top = (int)(mBounds.top * heightScale + 0.5);
145        mBounds.right = (int)(mBounds.right * widthScale + 0.5);
146        mBounds.bottom = (int)(mBounds.bottom * heightScale + 0.5);
147        resizeWindows();
148    }
149
150    /**
151     * Put a Task in this stack. Used for adding and moving.
152     * @param task The task to add.
153     * @param toTop Whether to add it to the top or bottom.
154     */
155    void addTask(Task task, boolean toTop) {
156        int stackNdx;
157        if (!toTop) {
158            stackNdx = 0;
159        } else {
160            stackNdx = mTasks.size();
161            final int currentUserId = mService.mCurrentUserId;
162            if (task.mUserId != currentUserId) {
163                // Place the task below all current user tasks.
164                while (--stackNdx >= 0) {
165                    if (currentUserId != mTasks.get(stackNdx).mUserId) {
166                        break;
167                    }
168                }
169                ++stackNdx;
170            }
171        }
172        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
173                + " pos=" + stackNdx);
174        mTasks.add(stackNdx, task);
175
176        task.mStack = this;
177        mDisplayContent.addTask(task, toTop);
178        mDisplayContent.moveStack(this, true);
179    }
180
181    void moveTaskToTop(Task task) {
182        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
183                + Debug.getCallers(6));
184        mTasks.remove(task);
185        addTask(task, true);
186    }
187
188    void moveTaskToBottom(Task task) {
189        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
190        mTasks.remove(task);
191        addTask(task, false);
192    }
193
194    /**
195     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
196     * back.
197     * @param task The Task to delete.
198     */
199    void removeTask(Task task) {
200        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
201        mTasks.remove(task);
202        if (mDisplayContent != null) {
203            mDisplayContent.removeTask(task);
204            if (mTasks.isEmpty()) {
205                mDisplayContent.moveStack(this, false);
206            }
207            mDisplayContent.layoutNeeded = true;
208        }
209    }
210
211    void attachDisplayContent(DisplayContent displayContent) {
212        if (mDisplayContent != null) {
213            throw new IllegalStateException("attachDisplayContent: Already attached");
214        }
215
216        mDisplayContent = displayContent;
217        mDimLayer = new DimLayer(mService, this, displayContent);
218        mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent);
219
220        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
221            Task task = mTasks.get(taskNdx);
222            displayContent.addTask(task, true);
223        }
224    }
225
226    void detach() {
227        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
228        mAnimationBackgroundSurface.destroySurface();
229        mAnimationBackgroundSurface = null;
230        mDimLayer.destroySurface();
231        mDimLayer = null;
232        mDisplayContent = null;
233    }
234
235    void resetAnimationBackgroundAnimator() {
236        mAnimationBackgroundAnimator = null;
237        mAnimationBackgroundSurface.hide();
238    }
239
240    private long getDimBehindFadeDuration(long duration) {
241        TypedValue tv = new TypedValue();
242        mService.mContext.getResources().getValue(
243                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
244        if (tv.type == TypedValue.TYPE_FRACTION) {
245            duration = (long)tv.getFraction(duration, duration);
246        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
247            duration = tv.data;
248        }
249        return duration;
250    }
251
252    boolean animateDimLayers() {
253        final int dimLayer;
254        final float dimAmount;
255        if (mDimWinAnimator == null) {
256            dimLayer = mDimLayer.getLayer();
257            dimAmount = 0;
258        } else {
259            dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
260            dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
261        }
262        final float targetAlpha = mDimLayer.getTargetAlpha();
263        if (targetAlpha != dimAmount) {
264            if (mDimWinAnimator == null) {
265                mDimLayer.hide(DEFAULT_DIM_DURATION);
266            } else {
267                long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
268                        ? mDimWinAnimator.mAnimation.computeDurationHint()
269                        : DEFAULT_DIM_DURATION;
270                if (targetAlpha > dimAmount) {
271                    duration = getDimBehindFadeDuration(duration);
272                }
273                mDimLayer.show(dimLayer, dimAmount, duration);
274            }
275        } else if (mDimLayer.getLayer() != dimLayer) {
276            mDimLayer.setLayer(dimLayer);
277        }
278        if (mDimLayer.isAnimating()) {
279            if (!mService.okToDisplay()) {
280                // Jump to the end of the animation.
281                mDimLayer.show();
282            } else {
283                return mDimLayer.stepAnimation();
284            }
285        }
286        return false;
287    }
288
289    void resetDimmingTag() {
290        mDimmingTag = false;
291    }
292
293    void setDimmingTag() {
294        mDimmingTag = true;
295    }
296
297    boolean testDimmingTag() {
298        return mDimmingTag;
299    }
300
301    boolean isDimming() {
302        return mDimLayer.isDimming();
303    }
304
305    boolean isDimming(WindowStateAnimator winAnimator) {
306        return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
307    }
308
309    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
310        // Only set dim params on the highest dimmed layer.
311        final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator;
312        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
313        if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null
314                || !existingDimWinAnimator.mSurfaceShown
315                || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
316            mDimWinAnimator = newWinAnimator;
317        }
318    }
319
320    void stopDimmingIfNeeded() {
321        if (!mDimmingTag && isDimming()) {
322            mDimWinAnimator = null;
323        }
324    }
325
326    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
327        int animLayer = winAnimator.mAnimLayer;
328        if (mAnimationBackgroundAnimator == null
329                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
330            mAnimationBackgroundAnimator = winAnimator;
331            animLayer = mService.adjustAnimationBackground(winAnimator);
332            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
333                    ((color >> 24) & 0xff) / 255f, 0);
334        }
335    }
336
337    void switchUser(int userId) {
338        int top = mTasks.size();
339        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
340            Task task = mTasks.get(taskNdx);
341            if (task.mUserId == userId) {
342                mTasks.remove(taskNdx);
343                mTasks.add(task);
344                --top;
345            }
346        }
347    }
348
349    void close() {
350        mDimLayer.mDimSurface.destroy();
351        mAnimationBackgroundSurface.mDimSurface.destroy();
352    }
353
354    public void dump(String prefix, PrintWriter pw) {
355        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
356        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
357            pw.print(prefix); pw.println(mTasks.get(taskNdx));
358        }
359        if (mAnimationBackgroundSurface.isDimming()) {
360            pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
361            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
362        }
363        if (mDimLayer.isDimming()) {
364            pw.print(prefix); pw.println("mDimLayer:");
365            mDimLayer.printTo(prefix, pw);
366            pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
367        }
368    }
369
370    @Override
371    public String toString() {
372        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
373    }
374}
375