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.Slog;
25import android.util.TypedValue;
26
27import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
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 final 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 ArrayList<Task> mTasks = new ArrayList<Task>();
49
50    /** The StackBox this sits in. */
51    StackBox mStackBox;
52
53    /** Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} */
54    final DimLayer mDimLayer;
55
56    /** The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer. */
57    WindowStateAnimator mDimWinAnimator;
58
59    /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
60    final DimLayer mAnimationBackgroundSurface;
61
62    /** The particular window with an Animation with non-zero background color. */
63    WindowStateAnimator mAnimationBackgroundAnimator;
64
65    /** Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
66     * then stop any dimming. */
67    boolean mDimmingTag;
68
69    TaskStack(WindowManagerService service, int stackId, DisplayContent displayContent) {
70        mService = service;
71        mStackId = stackId;
72        mDisplayContent = displayContent;
73        final int displayId = displayContent.getDisplayId();
74        mDimLayer = new DimLayer(service, this);
75        mAnimationBackgroundSurface = new DimLayer(service, this);
76    }
77
78    DisplayContent getDisplayContent() {
79        return mDisplayContent;
80    }
81
82    ArrayList<Task> getTasks() {
83        return mTasks;
84    }
85
86    boolean isHomeStack() {
87        return mStackId == HOME_STACK_ID;
88    }
89
90    boolean hasSibling() {
91        return mStackBox.mParent != null;
92    }
93
94    /**
95     * Put a Task in this stack. Used for adding and moving.
96     * @param task The task to add.
97     * @param toTop Whether to add it to the top or bottom.
98     */
99    boolean addTask(Task task, boolean toTop) {
100        mStackBox.makeDirty();
101
102        int stackNdx;
103        if (!toTop) {
104            stackNdx = 0;
105        } else {
106            stackNdx = mTasks.size();
107            final int currentUserId = mService.mCurrentUserId;
108            if (task.mUserId != currentUserId) {
109                // Place the task below all current user tasks.
110                while (--stackNdx >= 0) {
111                    if (currentUserId != mTasks.get(stackNdx).mUserId) {
112                        break;
113                    }
114                }
115                ++stackNdx;
116            }
117        }
118        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
119                + " pos=" + stackNdx);
120        mTasks.add(stackNdx, task);
121
122        task.mStack = this;
123        return mDisplayContent.moveHomeStackBox(mStackId == HOME_STACK_ID);
124    }
125
126    boolean moveTaskToTop(Task task) {
127        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
128                + Debug.getCallers(6));
129        mTasks.remove(task);
130        return addTask(task, true);
131    }
132
133    boolean moveTaskToBottom(Task task) {
134        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
135        mTasks.remove(task);
136        return addTask(task, false);
137    }
138
139    /**
140     * Delete a Task from this stack. If it is the last Task in the stack, remove this stack from
141     * its parent StackBox and merge the parent.
142     * @param task The Task to delete.
143     */
144    void removeTask(Task task) {
145        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
146        mStackBox.makeDirty();
147        mTasks.remove(task);
148    }
149
150    int remove() {
151        mAnimationBackgroundSurface.destroySurface();
152        mDimLayer.destroySurface();
153        return mStackBox.remove();
154    }
155
156    void resetAnimationBackgroundAnimator() {
157        mAnimationBackgroundAnimator = null;
158        mAnimationBackgroundSurface.hide();
159    }
160
161    private long getDimBehindFadeDuration(long duration) {
162        TypedValue tv = new TypedValue();
163        mService.mContext.getResources().getValue(
164                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
165        if (tv.type == TypedValue.TYPE_FRACTION) {
166            duration = (long)tv.getFraction(duration, duration);
167        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
168            duration = tv.data;
169        }
170        return duration;
171    }
172
173    boolean animateDimLayers() {
174        final int dimLayer;
175        final float dimAmount;
176        if (mDimWinAnimator == null) {
177            dimLayer = mDimLayer.getLayer();
178            dimAmount = 0;
179        } else {
180            dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
181            dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
182        }
183        final float targetAlpha = mDimLayer.getTargetAlpha();
184        if (targetAlpha != dimAmount) {
185            if (mDimWinAnimator == null) {
186                mDimLayer.hide(DEFAULT_DIM_DURATION);
187            } else {
188                long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
189                        ? mDimWinAnimator.mAnimation.computeDurationHint()
190                        : DEFAULT_DIM_DURATION;
191                if (targetAlpha > dimAmount) {
192                    duration = getDimBehindFadeDuration(duration);
193                }
194                mDimLayer.show(dimLayer, dimAmount, duration);
195            }
196        } else if (mDimLayer.getLayer() != dimLayer) {
197            mDimLayer.setLayer(dimLayer);
198        }
199        if (mDimLayer.isAnimating()) {
200            if (!mService.okToDisplay()) {
201                // Jump to the end of the animation.
202                mDimLayer.show();
203            } else {
204                return mDimLayer.stepAnimation();
205            }
206        }
207        return false;
208    }
209
210    void resetDimmingTag() {
211        mDimmingTag = false;
212    }
213
214    void setDimmingTag() {
215        mDimmingTag = true;
216    }
217
218    boolean testDimmingTag() {
219        return mDimmingTag;
220    }
221
222    boolean isDimming() {
223        return mDimLayer.isDimming();
224    }
225
226    boolean isDimming(WindowStateAnimator winAnimator) {
227        return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
228    }
229
230    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
231        // Only set dim params on the highest dimmed layer.
232        final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator;
233        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
234        if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null
235                || !existingDimWinAnimator.mSurfaceShown
236                || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
237            mDimWinAnimator = newWinAnimator;
238        }
239    }
240
241    void stopDimmingIfNeeded() {
242        if (!mDimmingTag && isDimming()) {
243            mDimWinAnimator = null;
244        }
245    }
246
247    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
248        int animLayer = winAnimator.mAnimLayer;
249        if (mAnimationBackgroundAnimator == null
250                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
251            mAnimationBackgroundAnimator = winAnimator;
252            animLayer = mService.adjustAnimationBackground(winAnimator);
253            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
254                    ((color >> 24) & 0xff) / 255f, 0);
255        }
256    }
257
258    void setBounds(Rect bounds, boolean underStatusBar) {
259        mDimLayer.setBounds(bounds);
260        mAnimationBackgroundSurface.setBounds(bounds);
261
262        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
263        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
264            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
265            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
266                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
267                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
268                    final WindowState win = windows.get(winNdx);
269                    if (!resizingWindows.contains(win)) {
270                        resizingWindows.add(win);
271                    }
272                    win.mUnderStatusBar = underStatusBar;
273                }
274            }
275        }
276    }
277
278    void switchUser(int userId) {
279        int top = mTasks.size();
280        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
281            Task task = mTasks.get(taskNdx);
282            if (task.mUserId == userId) {
283                mTasks.remove(taskNdx);
284                mTasks.add(task);
285                --top;
286            }
287        }
288    }
289
290    public void dump(String prefix, PrintWriter pw) {
291        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
292        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
293            pw.print(prefix); pw.println(mTasks.get(taskNdx));
294        }
295        if (mAnimationBackgroundSurface.isDimming()) {
296            pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
297            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
298        }
299        if (mDimLayer.isDimming()) {
300            pw.print(prefix); pw.println("mDimLayer:");
301            mDimLayer.printTo(prefix, pw);
302            pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
303        }
304    }
305
306    @Override
307    public String toString() {
308        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
309    }
310}
311