• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.wm;
18 
19 import android.graphics.Rect;
20 import android.util.Slog;
21 
22 import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
23 import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
24 import static com.android.server.wm.WindowManagerService.TAG;
25 
26 import java.io.PrintWriter;
27 
28 public class StackBox {
29     /** Used with {@link WindowManagerService#createStack}. Dependent on Configuration LTR/RTL. */
30     public static final int TASK_STACK_GOES_BEFORE = 0;
31     /** Used with {@link WindowManagerService#createStack}. Dependent on Configuration LTR/RTL. */
32     public static final int TASK_STACK_GOES_AFTER = 1;
33     /** Used with {@link WindowManagerService#createStack}. Horizontal to left of. */
34     public static final int TASK_STACK_TO_LEFT_OF = 2;
35     /** Used with {@link WindowManagerService#createStack}. Horizontal to right of. */
36     public static final int TASK_STACK_TO_RIGHT_OF = 3;
37     /** Used with {@link WindowManagerService#createStack}. Vertical: lower t/b Rect values. */
38     public static final int TASK_STACK_GOES_ABOVE = 4;
39     /** Used with {@link WindowManagerService#createStack}. Vertical: higher t/b Rect values. */
40     public static final int TASK_STACK_GOES_BELOW = 5;
41     /** Used with {@link WindowManagerService#createStack}. Put on a higher layer on display. */
42     public static final int TASK_STACK_GOES_OVER = 6;
43     /** Used with {@link WindowManagerService#createStack}. Put on a lower layer on display. */
44     public static final int TASK_STACK_GOES_UNDER = 7;
45 
46     static int sCurrentBoxId = 0;
47 
48     /** Unique id for this box */
49     final int mStackBoxId;
50 
51     /** The service */
52     final WindowManagerService mService;
53 
54     /** The display this box sits in. */
55     final DisplayContent mDisplayContent;
56 
57     /** Non-null indicates this is mFirst or mSecond of a parent StackBox. Null indicates this
58      * is this entire size of mDisplayContent. */
59     StackBox mParent;
60 
61     /** First child, this is null exactly when mStack is non-null. */
62     StackBox mFirst;
63 
64     /** Second child, this is null exactly when mStack is non-null. */
65     StackBox mSecond;
66 
67     /** Stack of Tasks, this is null exactly when mFirst and mSecond are non-null. */
68     TaskStack mStack;
69 
70     /** Content limits relative to the DisplayContent this sits in. */
71     Rect mBounds = new Rect();
72 
73     /** Relative orientation of mFirst and mSecond. */
74     boolean mVertical;
75 
76     /** Fraction of mBounds to devote to mFirst, remainder goes to mSecond */
77     float mWeight;
78 
79     /** Dirty flag. Something inside this or some descendant of this has changed. */
80     boolean layoutNeeded;
81 
82     /** True if this StackBox sits below the Status Bar. */
83     boolean mUnderStatusBar;
84 
85     /** Used to keep from reallocating a temporary Rect for propagating bounds to child boxes */
86     Rect mTmpRect = new Rect();
87 
StackBox(WindowManagerService service, DisplayContent displayContent, StackBox parent)88     StackBox(WindowManagerService service, DisplayContent displayContent, StackBox parent) {
89         synchronized (StackBox.class) {
90             mStackBoxId = sCurrentBoxId++;
91         }
92 
93         mService = service;
94         mDisplayContent = displayContent;
95         mParent = parent;
96     }
97 
98     /** Propagate #layoutNeeded bottom up. */
makeDirty()99     void makeDirty() {
100         layoutNeeded = true;
101         if (mParent != null) {
102             mParent.makeDirty();
103         }
104     }
105 
106     /**
107      * Determine if a particular StackBox is this one or a descendant of this one.
108      * @param stackBoxId The StackBox being searched for.
109      * @return true if the specified StackBox matches this or one of its descendants.
110      */
contains(int stackBoxId)111     boolean contains(int stackBoxId) {
112         return mStackBoxId == stackBoxId ||
113                 (mStack == null &&  (mFirst.contains(stackBoxId) || mSecond.contains(stackBoxId)));
114     }
115 
116     /**
117      * Return the stackId of the stack that intersects the passed point.
118      * @param x coordinate of point.
119      * @param y coordinate of point.
120      * @return -1 if point is outside of mBounds, otherwise the stackId of the containing stack.
121      */
stackIdFromPoint(int x, int y)122     int stackIdFromPoint(int x, int y) {
123         if (!mBounds.contains(x, y)) {
124             return -1;
125         }
126         if (mStack != null) {
127             return mStack.mStackId;
128         }
129         int stackId = mFirst.stackIdFromPoint(x, y);
130         if (stackId >= 0) {
131             return stackId;
132         }
133         return mSecond.stackIdFromPoint(x, y);
134     }
135 
136     /** Determine if this StackBox is the first child or second child.
137      * @return true if this is the first child.
138      */
isFirstChild()139     boolean isFirstChild() {
140         return mParent != null && mParent.mFirst == this;
141     }
142 
143     /** Returns the bounds of the specified TaskStack if it is contained in this StackBox.
144      * @param stackId the TaskStack to find the bounds of.
145      * @return a new Rect with the bounds of stackId if it is within this StackBox, null otherwise.
146      */
getStackBounds(int stackId)147     Rect getStackBounds(int stackId) {
148         if (mStack != null) {
149             return mStack.mStackId == stackId ? new Rect(mBounds) : null;
150         }
151         Rect bounds = mFirst.getStackBounds(stackId);
152         if (bounds != null) {
153             return bounds;
154         }
155         return mSecond.getStackBounds(stackId);
156     }
157 
158     /**
159      * Create a new TaskStack relative to a specified one by splitting the StackBox containing
160      * the specified TaskStack into two children. The size and position each of the new StackBoxes
161      * is determined by the passed parameters.
162      * @param stackId The id of the new TaskStack to create.
163      * @param relativeStackBoxId The id of the StackBox to place the new TaskStack next to.
164      * @param position One of the static TASK_STACK_GOES_xxx positions defined in this class.
165      * @param weight The percentage size of the parent StackBox to devote to the new TaskStack.
166      * @return The new TaskStack.
167      */
split(int stackId, int relativeStackBoxId, int position, float weight)168     TaskStack split(int stackId, int relativeStackBoxId, int position, float weight) {
169         if (mStackBoxId != relativeStackBoxId) {
170             // This is not the targeted StackBox.
171             if (mStack != null) {
172                 return null;
173             }
174             // Propagate the split to see if the targeted StackBox is in either sub box.
175             TaskStack stack = mFirst.split(stackId, relativeStackBoxId, position, weight);
176             if (stack != null) {
177                 return stack;
178             }
179             return mSecond.split(stackId, relativeStackBoxId, position, weight);
180         }
181 
182         // Found it!
183         TaskStack stack = new TaskStack(mService, stackId, mDisplayContent);
184         TaskStack firstStack;
185         TaskStack secondStack;
186         if (position == TASK_STACK_GOES_BEFORE) {
187             // TODO: Test Configuration here for LTR/RTL.
188             position = TASK_STACK_TO_LEFT_OF;
189         } else if (position == TASK_STACK_GOES_AFTER) {
190             // TODO: Test Configuration here for LTR/RTL.
191             position = TASK_STACK_TO_RIGHT_OF;
192         }
193         switch (position) {
194             default:
195             case TASK_STACK_TO_LEFT_OF:
196             case TASK_STACK_TO_RIGHT_OF:
197                 mVertical = false;
198                 if (position == TASK_STACK_TO_LEFT_OF) {
199                     mWeight = weight;
200                     firstStack = stack;
201                     secondStack = mStack;
202                 } else {
203                     mWeight = 1.0f - weight;
204                     firstStack = mStack;
205                     secondStack = stack;
206                 }
207                 break;
208             case TASK_STACK_GOES_ABOVE:
209             case TASK_STACK_GOES_BELOW:
210                 mVertical = true;
211                 if (position == TASK_STACK_GOES_ABOVE) {
212                     mWeight = weight;
213                     firstStack = stack;
214                     secondStack = mStack;
215                 } else {
216                     mWeight = 1.0f - weight;
217                     firstStack = mStack;
218                     secondStack = stack;
219                 }
220                 break;
221         }
222 
223         mFirst = new StackBox(mService, mDisplayContent, this);
224         firstStack.mStackBox = mFirst;
225         mFirst.mStack = firstStack;
226 
227         mSecond = new StackBox(mService, mDisplayContent, this);
228         secondStack.mStackBox = mSecond;
229         mSecond.mStack = secondStack;
230 
231         mStack = null;
232         return stack;
233     }
234 
235     /** Return the stackId of the first mFirst StackBox with a non-null mStack */
getStackId()236     int getStackId() {
237         if (mStack != null) {
238             return mStack.mStackId;
239         }
240         return mFirst.getStackId();
241     }
242 
243     /** Remove this box and propagate its sibling's content up to their parent.
244      * @return The first stackId of the resulting StackBox. */
remove()245     int remove() {
246         mDisplayContent.layoutNeeded = true;
247 
248         if (mParent == null) {
249             // This is the top-plane stack.
250             if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: removing top plane.");
251             mDisplayContent.removeStackBox(this);
252             return HOME_STACK_ID;
253         }
254 
255         StackBox sibling = isFirstChild() ? mParent.mSecond : mParent.mFirst;
256         StackBox grandparent = mParent.mParent;
257         sibling.mParent = grandparent;
258         if (grandparent == null) {
259             // mParent is a top-plane stack. Now sibling will be.
260             if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: grandparent null");
261             mDisplayContent.removeStackBox(mParent);
262             mDisplayContent.addStackBox(sibling, true);
263         } else {
264             if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: grandparent getting sibling");
265             if (mParent.isFirstChild()) {
266                 grandparent.mFirst = sibling;
267             } else {
268                 grandparent.mSecond = sibling;
269             }
270         }
271         return sibling.getStackId();
272     }
273 
resize(int stackBoxId, float weight)274     boolean resize(int stackBoxId, float weight) {
275         if (mStackBoxId != stackBoxId) {
276             return mStack == null &&
277                     (mFirst.resize(stackBoxId, weight) || mSecond.resize(stackBoxId, weight));
278         }
279         // Don't change weight on topmost stack.
280         if (mParent != null) {
281             mParent.mWeight = isFirstChild() ? weight : 1.0f - weight;
282         }
283         return true;
284     }
285 
286     /** If this is a terminal StackBox (contains a TaskStack) set the bounds.
287      * @param bounds The rectangle to set the bounds to.
288      * @param underStatusBar True if the StackBox is directly below the Status Bar.
289      * @return True if the bounds changed, false otherwise. */
setStackBoxSizes(Rect bounds, boolean underStatusBar)290     boolean setStackBoxSizes(Rect bounds, boolean underStatusBar) {
291         boolean change = false;
292         if (mUnderStatusBar != underStatusBar) {
293             change = true;
294             mUnderStatusBar = underStatusBar;
295         }
296         if (mStack != null) {
297             change |= !mBounds.equals(bounds);
298             if (change) {
299                 mBounds.set(bounds);
300                 mStack.setBounds(bounds, underStatusBar);
301             }
302         } else {
303             mTmpRect.set(bounds);
304             if (mVertical) {
305                 final int height = bounds.height();
306                 int firstHeight = (int)(height * mWeight);
307                 mTmpRect.bottom = bounds.top + firstHeight;
308                 change |= mFirst.setStackBoxSizes(mTmpRect, underStatusBar);
309                 mTmpRect.top = mTmpRect.bottom;
310                 mTmpRect.bottom = bounds.top + height;
311                 change |= mSecond.setStackBoxSizes(mTmpRect, false);
312             } else {
313                 final int width = bounds.width();
314                 int firstWidth = (int)(width * mWeight);
315                 mTmpRect.right = bounds.left + firstWidth;
316                 change |= mFirst.setStackBoxSizes(mTmpRect, underStatusBar);
317                 mTmpRect.left = mTmpRect.right;
318                 mTmpRect.right = bounds.left + width;
319                 change |= mSecond.setStackBoxSizes(mTmpRect, underStatusBar);
320             }
321         }
322         return change;
323     }
324 
resetAnimationBackgroundAnimator()325     void resetAnimationBackgroundAnimator() {
326         if (mStack != null) {
327             mStack.resetAnimationBackgroundAnimator();
328             return;
329         }
330         mFirst.resetAnimationBackgroundAnimator();
331         mSecond.resetAnimationBackgroundAnimator();
332     }
333 
animateDimLayers()334     boolean animateDimLayers() {
335         if (mStack != null) {
336             return mStack.animateDimLayers();
337         }
338         boolean result = mFirst.animateDimLayers();
339         result |= mSecond.animateDimLayers();
340         return result;
341     }
342 
resetDimming()343     void resetDimming() {
344         if (mStack != null) {
345             mStack.resetDimmingTag();
346             return;
347         }
348         mFirst.resetDimming();
349         mSecond.resetDimming();
350     }
351 
isDimming()352     boolean isDimming() {
353         if (mStack != null) {
354             return mStack.isDimming();
355         }
356         boolean result = mFirst.isDimming();
357         result |= mSecond.isDimming();
358         return result;
359     }
360 
stopDimmingIfNeeded()361     void stopDimmingIfNeeded() {
362         if (mStack != null) {
363             mStack.stopDimmingIfNeeded();
364             return;
365         }
366         mFirst.stopDimmingIfNeeded();
367         mSecond.stopDimmingIfNeeded();
368     }
369 
switchUserStacks(int userId)370     void switchUserStacks(int userId) {
371         if (mStack != null) {
372             mStack.switchUser(userId);
373             return;
374         }
375         mFirst.switchUserStacks(userId);
376         mSecond.switchUserStacks(userId);
377     }
378 
close()379     void close() {
380         if (mStack != null) {
381             mStack.mDimLayer.mDimSurface.destroy();
382             mStack.mAnimationBackgroundSurface.mDimSurface.destroy();
383             return;
384         }
385         mFirst.close();
386         mSecond.close();
387     }
388 
dump(String prefix, PrintWriter pw)389     public void dump(String prefix, PrintWriter pw) {
390         pw.print(prefix); pw.print("mParent="); pw.println(mParent);
391         pw.print(prefix); pw.print("mBounds="); pw.print(mBounds.toShortString());
392             pw.print(" mVertical="); pw.print(mVertical);
393             pw.print(" layoutNeeded="); pw.println(layoutNeeded);
394         if (mFirst != null) {
395             pw.print(prefix); pw.print("mFirst="); pw.println(System.identityHashCode(mFirst));
396             mFirst.dump(prefix + "  ", pw);
397             pw.print(prefix); pw.print("mSecond="); pw.println(System.identityHashCode(mSecond));
398             mSecond.dump(prefix + "  ", pw);
399         } else {
400             pw.print(prefix); pw.print("mStack="); pw.println(mStack);
401             mStack.dump(prefix + "  ", pw);
402         }
403     }
404 
405     @Override
toString()406     public String toString() {
407         if (mStack != null) {
408             return "Box{" + hashCode() + " stack=" + mStack.mStackId + "}";
409         }
410         return "Box{" + hashCode() + " parent=" + System.identityHashCode(mParent)
411                 + " first=" + System.identityHashCode(mFirst)
412                 + " second=" + System.identityHashCode(mSecond) + "}";
413     }
414 }
415