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