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 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 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. */ 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 325 void resetAnimationBackgroundAnimator() { 326 if (mStack != null) { 327 mStack.resetAnimationBackgroundAnimator(); 328 return; 329 } 330 mFirst.resetAnimationBackgroundAnimator(); 331 mSecond.resetAnimationBackgroundAnimator(); 332 } 333 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 343 void resetDimming() { 344 if (mStack != null) { 345 mStack.resetDimmingTag(); 346 return; 347 } 348 mFirst.resetDimming(); 349 mSecond.resetDimming(); 350 } 351 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 361 void stopDimmingIfNeeded() { 362 if (mStack != null) { 363 mStack.stopDimmingIfNeeded(); 364 return; 365 } 366 mFirst.stopDimmingIfNeeded(); 367 mSecond.stopDimmingIfNeeded(); 368 } 369 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 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 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 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