blob: 563eb9cd1d88c74b64e0eb8799f3388f89df0984 [file] [log] [blame]
Justin Klaassen10d07c82017-09-15 17:58:39 -04001/*
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 static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
20import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
21import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
Justin Klaassen93b7ee42017-10-10 15:20:13 -040022import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
23import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
Justin Klaassen10d07c82017-09-15 17:58:39 -040024
25import android.annotation.CallSuper;
26import android.content.res.Configuration;
27import android.util.Pools;
28
Justin Klaassen93b7ee42017-10-10 15:20:13 -040029import android.util.proto.ProtoOutputStream;
Justin Klaassen10d07c82017-09-15 17:58:39 -040030import com.android.internal.util.ToBooleanFunction;
31
32import java.util.Comparator;
33import java.util.LinkedList;
34import java.util.function.Consumer;
35import java.util.function.Predicate;
36
37/**
38 * Defines common functionality for classes that can hold windows directly or through their
39 * children in a hierarchy form.
40 * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime
41 * changes are made to this class.
42 */
43class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
44 implements Comparable<WindowContainer> {
45
46 static final int POSITION_TOP = Integer.MAX_VALUE;
47 static final int POSITION_BOTTOM = Integer.MIN_VALUE;
48
49 /**
50 * The parent of this window container.
51 * For removing or setting new parent {@link #setParent} should be used, because it also
52 * performs configuration updates based on new parent's settings.
53 */
54 private WindowContainer mParent = null;
55
56 // List of children for this window container. List is in z-order as the children appear on
57 // screen with the top-most window container at the tail of the list.
58 protected final WindowList<E> mChildren = new WindowList<E>();
59
60 // The specified orientation for this window container.
61 protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
62
63 private final Pools.SynchronizedPool<ForAllWindowsConsumerWrapper> mConsumerWrapperPool =
64 new Pools.SynchronizedPool<>(3);
65
66 // The owner/creator for this container. No controller if null.
67 private WindowContainerController mController;
68
69 @Override
70 final protected WindowContainer getParent() {
71 return mParent;
72 }
73
74
75 @Override
Justin Klaassen47ed54e2017-10-24 19:50:40 -040076 protected int getChildCount() {
Justin Klaassen10d07c82017-09-15 17:58:39 -040077 return mChildren.size();
78 }
79
80 @Override
Justin Klaassen47ed54e2017-10-24 19:50:40 -040081 protected E getChildAt(int index) {
Justin Klaassen10d07c82017-09-15 17:58:39 -040082 return mChildren.get(index);
83 }
84
85 final protected void setParent(WindowContainer parent) {
86 mParent = parent;
87 // Removing parent usually means that we've detached this entity to destroy it or to attach
88 // to another parent. In both cases we don't need to update the configuration now.
89 if (mParent != null) {
90 // Update full configuration of this container and all its children.
91 onConfigurationChanged(mParent.getConfiguration());
92 // Update merged override configuration of this container and all its children.
93 onMergedOverrideConfigurationChanged();
94 }
95
96 onParentSet();
97 }
98
99 /**
100 * Callback that is triggered when @link WindowContainer#setParent(WindowContainer)} was called.
101 * Supposed to be overridden and contain actions that should be executed after parent was set.
102 */
103 void onParentSet() {
104 // Do nothing by default.
105 }
106
107 // Temp. holders for a chain of containers we are currently processing.
108 private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList();
109 private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList();
110
111 /**
112 * Adds the input window container has a child of this container in order based on the input
113 * comparator.
114 * @param child The window container to add as a child of this window container.
115 * @param comparator Comparator to use in determining the position the child should be added to.
116 * If null, the child will be added to the top.
117 */
118 @CallSuper
119 protected void addChild(E child, Comparator<E> comparator) {
120 if (child.getParent() != null) {
121 throw new IllegalArgumentException("addChild: container=" + child.getName()
122 + " is already a child of container=" + child.getParent().getName()
123 + " can't add to container=" + getName());
124 }
125
126 int positionToAdd = -1;
127 if (comparator != null) {
128 final int count = mChildren.size();
129 for (int i = 0; i < count; i++) {
130 if (comparator.compare(child, mChildren.get(i)) < 0) {
131 positionToAdd = i;
132 break;
133 }
134 }
135 }
136
137 if (positionToAdd == -1) {
138 mChildren.add(child);
139 } else {
140 mChildren.add(positionToAdd, child);
141 }
142 // Set the parent after we've actually added a child in case a subclass depends on this.
143 child.setParent(this);
144 }
145
146 /** Adds the input window container has a child of this container at the input index. */
147 @CallSuper
148 void addChild(E child, int index) {
149 if (child.getParent() != null) {
150 throw new IllegalArgumentException("addChild: container=" + child.getName()
151 + " is already a child of container=" + child.getParent().getName()
152 + " can't add to container=" + getName());
153 }
154 mChildren.add(index, child);
155 // Set the parent after we've actually added a child in case a subclass depends on this.
156 child.setParent(this);
157 }
158
159 /**
160 * Removes the input child container from this container which is its parent.
161 *
162 * @return True if the container did contain the input child and it was detached.
163 */
164 @CallSuper
165 void removeChild(E child) {
166 if (mChildren.remove(child)) {
167 child.setParent(null);
168 } else {
169 throw new IllegalArgumentException("removeChild: container=" + child.getName()
170 + " is not a child of container=" + getName());
171 }
172 }
173
174 /**
175 * Removes this window container and its children with no regard for what else might be going on
176 * in the system. For example, the container will be removed during animation if this method is
177 * called which isn't desirable. For most cases you want to call {@link #removeIfPossible()}
178 * which allows the system to defer removal until a suitable time.
179 */
180 @CallSuper
181 void removeImmediately() {
182 while (!mChildren.isEmpty()) {
183 final WindowContainer child = mChildren.peekLast();
184 child.removeImmediately();
185 // Need to do this after calling remove on the child because the child might try to
186 // remove/detach itself from its parent which will cause an exception if we remove
187 // it before calling remove on the child.
188 mChildren.remove(child);
189 }
190
191 if (mParent != null) {
192 mParent.removeChild(this);
193 }
194
195 if (mController != null) {
196 setController(null);
197 }
198 }
199
200 /**
201 * Removes this window container and its children taking care not to remove them during a
202 * critical stage in the system. For example, some containers will not be removed during
203 * animation if this method is called.
204 */
205 // TODO: figure-out implementation that works best for this.
206 // E.g. when do we remove from parent list? maybe not...
207 void removeIfPossible() {
208 for (int i = mChildren.size() - 1; i >= 0; --i) {
209 final WindowContainer wc = mChildren.get(i);
210 wc.removeIfPossible();
211 }
212 }
213
214 /** Returns true if this window container has the input child. */
215 boolean hasChild(WindowContainer child) {
216 for (int i = mChildren.size() - 1; i >= 0; --i) {
217 final WindowContainer current = mChildren.get(i);
218 if (current == child || current.hasChild(child)) {
219 return true;
220 }
221 }
222 return false;
223 }
224
225 /**
226 * Move a child from it's current place in siblings list to the specified position,
227 * with an option to move all its parents to top.
228 * @param position Target position to move the child to.
229 * @param child Child to move to selected position.
230 * @param includingParents Flag indicating whether we need to move the entire branch of the
231 * hierarchy when we're moving a child to {@link #POSITION_TOP} or
232 * {@link #POSITION_BOTTOM}. When moving to other intermediate positions
233 * this flag will do nothing.
234 */
235 @CallSuper
236 void positionChildAt(int position, E child, boolean includingParents) {
237
238 if (child.getParent() != this) {
239 throw new IllegalArgumentException("removeChild: container=" + child.getName()
240 + " is not a child of container=" + getName()
241 + " current parent=" + child.getParent());
242 }
243
244 if ((position < 0 && position != POSITION_BOTTOM)
245 || (position > mChildren.size() && position != POSITION_TOP)) {
246 throw new IllegalArgumentException("positionAt: invalid position=" + position
247 + ", children number=" + mChildren.size());
248 }
249
250 if (position >= mChildren.size() - 1) {
251 position = POSITION_TOP;
252 } else if (position == 0) {
253 position = POSITION_BOTTOM;
254 }
255
256 switch (position) {
257 case POSITION_TOP:
258 if (mChildren.peekLast() != child) {
259 mChildren.remove(child);
260 mChildren.add(child);
261 }
262 if (includingParents && getParent() != null) {
263 getParent().positionChildAt(POSITION_TOP, this /* child */,
264 true /* includingParents */);
265 }
266 break;
267 case POSITION_BOTTOM:
268 if (mChildren.peekFirst() != child) {
269 mChildren.remove(child);
270 mChildren.addFirst(child);
271 }
272 if (includingParents && getParent() != null) {
273 getParent().positionChildAt(POSITION_BOTTOM, this /* child */,
274 true /* includingParents */);
275 }
276 break;
277 default:
278 mChildren.remove(child);
279 mChildren.add(position, child);
280 }
281 }
282
283 /**
284 * Update override configuration and recalculate full config.
285 * @see #mOverrideConfiguration
286 * @see #mFullConfiguration
287 */
288 @Override
289 final public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
290 super.onOverrideConfigurationChanged(overrideConfiguration);
291 if (mParent != null) {
292 mParent.onDescendantOverrideConfigurationChanged();
293 }
294 }
295
296 /**
297 * Notify that a descendant's overrideConfiguration has changed.
298 */
299 void onDescendantOverrideConfigurationChanged() {
300 if (mParent != null) {
301 mParent.onDescendantOverrideConfigurationChanged();
302 }
303 }
304
305 /**
306 * Notify that the display this container is on has changed.
307 * @param dc The new display this container is on.
308 */
309 void onDisplayChanged(DisplayContent dc) {
310 for (int i = mChildren.size() - 1; i >= 0; --i) {
311 final WindowContainer child = mChildren.get(i);
312 child.onDisplayChanged(dc);
313 }
314 }
315
316 void setWaitingForDrawnIfResizingChanged() {
317 for (int i = mChildren.size() - 1; i >= 0; --i) {
318 final WindowContainer wc = mChildren.get(i);
319 wc.setWaitingForDrawnIfResizingChanged();
320 }
321 }
322
323 void onResize() {
324 for (int i = mChildren.size() - 1; i >= 0; --i) {
325 final WindowContainer wc = mChildren.get(i);
326 wc.onResize();
327 }
328 }
329
330 void onMovedByResize() {
331 for (int i = mChildren.size() - 1; i >= 0; --i) {
332 final WindowContainer wc = mChildren.get(i);
333 wc.onMovedByResize();
334 }
335 }
336
337 void resetDragResizingChangeReported() {
338 for (int i = mChildren.size() - 1; i >= 0; --i) {
339 final WindowContainer wc = mChildren.get(i);
340 wc.resetDragResizingChangeReported();
341 }
342 }
343
344 void forceWindowsScaleableInTransaction(boolean force) {
345 for (int i = mChildren.size() - 1; i >= 0; --i) {
346 final WindowContainer wc = mChildren.get(i);
347 wc.forceWindowsScaleableInTransaction(force);
348 }
349 }
350
351 boolean isAnimating() {
352 for (int j = mChildren.size() - 1; j >= 0; j--) {
353 final WindowContainer wc = mChildren.get(j);
354 if (wc.isAnimating()) {
355 return true;
356 }
357 }
358 return false;
359 }
360
361 void sendAppVisibilityToClients() {
362 for (int i = mChildren.size() - 1; i >= 0; --i) {
363 final WindowContainer wc = mChildren.get(i);
364 wc.sendAppVisibilityToClients();
365 }
366 }
367
368 /**
369 * Returns true if the container or one of its children as some content it can display or wants
370 * to display (e.g. app views or saved surface).
371 *
372 * NOTE: While this method will return true if the there is some content to display, it doesn't
373 * mean the container is visible. Use {@link #isVisible()} to determine if the container is
374 * visible.
375 */
376 boolean hasContentToDisplay() {
377 for (int i = mChildren.size() - 1; i >= 0; --i) {
378 final WindowContainer wc = mChildren.get(i);
379 if (wc.hasContentToDisplay()) {
380 return true;
381 }
382 }
383 return false;
384 }
385
386 /**
387 * Returns true if the container or one of its children is considered visible from the
388 * WindowManager perspective which usually means valid surface and some other internal state
389 * are true.
390 *
391 * NOTE: While this method will return true if the surface is visible, it doesn't mean the
392 * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if
393 * the container has any content to display.
394 */
395 boolean isVisible() {
396 // TODO: Will this be more correct if it checks the visibility of its parents?
397 // It depends...For example, Tasks and Stacks are only visible if there children are visible
398 // but, WindowState are not visible if there parent are not visible. Maybe have the
399 // container specify which direction to traverse for visibility?
400 for (int i = mChildren.size() - 1; i >= 0; --i) {
401 final WindowContainer wc = mChildren.get(i);
402 if (wc.isVisible()) {
403 return true;
404 }
405 }
406 return false;
407 }
408
409 /**
410a * Returns whether this child is on top of the window hierarchy.
411 */
412 boolean isOnTop() {
413 return getParent().getTopChild() == this && getParent().isOnTop();
414 }
415
416 /** Returns the top child container. */
417 E getTopChild() {
418 return mChildren.peekLast();
419 }
420
421 /** Returns true if there is still a removal being deferred */
422 boolean checkCompleteDeferredRemoval() {
423 boolean stillDeferringRemoval = false;
424
425 for (int i = mChildren.size() - 1; i >= 0; --i) {
426 final WindowContainer wc = mChildren.get(i);
427 stillDeferringRemoval |= wc.checkCompleteDeferredRemoval();
428 }
429
430 return stillDeferringRemoval;
431 }
432
433 /** Checks if all windows in an app are all drawn and shows them if needed. */
434 void checkAppWindowsReadyToShow() {
435 for (int i = mChildren.size() - 1; i >= 0; --i) {
436 final WindowContainer wc = mChildren.get(i);
437 wc.checkAppWindowsReadyToShow();
438 }
439 }
440
441 /** Step currently ongoing animation for App window containers. */
442 void stepAppWindowsAnimation(long currentTime) {
443 for (int i = mChildren.size() - 1; i >= 0; --i) {
444 final WindowContainer wc = mChildren.get(i);
445 wc.stepAppWindowsAnimation(currentTime);
446 }
447 }
448
449 void onAppTransitionDone() {
450 for (int i = mChildren.size() - 1; i >= 0; --i) {
451 final WindowContainer wc = mChildren.get(i);
452 wc.onAppTransitionDone();
453 }
454 }
455
456 void setOrientation(int orientation) {
457 mOrientation = orientation;
458 }
459
460 int getOrientation() {
461 return getOrientation(mOrientation);
462 }
463
464 /**
465 * Returns the specified orientation for this window container or one of its children is there
466 * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no
467 * specification is set.
468 * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a
469 * specification...
470 *
471 * @param candidate The current orientation candidate that will be returned if we don't find a
472 * better match.
473 * @return The orientation as specified by this branch or the window hierarchy.
474 */
475 int getOrientation(int candidate) {
476 if (!fillsParent()) {
477 // Ignore containers that don't completely fill their parents.
478 return SCREEN_ORIENTATION_UNSET;
479 }
480
481 // The container fills its parent so we can use it orientation if it has one
482 // specified; otherwise we prefer to use the orientation of its topmost child that has one
483 // specified and fall back on this container's unset or unspecified value as a candidate
484 // if none of the children have a better candidate for the orientation.
485 if (mOrientation != SCREEN_ORIENTATION_UNSET
486 && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
487 return mOrientation;
488 }
489
490 for (int i = mChildren.size() - 1; i >= 0; --i) {
491 final WindowContainer wc = mChildren.get(i);
492
493 // TODO: Maybe mOrientation should default to SCREEN_ORIENTATION_UNSET vs.
494 // SCREEN_ORIENTATION_UNSPECIFIED?
495 final int orientation = wc.getOrientation(candidate == SCREEN_ORIENTATION_BEHIND
496 ? SCREEN_ORIENTATION_BEHIND : SCREEN_ORIENTATION_UNSET);
497 if (orientation == SCREEN_ORIENTATION_BEHIND) {
498 // container wants us to use the orientation of the container behind it. See if we
499 // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to
500 // look behind this container.
501 candidate = orientation;
502 continue;
503 }
504
505 if (orientation == SCREEN_ORIENTATION_UNSET) {
506 continue;
507 }
508
509 if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
510 // Use the orientation if the container fills its parent or requested an explicit
511 // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED.
512 return orientation;
513 }
514 }
515
516 return candidate;
517 }
518
519 /**
520 * Returns true if this container is opaque and fills all the space made available by its parent
521 * container.
522 *
523 * NOTE: It is possible for this container to occupy more space than the parent has (or less),
524 * this is just a signal from the client to window manager stating its intent, but not what it
525 * actually does.
526 */
527 boolean fillsParent() {
528 return false;
529 }
530
531 // TODO: Users would have their own window containers under the display container?
532 void switchUser() {
533 for (int i = mChildren.size() - 1; i >= 0; --i) {
534 mChildren.get(i).switchUser();
535 }
536 }
537
538 /**
539 * For all windows at or below this container call the callback.
540 * @param callback Calls the {@link ToBooleanFunction#apply} method for each window found and
541 * stops the search if {@link ToBooleanFunction#apply} returns true.
542 * @param traverseTopToBottom If true traverses the hierarchy from top-to-bottom in terms of
543 * z-order, else from bottom-to-top.
544 * @return True if the search ended before we reached the end of the hierarchy due to
545 * {@link ToBooleanFunction#apply} returning true.
546 */
547 boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
548 if (traverseTopToBottom) {
549 for (int i = mChildren.size() - 1; i >= 0; --i) {
550 if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
551 return true;
552 }
553 }
554 } else {
555 final int count = mChildren.size();
556 for (int i = 0; i < count; i++) {
557 if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
558 return true;
559 }
560 }
561 }
562 return false;
563 }
564
565 void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) {
566 ForAllWindowsConsumerWrapper wrapper = obtainConsumerWrapper(callback);
567 forAllWindows(wrapper, traverseTopToBottom);
568 wrapper.release();
569 }
570
571 /**
572 * For all tasks at or below this container call the callback.
573 *
574 * @param callback Callback to be called for every task.
575 */
576 void forAllTasks(Consumer<Task> callback) {
577 for (int i = mChildren.size() - 1; i >= 0; --i) {
578 mChildren.get(i).forAllTasks(callback);
579 }
580 }
581
582 WindowState getWindow(Predicate<WindowState> callback) {
583 for (int i = mChildren.size() - 1; i >= 0; --i) {
584 final WindowState w = mChildren.get(i).getWindow(callback);
585 if (w != null) {
586 return w;
587 }
588 }
589
590 return null;
591 }
592
593 /**
594 * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
595 * the input container in terms of z-order.
596 */
597 @Override
598 public int compareTo(WindowContainer other) {
599 if (this == other) {
600 return 0;
601 }
602
603 if (mParent != null && mParent == other.mParent) {
604 final WindowList<WindowContainer> list = mParent.mChildren;
605 return list.indexOf(this) > list.indexOf(other) ? 1 : -1;
606 }
607
608 final LinkedList<WindowContainer> thisParentChain = mTmpChain1;
609 final LinkedList<WindowContainer> otherParentChain = mTmpChain2;
610 try {
611 getParents(thisParentChain);
612 other.getParents(otherParentChain);
613
614 // Find the common ancestor of both containers.
615 WindowContainer commonAncestor = null;
616 WindowContainer thisTop = thisParentChain.peekLast();
617 WindowContainer otherTop = otherParentChain.peekLast();
618 while (thisTop != null && otherTop != null && thisTop == otherTop) {
619 commonAncestor = thisParentChain.removeLast();
620 otherParentChain.removeLast();
621 thisTop = thisParentChain.peekLast();
622 otherTop = otherParentChain.peekLast();
623 }
624
625 // Containers don't belong to the same hierarchy???
626 if (commonAncestor == null) {
627 throw new IllegalArgumentException("No in the same hierarchy this="
628 + thisParentChain + " other=" + otherParentChain);
629 }
630
631 // Children are always considered greater than their parents, so if one of the containers
632 // we are comparing it the parent of the other then whichever is the child is greater.
633 if (commonAncestor == this) {
634 return -1;
635 } else if (commonAncestor == other) {
636 return 1;
637 }
638
639 // The position of the first non-common ancestor in the common ancestor list determines
640 // which is greater the which.
641 final WindowList<WindowContainer> list = commonAncestor.mChildren;
642 return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast())
643 ? 1 : -1;
644 } finally {
645 mTmpChain1.clear();
646 mTmpChain2.clear();
647 }
648 }
649
650 private void getParents(LinkedList<WindowContainer> parents) {
651 parents.clear();
652 WindowContainer current = this;
653 do {
654 parents.addLast(current);
655 current = current.mParent;
656 } while (current != null);
657 }
658
659 WindowContainerController getController() {
660 return mController;
661 }
662
663 void setController(WindowContainerController controller) {
664 if (mController != null && controller != null) {
665 throw new IllegalArgumentException("Can't set controller=" + mController
666 + " for container=" + this + " Already set to=" + mController);
667 }
668 if (controller != null) {
669 controller.setContainer(this);
670 } else if (mController != null) {
671 mController.setContainer(null);
672 }
673 mController = controller;
674 }
675
676 /**
Justin Klaassen93b7ee42017-10-10 15:20:13 -0400677 * Write to a protocol buffer output stream. Protocol buffer message definition is at
678 * {@link com.android.server.wm.proto.WindowContainerProto}.
679 *
680 * @param protoOutputStream Stream to write the WindowContainer object to.
681 * @param fieldId Field Id of the WindowContainer as defined in the parent message.
682 * @hide
683 */
684 @CallSuper
685 @Override
686 public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
687 final long token = protoOutputStream.start(fieldId);
688 super.writeToProto(protoOutputStream, CONFIGURATION_CONTAINER);
689 protoOutputStream.write(ORIENTATION, mOrientation);
690 protoOutputStream.end(token);
691 }
692
Justin Klaassen10d07c82017-09-15 17:58:39 -0400693 private ForAllWindowsConsumerWrapper obtainConsumerWrapper(Consumer<WindowState> consumer) {
694 ForAllWindowsConsumerWrapper wrapper = mConsumerWrapperPool.acquire();
695 if (wrapper == null) {
696 wrapper = new ForAllWindowsConsumerWrapper();
697 }
698 wrapper.setConsumer(consumer);
699 return wrapper;
700 }
701
702 private final class ForAllWindowsConsumerWrapper implements ToBooleanFunction<WindowState> {
703
704 private Consumer<WindowState> mConsumer;
705
706 void setConsumer(Consumer<WindowState> consumer) {
707 mConsumer = consumer;
708 }
709
710 @Override
711 public boolean apply(WindowState w) {
712 mConsumer.accept(w);
713 return false;
714 }
715
716 void release() {
717 mConsumer = null;
718 mConsumerWrapperPool.release(this);
719 }
720 }
721}