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