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 org.junit.Test;
20import org.junit.runner.RunWith;
21
22import android.content.res.Configuration;
23import android.platform.test.annotations.Presubmit;
24import android.support.test.filters.SmallTest;
25import android.support.test.runner.AndroidJUnit4;
26
27import java.util.Comparator;
28
29import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
30import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
31import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
32import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
33import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
34import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
35import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
36
37import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
38import static com.android.server.wm.WindowContainer.POSITION_TOP;
39
40import static org.junit.Assert.assertEquals;
41import static org.junit.Assert.assertFalse;
42import static org.junit.Assert.assertNotNull;
43import static org.junit.Assert.assertNull;
44import static org.junit.Assert.assertTrue;
45
46/**
47 * Test class for {@link WindowContainer}.
48 *
49 * Build/Install/Run:
50 *  bit FrameworksServicesTests:com.android.server.wm.WindowContainerTests
51 */
52@SmallTest
53@Presubmit
54@RunWith(AndroidJUnit4.class)
55public class WindowContainerTests extends WindowTestsBase {
56
57    @Test
58    public void testCreation() throws Exception {
59        final TestWindowContainer w = new TestWindowContainerBuilder().setLayer(0).build();
60        assertNull("window must have no parent", w.getParentWindow());
61        assertEquals("window must have no children", 0, w.getChildrenCount());
62    }
63
64    @Test
65    public void testAdd() throws Exception {
66        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
67        final TestWindowContainer root = builder.setLayer(0).build();
68
69        final TestWindowContainer layer1 = root.addChildWindow(builder.setLayer(1));
70        final TestWindowContainer secondLayer1 = root.addChildWindow(builder.setLayer(1));
71        final TestWindowContainer layer2 = root.addChildWindow(builder.setLayer(2));
72        final TestWindowContainer layerNeg1 = root.addChildWindow(builder.setLayer(-1));
73        final TestWindowContainer layerNeg2 = root.addChildWindow(builder.setLayer(-2));
74        final TestWindowContainer secondLayerNeg1 = root.addChildWindow(builder.setLayer(-1));
75        final TestWindowContainer layer0 = root.addChildWindow(builder.setLayer(0));
76
77        assertEquals(7, root.getChildrenCount());
78
79        assertEquals(root, layer1.getParentWindow());
80        assertEquals(root, secondLayer1.getParentWindow());
81        assertEquals(root, layer2.getParentWindow());
82        assertEquals(root, layerNeg1.getParentWindow());
83        assertEquals(root, layerNeg2.getParentWindow());
84        assertEquals(root, secondLayerNeg1.getParentWindow());
85        assertEquals(root, layer0.getParentWindow());
86
87        assertEquals(layerNeg2, root.getChildAt(0));
88        assertEquals(secondLayerNeg1, root.getChildAt(1));
89        assertEquals(layerNeg1, root.getChildAt(2));
90        assertEquals(layer0, root.getChildAt(3));
91        assertEquals(layer1, root.getChildAt(4));
92        assertEquals(secondLayer1, root.getChildAt(5));
93        assertEquals(layer2, root.getChildAt(6));
94
95        assertTrue(layer1.mOnParentSetCalled);
96        assertTrue(secondLayer1.mOnParentSetCalled);
97        assertTrue(layer2.mOnParentSetCalled);
98        assertTrue(layerNeg1.mOnParentSetCalled);
99        assertTrue(layerNeg2.mOnParentSetCalled);
100        assertTrue(secondLayerNeg1.mOnParentSetCalled);
101        assertTrue(layer0.mOnParentSetCalled);
102    }
103
104    @Test
105    public void testAdd_AlreadyHasParent() throws Exception {
106        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
107        final TestWindowContainer root = builder.setLayer(0).build();
108
109        final TestWindowContainer child1 = root.addChildWindow();
110        final TestWindowContainer child2 = root.addChildWindow();
111
112        boolean gotException = false;
113        try {
114            child1.addChildWindow(child2);
115        } catch (IllegalArgumentException e) {
116            gotException = true;
117        }
118        assertTrue(gotException);
119
120        gotException = false;
121        try {
122            root.addChildWindow(child2);
123        } catch (IllegalArgumentException e) {
124            gotException = true;
125        }
126        assertTrue(gotException);
127    }
128
129    @Test
130    public void testHasChild() throws Exception {
131        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
132        final TestWindowContainer root = builder.setLayer(0).build();
133
134        final TestWindowContainer child1 = root.addChildWindow();
135        final TestWindowContainer child2 = root.addChildWindow();
136        final TestWindowContainer child11 = child1.addChildWindow();
137        final TestWindowContainer child12 = child1.addChildWindow();
138        final TestWindowContainer child21 = child2.addChildWindow();
139
140        assertEquals(2, root.getChildrenCount());
141        assertEquals(2, child1.getChildrenCount());
142        assertEquals(1, child2.getChildrenCount());
143
144        assertTrue(root.hasChild(child1));
145        assertTrue(root.hasChild(child2));
146        assertTrue(root.hasChild(child11));
147        assertTrue(root.hasChild(child12));
148        assertTrue(root.hasChild(child21));
149
150        assertTrue(child1.hasChild(child11));
151        assertTrue(child1.hasChild(child12));
152        assertFalse(child1.hasChild(child21));
153
154        assertTrue(child2.hasChild(child21));
155        assertFalse(child2.hasChild(child11));
156        assertFalse(child2.hasChild(child12));
157    }
158
159    @Test
160    public void testRemoveImmediately() throws Exception {
161        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
162        final TestWindowContainer root = builder.setLayer(0).build();
163
164        final TestWindowContainer child1 = root.addChildWindow();
165        final TestWindowContainer child2 = root.addChildWindow();
166        final TestWindowContainer child11 = child1.addChildWindow();
167        final TestWindowContainer child12 = child1.addChildWindow();
168        final TestWindowContainer child21 = child2.addChildWindow();
169
170        assertNotNull(child12.getParentWindow());
171        child12.removeImmediately();
172        assertNull(child12.getParentWindow());
173        assertEquals(1, child1.getChildrenCount());
174        assertFalse(child1.hasChild(child12));
175        assertFalse(root.hasChild(child12));
176
177        assertTrue(root.hasChild(child2));
178        assertNotNull(child2.getParentWindow());
179        child2.removeImmediately();
180        assertNull(child2.getParentWindow());
181        assertNull(child21.getParentWindow());
182        assertEquals(0, child2.getChildrenCount());
183        assertEquals(1, root.getChildrenCount());
184        assertFalse(root.hasChild(child2));
185        assertFalse(root.hasChild(child21));
186
187        assertTrue(root.hasChild(child1));
188        assertTrue(root.hasChild(child11));
189
190        root.removeImmediately();
191        assertEquals(0, root.getChildrenCount());
192    }
193
194    @Test
195    public void testRemoveImmediately_WithController() throws Exception {
196        final WindowContainer container = new WindowContainer();
197        final WindowContainerController controller = new WindowContainerController(null, sWm);
198
199        container.setController(controller);
200        assertEquals(controller, container.getController());
201        assertEquals(container, controller.mContainer);
202
203        container.removeImmediately();
204        assertNull(container.getController());
205        assertNull(controller.mContainer);
206    }
207
208    @Test
209    public void testSetController() throws Exception {
210        final WindowContainerController controller = new WindowContainerController(null, sWm);
211        final WindowContainer container = new WindowContainer();
212
213        container.setController(controller);
214        assertEquals(controller, container.getController());
215        assertEquals(container, controller.mContainer);
216
217        // Assert we can't change the controller to another one once set
218        boolean gotException = false;
219        try {
220            container.setController(new WindowContainerController(null, sWm));
221        } catch (IllegalArgumentException e) {
222            gotException = true;
223        }
224        assertTrue(gotException);
225
226        // Assert that we can set the controller to null.
227        container.setController(null);
228        assertNull(container.getController());
229        assertNull(controller.mContainer);
230    }
231
232    @Test
233    public void testPositionChildAt() throws Exception {
234        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
235        final TestWindowContainer root = builder.setLayer(0).build();
236
237        final TestWindowContainer child1 = root.addChildWindow();
238        final TestWindowContainer child2 = root.addChildWindow();
239        final TestWindowContainer child3 = root.addChildWindow();
240
241        // Test position at top.
242        root.positionChildAt(POSITION_TOP, child1, false /* includingParents */);
243        assertEquals(child1, root.getChildAt(root.getChildrenCount() - 1));
244
245        // Test position at bottom.
246        root.positionChildAt(POSITION_BOTTOM, child1, false /* includingParents */);
247        assertEquals(child1, root.getChildAt(0));
248
249        // Test position in the middle.
250        root.positionChildAt(1, child3, false /* includingParents */);
251        assertEquals(child1, root.getChildAt(0));
252        assertEquals(child3, root.getChildAt(1));
253        assertEquals(child2, root.getChildAt(2));
254    }
255
256    @Test
257    public void testPositionChildAtIncludeParents() throws Exception {
258        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
259        final TestWindowContainer root = builder.setLayer(0).build();
260
261        final TestWindowContainer child1 = root.addChildWindow();
262        final TestWindowContainer child2 = root.addChildWindow();
263        final TestWindowContainer child11 = child1.addChildWindow();
264        final TestWindowContainer child12 = child1.addChildWindow();
265        final TestWindowContainer child13 = child1.addChildWindow();
266        final TestWindowContainer child21 = child2.addChildWindow();
267        final TestWindowContainer child22 = child2.addChildWindow();
268        final TestWindowContainer child23 = child2.addChildWindow();
269
270        // Test moving to top.
271        child1.positionChildAt(POSITION_TOP, child11, true /* includingParents */);
272        assertEquals(child12, child1.getChildAt(0));
273        assertEquals(child13, child1.getChildAt(1));
274        assertEquals(child11, child1.getChildAt(2));
275        assertEquals(child2, root.getChildAt(0));
276        assertEquals(child1, root.getChildAt(1));
277
278        // Test moving to bottom.
279        child1.positionChildAt(POSITION_BOTTOM, child11, true /* includingParents */);
280        assertEquals(child11, child1.getChildAt(0));
281        assertEquals(child12, child1.getChildAt(1));
282        assertEquals(child13, child1.getChildAt(2));
283        assertEquals(child1, root.getChildAt(0));
284        assertEquals(child2, root.getChildAt(1));
285
286        // Test moving to middle, includeParents shouldn't do anything.
287        child2.positionChildAt(1, child21, true /* includingParents */);
288        assertEquals(child11, child1.getChildAt(0));
289        assertEquals(child12, child1.getChildAt(1));
290        assertEquals(child13, child1.getChildAt(2));
291        assertEquals(child22, child2.getChildAt(0));
292        assertEquals(child21, child2.getChildAt(1));
293        assertEquals(child23, child2.getChildAt(2));
294        assertEquals(child1, root.getChildAt(0));
295        assertEquals(child2, root.getChildAt(1));
296    }
297
298    @Test
299    public void testPositionChildAtInvalid() throws Exception {
300        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
301        final TestWindowContainer root = builder.setLayer(0).build();
302
303        final TestWindowContainer child1 = root.addChildWindow();
304        final TestWindowContainer child2 = root.addChildWindow();
305
306        boolean gotException = false;
307        try {
308            // Check response to negative position.
309            root.positionChildAt(-1, child1, false /* includingParents */);
310        } catch (IllegalArgumentException e) {
311            gotException = true;
312        }
313        assertTrue(gotException);
314
315        gotException = false;
316        try {
317            // Check response to position that's bigger than child number.
318            root.positionChildAt(3, child1, false /* includingParents */);
319        } catch (IllegalArgumentException e) {
320            gotException = true;
321        }
322        assertTrue(gotException);
323    }
324
325    @Test
326    public void testIsAnimating() throws Exception {
327        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
328        final TestWindowContainer root = builder.setLayer(0).build();
329
330        final TestWindowContainer child1 = root.addChildWindow(builder.setIsAnimating(true));
331        final TestWindowContainer child2 = root.addChildWindow();
332        final TestWindowContainer child11 = child1.addChildWindow();
333        final TestWindowContainer child12 = child1.addChildWindow(builder.setIsAnimating(true));
334        final TestWindowContainer child21 = child2.addChildWindow();
335
336        assertTrue(root.isAnimating());
337        assertTrue(child1.isAnimating());
338        assertFalse(child11.isAnimating());
339        assertTrue(child12.isAnimating());
340        assertFalse(child2.isAnimating());
341        assertFalse(child21.isAnimating());
342    }
343
344    @Test
345    public void testIsVisible() throws Exception {
346        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
347        final TestWindowContainer root = builder.setLayer(0).build();
348
349        final TestWindowContainer child1 = root.addChildWindow(builder.setIsVisible(true));
350        final TestWindowContainer child2 = root.addChildWindow();
351        final TestWindowContainer child11 = child1.addChildWindow();
352        final TestWindowContainer child12 = child1.addChildWindow(builder.setIsVisible(true));
353        final TestWindowContainer child21 = child2.addChildWindow();
354
355        assertFalse(root.isVisible());
356        assertTrue(child1.isVisible());
357        assertFalse(child11.isVisible());
358        assertTrue(child12.isVisible());
359        assertFalse(child2.isVisible());
360        assertFalse(child21.isVisible());
361    }
362
363    @Test
364    public void testOverrideConfigurationAncestorNotification() {
365        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
366        final TestWindowContainer grandparent = builder.setLayer(0).build();
367
368        final TestWindowContainer parent = grandparent.addChildWindow();
369        final TestWindowContainer child = parent.addChildWindow();
370        child.onOverrideConfigurationChanged(new Configuration());
371
372        assertTrue(grandparent.mOnDescendantOverrideCalled);
373    }
374
375    @Test
376    public void testRemoveChild() throws Exception {
377        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
378        final TestWindowContainer root = builder.setLayer(0).build();
379        final TestWindowContainer child1 = root.addChildWindow();
380        final TestWindowContainer child2 = root.addChildWindow();
381        final TestWindowContainer child11 = child1.addChildWindow();
382        final TestWindowContainer child21 = child2.addChildWindow();
383
384        assertTrue(root.hasChild(child2));
385        assertTrue(root.hasChild(child21));
386        root.removeChild(child2);
387        assertFalse(root.hasChild(child2));
388        assertFalse(root.hasChild(child21));
389        assertNull(child2.getParentWindow());
390
391        boolean gotException = false;
392        assertTrue(root.hasChild(child11));
393        try {
394            // Can only detach our direct children.
395            root.removeChild(child11);
396        } catch (IllegalArgumentException e) {
397            gotException = true;
398        }
399        assertTrue(gotException);
400    }
401
402    @Test
403    public void testGetOrientation_childSpecified() throws Exception {
404        testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_LANDSCAPE,
405            SCREEN_ORIENTATION_LANDSCAPE);
406        testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_UNSET,
407            SCREEN_ORIENTATION_UNSPECIFIED);
408    }
409
410    private void testGetOrientation_childSpecifiedConfig(boolean childVisible, int childOrientation,
411        int expectedOrientation) {
412        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
413        final TestWindowContainer root = builder.setLayer(0).build();
414        root.setFillsParent(true);
415
416        builder.setIsVisible(childVisible);
417
418        if (childOrientation != SCREEN_ORIENTATION_UNSET) {
419            builder.setOrientation(childOrientation);
420        }
421
422        final TestWindowContainer child1 = root.addChildWindow(builder);
423        child1.setFillsParent(true);
424
425        assertEquals(expectedOrientation, root.getOrientation());
426    }
427
428    @Test
429    public void testGetOrientation_Unset() throws Exception {
430        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
431        final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
432        // Unspecified well because we didn't specify anything...
433        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, root.getOrientation());
434    }
435
436    @Test
437    public void testGetOrientation_InvisibleParentUnsetVisibleChildren() throws Exception {
438        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
439        final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
440
441        builder.setIsVisible(false).setLayer(-1);
442        final TestWindowContainer invisible = root.addChildWindow(builder);
443        builder.setIsVisible(true).setLayer(-2);
444        final TestWindowContainer invisibleChild1VisibleAndSet = invisible.addChildWindow(builder);
445        invisibleChild1VisibleAndSet.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
446        // Landscape well because the container is visible and that is what we set on it above.
447        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, invisibleChild1VisibleAndSet.getOrientation());
448        // Landscape because even though the container isn't visible it has a child that is
449        // specifying it can influence the orientation by being visible.
450        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, invisible.getOrientation());
451        // Landscape because the grandchild is visible and therefore can participate.
452        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, root.getOrientation());
453
454        builder.setIsVisible(true).setLayer(-3);
455        final TestWindowContainer visibleUnset = root.addChildWindow(builder);
456        visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET);
457        assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnset.getOrientation());
458        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, root.getOrientation());
459
460    }
461
462    @Test
463    public void testGetOrientation_setBehind() throws Exception {
464        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
465        final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
466
467        builder.setIsVisible(true).setLayer(-1);
468        final TestWindowContainer visibleUnset = root.addChildWindow(builder);
469        visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET);
470
471        builder.setIsVisible(true).setLayer(-2);
472        final TestWindowContainer visibleUnsetChild1VisibleSetBehind =
473                visibleUnset.addChildWindow(builder);
474        visibleUnsetChild1VisibleSetBehind.setOrientation(SCREEN_ORIENTATION_BEHIND);
475        // Setting to visible behind will be used by the parents if there isn't another other
476        // container behind this one that has an orientation set.
477        assertEquals(SCREEN_ORIENTATION_BEHIND,
478                visibleUnsetChild1VisibleSetBehind.getOrientation());
479        assertEquals(SCREEN_ORIENTATION_BEHIND, visibleUnset.getOrientation());
480        assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation());
481    }
482
483    @Test
484    public void testGetOrientation_fillsParent() throws Exception {
485        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
486        final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
487
488        builder.setIsVisible(true).setLayer(-1);
489        final TestWindowContainer visibleUnset = root.addChildWindow(builder);
490        visibleUnset.setOrientation(SCREEN_ORIENTATION_BEHIND);
491
492        builder.setLayer(1).setIsVisible(true);
493        final TestWindowContainer visibleUnspecifiedRootChild = root.addChildWindow(builder);
494        visibleUnspecifiedRootChild.setFillsParent(false);
495        visibleUnspecifiedRootChild.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
496        // Unset because the child doesn't fill the parent. May as well be invisible...
497        assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation());
498        // The parent uses whatever orientation is set behind this container since it doesn't fill
499        // the parent.
500        assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation());
501
502        // Test case of child filling its parent, but its parent isn't filling its own parent.
503        builder.setLayer(2).setIsVisible(true);
504        final TestWindowContainer visibleUnspecifiedRootChildChildFillsParent =
505                visibleUnspecifiedRootChild.addChildWindow(builder);
506        visibleUnspecifiedRootChildChildFillsParent.setOrientation(
507                SCREEN_ORIENTATION_PORTRAIT);
508        assertEquals(SCREEN_ORIENTATION_PORTRAIT,
509                visibleUnspecifiedRootChildChildFillsParent.getOrientation());
510        assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation());
511        assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation());
512
513
514        visibleUnspecifiedRootChild.setFillsParent(true);
515        assertEquals(SCREEN_ORIENTATION_PORTRAIT, visibleUnspecifiedRootChild.getOrientation());
516        assertEquals(SCREEN_ORIENTATION_PORTRAIT, root.getOrientation());
517    }
518
519    @Test
520    public void testCompareTo() throws Exception {
521        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
522        final TestWindowContainer root = builder.setLayer(0).build();
523
524        final TestWindowContainer child1 = root.addChildWindow();
525        final TestWindowContainer child11 = child1.addChildWindow();
526        final TestWindowContainer child12 = child1.addChildWindow();
527
528        final TestWindowContainer child2 = root.addChildWindow();
529        final TestWindowContainer child21 = child2.addChildWindow();
530        final TestWindowContainer child22 = child2.addChildWindow();
531        final TestWindowContainer child23 = child2.addChildWindow();
532        final TestWindowContainer child221 = child22.addChildWindow();
533        final TestWindowContainer child222 = child22.addChildWindow();
534        final TestWindowContainer child223 = child22.addChildWindow();
535        final TestWindowContainer child2221 = child222.addChildWindow();
536        final TestWindowContainer child2222 = child222.addChildWindow();
537        final TestWindowContainer child2223 = child222.addChildWindow();
538
539        final TestWindowContainer root2 = builder.setLayer(0).build();
540
541        assertEquals(0, root.compareTo(root));
542        assertEquals(-1, child1.compareTo(child2));
543        assertEquals(1, child2.compareTo(child1));
544
545        boolean inTheSameTree = true;
546        try {
547            root.compareTo(root2);
548        } catch (IllegalArgumentException e) {
549            inTheSameTree = false;
550        }
551        assertFalse(inTheSameTree);
552
553        assertEquals(-1, child1.compareTo(child11));
554        assertEquals(1, child21.compareTo(root));
555        assertEquals(1, child21.compareTo(child12));
556        assertEquals(-1, child11.compareTo(child2));
557        assertEquals(1, child2221.compareTo(child11));
558        assertEquals(-1, child2222.compareTo(child223));
559        assertEquals(1, child2223.compareTo(child21));
560    }
561
562    @Test
563    public void testConfigurationInit() throws Exception {
564        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
565
566        // Check root container initial config.
567        final TestWindowContainer root = builder.setLayer(0).build();
568        assertEquals(Configuration.EMPTY, root.getOverrideConfiguration());
569        assertEquals(Configuration.EMPTY, root.getMergedOverrideConfiguration());
570        assertEquals(Configuration.EMPTY, root.getConfiguration());
571
572        // Check child initial config.
573        final TestWindowContainer child1 = root.addChildWindow();
574        assertEquals(Configuration.EMPTY, child1.getOverrideConfiguration());
575        assertEquals(Configuration.EMPTY, child1.getMergedOverrideConfiguration());
576        assertEquals(Configuration.EMPTY, child1.getConfiguration());
577
578        // Check child initial config if root has overrides.
579        final Configuration rootOverrideConfig = new Configuration();
580        rootOverrideConfig.fontScale = 1.3f;
581        root.onOverrideConfigurationChanged(rootOverrideConfig);
582        final TestWindowContainer child2 = root.addChildWindow();
583        assertEquals(Configuration.EMPTY, child2.getOverrideConfiguration());
584        assertEquals(rootOverrideConfig, child2.getMergedOverrideConfiguration());
585        assertEquals(rootOverrideConfig, child2.getConfiguration());
586
587        // Check child initial config if root has parent config set.
588        final Configuration rootParentConfig = new Configuration();
589        rootParentConfig.fontScale = 0.8f;
590        rootParentConfig.orientation = SCREEN_ORIENTATION_LANDSCAPE;
591        root.onConfigurationChanged(rootParentConfig);
592        final Configuration rootFullConfig = new Configuration(rootParentConfig);
593        rootFullConfig.updateFrom(rootOverrideConfig);
594
595        final TestWindowContainer child3 = root.addChildWindow();
596        assertEquals(Configuration.EMPTY, child3.getOverrideConfiguration());
597        assertEquals(rootOverrideConfig, child3.getMergedOverrideConfiguration());
598        assertEquals(rootFullConfig, child3.getConfiguration());
599    }
600
601    @Test
602    public void testConfigurationChangeOnAddRemove() throws Exception {
603        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
604
605        // Init root's config.
606        final TestWindowContainer root = builder.setLayer(0).build();
607        final Configuration rootOverrideConfig = new Configuration();
608        rootOverrideConfig.fontScale = 1.3f;
609        root.onOverrideConfigurationChanged(rootOverrideConfig);
610
611        // Init child's config.
612        final TestWindowContainer child = root.addChildWindow();
613        final Configuration childOverrideConfig = new Configuration();
614        childOverrideConfig.densityDpi = 320;
615        child.onOverrideConfigurationChanged(childOverrideConfig);
616        final Configuration mergedOverrideConfig = new Configuration(root.getConfiguration());
617        mergedOverrideConfig.updateFrom(childOverrideConfig);
618
619        // Check configuration update when child is removed from parent - it should remain same.
620        root.removeChild(child);
621        assertEquals(childOverrideConfig, child.getOverrideConfiguration());
622        assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration());
623        assertEquals(mergedOverrideConfig, child.getConfiguration());
624
625        // It may be paranoia... but let's check if parent's config didn't change after removal.
626        assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
627        assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
628        assertEquals(rootOverrideConfig, root.getConfiguration());
629
630        // Init different root
631        final TestWindowContainer root2 = builder.setLayer(0).build();
632        final Configuration rootOverrideConfig2 = new Configuration();
633        rootOverrideConfig2.fontScale = 1.1f;
634        root2.onOverrideConfigurationChanged(rootOverrideConfig2);
635
636        // Check configuration update when child is added to different parent.
637        mergedOverrideConfig.setTo(rootOverrideConfig2);
638        mergedOverrideConfig.updateFrom(childOverrideConfig);
639        root2.addChildWindow(child);
640        assertEquals(childOverrideConfig, child.getOverrideConfiguration());
641        assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration());
642        assertEquals(mergedOverrideConfig, child.getConfiguration());
643    }
644
645    @Test
646    public void testConfigurationChangePropagation() throws Exception {
647        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
648
649        // Builds 3-level vertical hierarchy with one window container on each level.
650        // In addition to different overrides on each level, everyone in hierarchy will have one
651        // common overridden value - orientation;
652
653        // Init root's config.
654        final TestWindowContainer root = builder.setLayer(0).build();
655        final Configuration rootOverrideConfig = new Configuration();
656        rootOverrideConfig.fontScale = 1.3f;
657        rootOverrideConfig.orientation = SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
658        root.onOverrideConfigurationChanged(rootOverrideConfig);
659
660        // Init children.
661        final TestWindowContainer child1 = root.addChildWindow();
662        final Configuration childOverrideConfig1 = new Configuration();
663        childOverrideConfig1.densityDpi = 320;
664        childOverrideConfig1.orientation = SCREEN_ORIENTATION_LANDSCAPE;
665        child1.onOverrideConfigurationChanged(childOverrideConfig1);
666
667        final TestWindowContainer child2 = child1.addChildWindow();
668        final Configuration childOverrideConfig2 = new Configuration();
669        childOverrideConfig2.screenWidthDp = 150;
670        childOverrideConfig2.orientation = SCREEN_ORIENTATION_PORTRAIT;
671        child2.onOverrideConfigurationChanged(childOverrideConfig2);
672
673        // Check configuration on all levels when root override is updated.
674        rootOverrideConfig.smallestScreenWidthDp = 200;
675        root.onOverrideConfigurationChanged(rootOverrideConfig);
676
677        final Configuration mergedOverrideConfig1 = new Configuration(rootOverrideConfig);
678        mergedOverrideConfig1.updateFrom(childOverrideConfig1);
679        final Configuration mergedConfig1 = new Configuration(mergedOverrideConfig1);
680
681        final Configuration mergedOverrideConfig2 = new Configuration(mergedOverrideConfig1);
682        mergedOverrideConfig2.updateFrom(childOverrideConfig2);
683        final Configuration mergedConfig2 = new Configuration(mergedOverrideConfig2);
684
685        assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
686        assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
687        assertEquals(rootOverrideConfig, root.getConfiguration());
688
689        assertEquals(childOverrideConfig1, child1.getOverrideConfiguration());
690        assertEquals(mergedOverrideConfig1, child1.getMergedOverrideConfiguration());
691        assertEquals(mergedConfig1, child1.getConfiguration());
692
693        assertEquals(childOverrideConfig2, child2.getOverrideConfiguration());
694        assertEquals(mergedOverrideConfig2, child2.getMergedOverrideConfiguration());
695        assertEquals(mergedConfig2, child2.getConfiguration());
696
697        // Check configuration on all levels when root parent config is updated.
698        final Configuration rootParentConfig = new Configuration();
699        rootParentConfig.screenHeightDp = 100;
700        rootParentConfig.orientation = SCREEN_ORIENTATION_REVERSE_PORTRAIT;
701        root.onConfigurationChanged(rootParentConfig);
702        final Configuration mergedRootConfig = new Configuration(rootParentConfig);
703        mergedRootConfig.updateFrom(rootOverrideConfig);
704
705        mergedConfig1.setTo(mergedRootConfig);
706        mergedConfig1.updateFrom(mergedOverrideConfig1);
707
708        mergedConfig2.setTo(mergedConfig1);
709        mergedConfig2.updateFrom(mergedOverrideConfig2);
710
711        assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
712        assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
713        assertEquals(mergedRootConfig, root.getConfiguration());
714
715        assertEquals(childOverrideConfig1, child1.getOverrideConfiguration());
716        assertEquals(mergedOverrideConfig1, child1.getMergedOverrideConfiguration());
717        assertEquals(mergedConfig1, child1.getConfiguration());
718
719        assertEquals(childOverrideConfig2, child2.getOverrideConfiguration());
720        assertEquals(mergedOverrideConfig2, child2.getMergedOverrideConfiguration());
721        assertEquals(mergedConfig2, child2.getConfiguration());
722    }
723
724    /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
725    private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
726        private final int mLayer;
727        private boolean mIsAnimating;
728        private boolean mIsVisible;
729        private boolean mFillsParent;
730        private Integer mOrientation;
731
732        private boolean mOnParentSetCalled;
733        private boolean mOnDescendantOverrideCalled;
734
735        /**
736         * Compares 2 window layers and returns -1 if the first is lesser than the second in terms
737         * of z-order and 1 otherwise.
738         */
739        private final Comparator<TestWindowContainer> mWindowSubLayerComparator = (w1, w2) -> {
740            final int layer1 = w1.mLayer;
741            final int layer2 = w2.mLayer;
742            if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
743                // We insert the child window into the list ordered by the mLayer. For same layers,
744                // the negative one should go below others; the positive one should go above others.
745                return -1;
746            }
747            return 1;
748        };
749
750        TestWindowContainer(int layer, boolean isAnimating, boolean isVisible,
751            Integer orientation) {
752            mLayer = layer;
753            mIsAnimating = isAnimating;
754            mIsVisible = isVisible;
755            mFillsParent = true;
756            mOrientation = orientation;
757        }
758
759        TestWindowContainer getParentWindow() {
760            return (TestWindowContainer) getParent();
761        }
762
763        int getChildrenCount() {
764            return mChildren.size();
765        }
766
767        TestWindowContainer addChildWindow(TestWindowContainer child) {
768            addChild(child, mWindowSubLayerComparator);
769            return child;
770        }
771
772        TestWindowContainer addChildWindow(TestWindowContainerBuilder childBuilder) {
773            TestWindowContainer child = childBuilder.build();
774            addChild(child, mWindowSubLayerComparator);
775            return child;
776        }
777
778        TestWindowContainer addChildWindow() {
779            return addChildWindow(new TestWindowContainerBuilder().setLayer(1));
780        }
781
782        TestWindowContainer getChildAt(int index) {
783            return mChildren.get(index);
784        }
785
786        @Override
787        void onParentSet() {
788            mOnParentSetCalled = true;
789        }
790
791        @Override
792        void onDescendantOverrideConfigurationChanged() {
793            mOnDescendantOverrideCalled = true;
794            super.onDescendantOverrideConfigurationChanged();
795        }
796
797        @Override
798        boolean isAnimating() {
799            return mIsAnimating || super.isAnimating();
800        }
801
802        @Override
803        boolean isVisible() {
804            return mIsVisible;
805        }
806
807        @Override
808        int getOrientation(int candidate) {
809            return mOrientation != null ? mOrientation : super.getOrientation(candidate);
810        }
811
812        @Override
813        int getOrientation() {
814            return getOrientation(super.mOrientation);
815        }
816
817        @Override
818        boolean fillsParent() {
819            return mFillsParent;
820        }
821
822        void setFillsParent(boolean fillsParent) {
823            mFillsParent = fillsParent;
824        }
825    }
826
827    private class TestWindowContainerBuilder {
828        private int mLayer;
829        private boolean mIsAnimating;
830        private boolean mIsVisible;
831        private Integer mOrientation;
832
833        public TestWindowContainerBuilder() {
834            reset();
835        }
836
837        TestWindowContainerBuilder setLayer(int layer) {
838            mLayer = layer;
839            return this;
840        }
841
842        TestWindowContainerBuilder setIsAnimating(boolean isAnimating) {
843            mIsAnimating = isAnimating;
844            return this;
845        }
846
847        TestWindowContainerBuilder setIsVisible(boolean isVisible) {
848            mIsVisible = isVisible;
849            return this;
850        }
851
852        TestWindowContainerBuilder setOrientation(int orientation) {
853            mOrientation = orientation;
854            return this;
855        }
856
857        TestWindowContainerBuilder reset() {
858            mLayer = 0;
859            mIsAnimating = false;
860            mIsVisible = false;
861            mOrientation = null;
862            return this;
863        }
864
865        TestWindowContainer build() {
866            return new TestWindowContainer(mLayer, mIsAnimating, mIsVisible, mOrientation);
867        }
868    }
869}
870