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 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. */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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. */ 245 int remove() { 246 if (mStack != null) { 247 if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: removing stackId=" + mStack.mStackId); 248 mDisplayContent.mStackHistory.remove(mStack); 249 } 250 mDisplayContent.layoutNeeded = true; 251 252 if (mParent == null) { 253 // This is the top-plane stack. 254 if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: removing top plane."); 255 mDisplayContent.removeStackBox(this); 256 return HOME_STACK_ID; 257 } 258 259 StackBox sibling = isFirstChild() ? mParent.mSecond : mParent.mFirst; 260 StackBox grandparent = mParent.mParent; 261 sibling.mParent = grandparent; 262 if (grandparent == null) { 263 // mParent is a top-plane stack. Now sibling will be. 264 if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: grandparent null"); 265 mDisplayContent.removeStackBox(mParent); 266 mDisplayContent.addStackBox(sibling, true); 267 } else { 268 if (DEBUG_STACK) Slog.i(TAG, "StackBox.remove: grandparent getting sibling"); 269 if (mParent.isFirstChild()) { 270 grandparent.mFirst = sibling; 271 } else { 272 grandparent.mSecond = sibling; 273 } 274 } 275 return sibling.getStackId(); 276 } 277 278 boolean resize(int stackBoxId, float weight) { 279 if (mStackBoxId != stackBoxId) { 280 return mStack == null && 281 (mFirst.resize(stackBoxId, weight) || mSecond.resize(stackBoxId, weight)); 282 } 283 // Don't change weight on topmost stack. 284 if (mParent != null) { 285 mParent.mWeight = isFirstChild() ? weight : 1.0f - weight; 286 } 287 return true; 288 } 289 290 /** If this is a terminal StackBox (contains a TaskStack) set the bounds. 291 * @param bounds The rectangle to set the bounds to. 292 * @param underStatusBar True if the StackBox is directly below the Status Bar. 293 * @return True if the bounds changed, false otherwise. */ 294 boolean setStackBoxSizes(Rect bounds, boolean underStatusBar) { 295 boolean change = false; 296 if (mUnderStatusBar != underStatusBar) { 297 change = true; 298 mUnderStatusBar = underStatusBar; 299 } 300 if (mStack != null) { 301 change |= !mBounds.equals(bounds); 302 if (change) { 303 mBounds.set(bounds); 304 mStack.setBounds(bounds, underStatusBar); 305 } 306 } else { 307 mTmpRect.set(bounds); 308 if (mVertical) { 309 final int height = bounds.height(); 310 int firstHeight = (int)(height * mWeight); 311 mTmpRect.bottom = bounds.top + firstHeight; 312 change |= mFirst.setStackBoxSizes(mTmpRect, underStatusBar); 313 mTmpRect.top = mTmpRect.bottom; 314 mTmpRect.bottom = bounds.top + height; 315 change |= mSecond.setStackBoxSizes(mTmpRect, false); 316 } else { 317 final int width = bounds.width(); 318 int firstWidth = (int)(width * mWeight); 319 mTmpRect.right = bounds.left + firstWidth; 320 change |= mFirst.setStackBoxSizes(mTmpRect, underStatusBar); 321 mTmpRect.left = mTmpRect.right; 322 mTmpRect.right = bounds.left + width; 323 change |= mSecond.setStackBoxSizes(mTmpRect, underStatusBar); 324 } 325 } 326 return change; 327 } 328 329 void resetAnimationBackgroundAnimator() { 330 if (mStack != null) { 331 mStack.resetAnimationBackgroundAnimator(); 332 return; 333 } 334 mFirst.resetAnimationBackgroundAnimator(); 335 mSecond.resetAnimationBackgroundAnimator(); 336 } 337 338 boolean animateDimLayers() { 339 if (mStack != null) { 340 return mStack.animateDimLayers(); 341 } 342 boolean result = mFirst.animateDimLayers(); 343 result |= mSecond.animateDimLayers(); 344 return result; 345 } 346 347 void resetDimming() { 348 if (mStack != null) { 349 mStack.resetDimmingTag(); 350 return; 351 } 352 mFirst.resetDimming(); 353 mSecond.resetDimming(); 354 } 355 356 boolean isDimming() { 357 if (mStack != null) { 358 return mStack.isDimming(); 359 } 360 boolean result = mFirst.isDimming(); 361 result |= mSecond.isDimming(); 362 return result; 363 } 364 365 void stopDimmingIfNeeded() { 366 if (mStack != null) { 367 mStack.stopDimmingIfNeeded(); 368 return; 369 } 370 mFirst.stopDimmingIfNeeded(); 371 mSecond.stopDimmingIfNeeded(); 372 } 373 374 void switchUserStacks(int userId) { 375 if (mStack != null) { 376 mStack.switchUser(userId); 377 return; 378 } 379 mFirst.switchUserStacks(userId); 380 mSecond.switchUserStacks(userId); 381 } 382 383 void close() { 384 if (mStack != null) { 385 mStack.mDimLayer.mDimSurface.destroy(); 386 mStack.mAnimationBackgroundSurface.mDimSurface.destroy(); 387 return; 388 } 389 mFirst.close(); 390 mSecond.close(); 391 } 392 393 public void dump(String prefix, PrintWriter pw) { 394 pw.print(prefix); pw.print("mParent="); pw.println(mParent); 395 pw.print(prefix); pw.print("mBounds="); pw.print(mBounds.toShortString()); 396 pw.print(" mVertical="); pw.print(mVertical); 397 pw.print(" layoutNeeded="); pw.println(layoutNeeded); 398 if (mFirst != null) { 399 pw.print(prefix); pw.print("mFirst="); pw.println(System.identityHashCode(mFirst)); 400 mFirst.dump(prefix + " ", pw); 401 pw.print(prefix); pw.print("mSecond="); pw.println(System.identityHashCode(mSecond)); 402 mSecond.dump(prefix + " ", pw); 403 } else { 404 pw.print(prefix); pw.print("mStack="); pw.println(mStack); 405 mStack.dump(prefix + " ", pw); 406 } 407 } 408 409 @Override 410 public String toString() { 411 if (mStack != null) { 412 return "Box{" + hashCode() + " stack=" + mStack.mStackId + "}"; 413 } 414 return "Box{" + hashCode() + " parent=" + System.identityHashCode(mParent) 415 + " first=" + System.identityHashCode(mFirst) 416 + " second=" + System.identityHashCode(mSecond) + "}"; 417 } 418 } 419