WindowContainer.java revision 1012458343643e899ed8ca2684380b92d73fb47b
1/* 2 * Copyright (C) 2016 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 android.annotation.CallSuper; 20import android.view.animation.Animation; 21 22import java.io.PrintWriter; 23import java.util.Comparator; 24import java.util.LinkedList; 25 26import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; 27import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 28import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 29 30/** 31 * Defines common functionality for classes that can hold windows directly or through their 32 * children in a hierarchy form. 33 * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime 34 * changes are made to this class. 35 */ 36class WindowContainer<E extends WindowContainer> implements Comparable<WindowContainer> { 37 38 // The parent of this window container. 39 protected WindowContainer mParent = null; 40 41 // List of children for this window container. List is in z-order as the children appear on 42 // screen with the top-most window container at the tail of the list. 43 protected final LinkedList<E> mChildren = new LinkedList(); 44 45 // The specified orientation for this window container. 46 protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED; 47 48 final protected WindowContainer getParent() { 49 return mParent; 50 } 51 52 // Temp. holders for a chain of containers we are currently processing. 53 private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList(); 54 private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList(); 55 56 /** 57 * Adds the input window container has a child of this container in order based on the input 58 * comparator. 59 * @param child The window container to add as a child of this window container. 60 * @param comparator Comparator to use in determining the position the child should be added to. 61 * If null, the child will be added to the top. 62 */ 63 @CallSuper 64 protected void addChild(E child, Comparator<E> comparator) { 65 if (child.mParent != null) { 66 throw new IllegalArgumentException("addChild: container=" + child 67 + " is already a child of container=" + child.mParent 68 + " can't add to container=" + this); 69 } 70 child.mParent = this; 71 72 if (mChildren.isEmpty() || comparator == null) { 73 mChildren.add(child); 74 return; 75 } 76 77 final int count = mChildren.size(); 78 for (int i = 0; i < count; i++) { 79 if (comparator.compare(child, mChildren.get(i)) < 0) { 80 mChildren.add(i, child); 81 return; 82 } 83 } 84 85 mChildren.add(child); 86 } 87 88 /** Adds the input window container has a child of this container at the input index. */ 89 @CallSuper 90 protected void addChild(E child, int index) { 91 if (child.mParent != null) { 92 throw new IllegalArgumentException("addChild: container=" + child 93 + " is already a child of container=" + child.mParent 94 + " can't add to container=" + this); 95 } 96 child.mParent = this; 97 mChildren.add(index, child); 98 } 99 100 /** 101 * Removes the input child container from this container which is its parent. 102 * 103 * @return True if the container did contain the input child and it was detached. 104 */ 105 @CallSuper 106 void removeChild(E child) { 107 if (mChildren.remove(child)) { 108 child.mParent = null; 109 } else { 110 throw new IllegalArgumentException("removeChild: container=" + child 111 + " is not a child of container=" + this); 112 } 113 } 114 115 /** 116 * Removes this window container and its children with no regard for what else might be going on 117 * in the system. For example, the container will be removed during animation if this method is 118 * called which isn't desirable. For most cases you want to call {@link #removeIfPossible()} 119 * which allows the system to defer removal until a suitable time. 120 */ 121 @CallSuper 122 void removeImmediately() { 123 while (!mChildren.isEmpty()) { 124 final WindowContainer child = mChildren.peekLast(); 125 child.removeImmediately(); 126 // Need to do this after calling remove on the child because the child might try to 127 // remove/detach itself from its parent which will cause an exception if we remove 128 // it before calling remove on the child. 129 mChildren.remove(child); 130 } 131 132 if (mParent != null) { 133 mParent.removeChild(this); 134 } 135 } 136 137 /** 138 * Removes this window container and its children taking care not to remove them during a 139 * critical stage in the system. For example, some containers will not be removed during 140 * animation if this method is called. 141 */ 142 // TODO: figure-out implementation that works best for this. 143 // E.g. when do we remove from parent list? maybe not... 144 void removeIfPossible() { 145 for (int i = mChildren.size() - 1; i >= 0; --i) { 146 final WindowContainer wc = mChildren.get(i); 147 wc.removeIfPossible(); 148 } 149 } 150 151 /** Returns true if this window container has the input child. */ 152 boolean hasChild(WindowContainer child) { 153 for (int i = mChildren.size() - 1; i >= 0; --i) { 154 final WindowContainer current = mChildren.get(i); 155 if (current == child || current.hasChild(child)) { 156 return true; 157 } 158 } 159 return false; 160 } 161 162 void setWaitingForDrawnIfResizingChanged() { 163 for (int i = mChildren.size() - 1; i >= 0; --i) { 164 final WindowContainer wc = mChildren.get(i); 165 wc.setWaitingForDrawnIfResizingChanged(); 166 } 167 } 168 169 void onResize() { 170 for (int i = mChildren.size() - 1; i >= 0; --i) { 171 final WindowContainer wc = mChildren.get(i); 172 wc.onResize(); 173 } 174 } 175 176 void onMovedByResize() { 177 for (int i = mChildren.size() - 1; i >= 0; --i) { 178 final WindowContainer wc = mChildren.get(i); 179 wc.onMovedByResize(); 180 } 181 } 182 183 void resetDragResizingChangeReported() { 184 for (int i = mChildren.size() - 1; i >= 0; --i) { 185 final WindowContainer wc = mChildren.get(i); 186 wc.resetDragResizingChangeReported(); 187 } 188 } 189 190 void forceWindowsScaleableInTransaction(boolean force) { 191 for (int i = mChildren.size() - 1; i >= 0; --i) { 192 final WindowContainer wc = mChildren.get(i); 193 wc.forceWindowsScaleableInTransaction(force); 194 } 195 } 196 197 boolean isAnimating() { 198 for (int j = mChildren.size() - 1; j >= 0; j--) { 199 final WindowContainer wc = mChildren.get(j); 200 if (wc.isAnimating()) { 201 return true; 202 } 203 } 204 return false; 205 } 206 207 void sendAppVisibilityToClients() { 208 for (int i = mChildren.size() - 1; i >= 0; --i) { 209 final WindowContainer wc = mChildren.get(i); 210 wc.sendAppVisibilityToClients(); 211 } 212 } 213 214 void setVisibleBeforeClientHidden() { 215 for (int i = mChildren.size() - 1; i >= 0; --i) { 216 final WindowContainer wc = mChildren.get(i); 217 wc.setVisibleBeforeClientHidden(); 218 } 219 } 220 221 /** 222 * Returns true if the container or one of its children as some content it can display or wants 223 * to display (e.g. app views or saved surface). 224 * 225 * NOTE: While this method will return true if the there is some content to display, it doesn't 226 * mean the container is visible. Use {@link #isVisible()} to determine if the container is 227 * visible. 228 */ 229 boolean hasContentToDisplay() { 230 for (int i = mChildren.size() - 1; i >= 0; --i) { 231 final WindowContainer wc = mChildren.get(i); 232 if (wc.hasContentToDisplay()) { 233 return true; 234 } 235 } 236 return false; 237 } 238 239 /** 240 * Returns true if the container or one of its children is considered visible from the 241 * WindowManager perspective which usually means valid surface and some other internal state 242 * are true. 243 * 244 * NOTE: While this method will return true if the surface is visible, it doesn't mean the 245 * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if 246 * the container has any content to display. 247 */ 248 boolean isVisible() { 249 // TODO: Will this be more correct if it checks the visibility of its parents? 250 // It depends...For example, Tasks and Stacks are only visible if there children are visible 251 // but, WindowState are not visible if there parent are not visible. Maybe have the 252 // container specify which direction to treverse for for visibility? 253 for (int i = mChildren.size() - 1; i >= 0; --i) { 254 final WindowContainer wc = mChildren.get(i); 255 if (wc.isVisible()) { 256 return true; 257 } 258 } 259 return false; 260 } 261 262 /** Returns the top child container or this container if there are no children. */ 263 WindowContainer getTop() { 264 return mChildren.isEmpty() ? this : mChildren.peekLast(); 265 } 266 267 /** Returns true if there is still a removal being deferred */ 268 boolean checkCompleteDeferredRemoval() { 269 boolean stillDeferringRemoval = false; 270 271 for (int i = mChildren.size() - 1; i >= 0; --i) { 272 final WindowContainer wc = mChildren.get(i); 273 stillDeferringRemoval |= wc.checkCompleteDeferredRemoval(); 274 } 275 276 return stillDeferringRemoval; 277 } 278 279 /** Checks if all windows in an app are all drawn and shows them if needed. */ 280 // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one 281 // display. Remove once we migrate DisplayContent to use WindowContainer. 282 void checkAppWindowsReadyToShow(int displayId) { 283 for (int i = mChildren.size() - 1; i >= 0; --i) { 284 final WindowContainer wc = mChildren.get(i); 285 wc.checkAppWindowsReadyToShow(displayId); 286 } 287 } 288 289 /** 290 * Updates the current all drawn status for this container. That is all its children 291 * that should draw something have done so. 292 */ 293 // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one 294 // display. Remove once we migrate DisplayContent to use WindowContainer. 295 void updateAllDrawn(int displayId) { 296 for (int i = mChildren.size() - 1; i >= 0; --i) { 297 final WindowContainer wc = mChildren.get(i); 298 wc.updateAllDrawn(displayId); 299 } 300 } 301 302 /** Step currently ongoing animation for App window containers. */ 303 // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one 304 // display. Remove once we migrate DisplayContent to use WindowContainer. 305 void stepAppWindowsAnimation(long currentTime, int displayId) { 306 for (int i = mChildren.size() - 1; i >= 0; --i) { 307 final WindowContainer wc = mChildren.get(i); 308 wc.stepAppWindowsAnimation(currentTime, displayId); 309 } 310 } 311 312 void onAppTransitionDone() { 313 for (int i = mChildren.size() - 1; i >= 0; --i) { 314 final WindowContainer wc = mChildren.get(i); 315 wc.onAppTransitionDone(); 316 } 317 } 318 319 void overridePlayingAppAnimations(Animation a) { 320 for (int i = mChildren.size() - 1; i >= 0; i--) { 321 mChildren.get(i).overridePlayingAppAnimations(a); 322 } 323 } 324 325 void setOrientation(int orientation) { 326 mOrientation = orientation; 327 } 328 329 /** 330 * Returns the specified orientation for this window container or one of its children is there 331 * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no 332 * specification is set. 333 * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a 334 * specification... 335 */ 336 int getOrientation() { 337 338 if (!fillsParent() || !isVisible()) { 339 // Ignore invisible containers or containers that don't completely fills their parents. 340 return SCREEN_ORIENTATION_UNSET; 341 } 342 343 // The container fills its parent so we can use it orientation if it has one specified, 344 // otherwise we prefer to use the orientation of its topmost child that has one 345 // specified and fall back on this container's unset or unspecified value as a candidate 346 // if none of the children have a better candidate for the orientation. 347 if (mOrientation != SCREEN_ORIENTATION_UNSET 348 && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) { 349 return mOrientation; 350 } 351 int candidate = mOrientation; 352 353 for (int i = mChildren.size() - 1; i >= 0; --i) { 354 final WindowContainer wc = mChildren.get(i); 355 356 final int orientation = wc.getOrientation(); 357 if (orientation == SCREEN_ORIENTATION_BEHIND) { 358 // container wants us to use the orientation of the container behind it. See if we 359 // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to 360 // look behind this container. 361 candidate = orientation; 362 continue; 363 } 364 365 if (orientation == SCREEN_ORIENTATION_UNSET) { 366 continue; 367 } 368 369 if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) { 370 // Use the orientation if the container fills its parent or requested an explicit 371 // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED. 372 return orientation; 373 } 374 } 375 376 return candidate; 377 } 378 379 /** 380 * Returns true if this container is opaque and fills all the space made available by its parent 381 * container. 382 * 383 * NOTE: It is possible for this container to occupy more space than the parent has (or less), 384 * this is just a signal from the client to window manager stating its intent, but not what it 385 * actually does. 386 */ 387 boolean fillsParent() { 388 return false; 389 } 390 391 /** 392 * Rebuilds the WindowList for the input display content. 393 * @param dc The display content to rebuild the window list for. 394 * @param addIndex The index in the window list to add the next entry to. 395 * @return The next index in the window list to. 396 */ 397 // TODO: Hoping we can get rid of WindowList so this method wouldn't be needed. 398 int rebuildWindowList(DisplayContent dc, int addIndex) { 399 final int count = mChildren.size(); 400 for (int i = 0; i < count; i++) { 401 final WindowContainer wc = mChildren.get(i); 402 addIndex = wc.rebuildWindowList(dc, addIndex); 403 } 404 return addIndex; 405 } 406 407 /** 408 * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than 409 * the input container in terms of z-order. 410 */ 411 @Override 412 public int compareTo(WindowContainer other) { 413 if (this == other) { 414 return 0; 415 } 416 417 if (mParent != null && mParent == other.mParent) { 418 final LinkedList<WindowContainer> list = mParent.mChildren; 419 return list.indexOf(this) > list.indexOf(other) ? 1 : -1; 420 } 421 422 final LinkedList<WindowContainer> thisParentChain = mTmpChain1; 423 final LinkedList<WindowContainer> otherParentChain = mTmpChain2; 424 getParents(thisParentChain); 425 other.getParents(otherParentChain); 426 427 // Find the common ancestor of both containers. 428 WindowContainer commonAncestor = null; 429 WindowContainer thisTop = thisParentChain.peekLast(); 430 WindowContainer otherTop = otherParentChain.peekLast(); 431 while (thisTop != null && otherTop != null && thisTop == otherTop) { 432 commonAncestor = thisParentChain.removeLast(); 433 otherParentChain.removeLast(); 434 thisTop = thisParentChain.peekLast(); 435 otherTop = otherParentChain.peekLast(); 436 } 437 438 // Containers don't belong to the same hierarchy??? 439 if (commonAncestor == null) { 440 throw new IllegalArgumentException("No in the same hierarchy this=" 441 + thisParentChain + " other=" + otherParentChain); 442 } 443 444 // Children are always considered greater than their parents, so if one of the containers 445 // we are comparing it the parent of the other then whichever is the child is greater. 446 if (commonAncestor == this) { 447 return -1; 448 } else if (commonAncestor == other) { 449 return 1; 450 } 451 452 // The position of the first non-common ancestor in the common ancestor list determines 453 // which is greater the which. 454 final LinkedList<WindowContainer> list = commonAncestor.mChildren; 455 return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast()) 456 ? 1 : -1; 457 } 458 459 private void getParents(LinkedList<WindowContainer> parents) { 460 parents.clear(); 461 WindowContainer current = this; 462 do { 463 parents.addLast(current); 464 current = current.mParent; 465 } while (current != null); 466 } 467 468 /** 469 * Dumps the names of this container children in the input print writer indenting each 470 * level with the input prefix. 471 */ 472 void dumpChildrenNames(PrintWriter pw, String prefix) { 473 final String childPrefix = prefix + prefix; 474 for (int i = mChildren.size() - 1; i >= 0; --i) { 475 final WindowContainer wc = mChildren.get(i); 476 pw.println("#" + i + " " + getName()); 477 wc.dumpChildrenNames(pw, childPrefix); 478 } 479 } 480 481 String getName() { 482 return toString(); 483 } 484 485} 486