1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.widget.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 
26 import android.app.Activity;
27 import android.app.Instrumentation;
28 import android.content.Context;
29 import android.content.res.Resources;
30 import android.graphics.Color;
31 import android.graphics.Rect;
32 import android.graphics.drawable.Drawable;
33 import android.test.ViewAsserts;
34 import android.util.AttributeSet;
35 import android.util.Xml;
36 import android.view.Gravity;
37 import android.view.View;
38 import android.view.View.MeasureSpec;
39 import android.view.ViewGroup;
40 import android.view.ViewTreeObserver;
41 import android.widget.AbsoluteLayout;
42 import android.widget.LinearLayout;
43 import android.widget.LinearLayout.LayoutParams;
44 import android.widget.ListView;
45 import android.widget.TextView;
46 import android.widget.cts.util.TestUtils;
47 
48 import androidx.annotation.ColorInt;
49 import androidx.annotation.Nullable;
50 import androidx.test.InstrumentationRegistry;
51 import androidx.test.annotation.UiThreadTest;
52 import androidx.test.filters.MediumTest;
53 import androidx.test.rule.ActivityTestRule;
54 import androidx.test.runner.AndroidJUnit4;
55 
56 import com.android.compatibility.common.util.WidgetTestUtils;
57 
58 import org.junit.Before;
59 import org.junit.Rule;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 import org.xmlpull.v1.XmlPullParser;
63 
64 import java.util.concurrent.CountDownLatch;
65 import java.util.concurrent.TimeUnit;
66 
67 /**
68  * Test {@link LinearLayout}.
69  */
70 @MediumTest
71 @RunWith(AndroidJUnit4.class)
72 public class LinearLayoutTest {
73     private Instrumentation mInstrumentation;
74     private Activity mActivity;
75 
76     @Rule
77     public ActivityTestRule<LinearLayoutCtsActivity> mActivityRule =
78             new ActivityTestRule<>(LinearLayoutCtsActivity.class);
79 
80     @Before
setup()81     public void setup() {
82         mInstrumentation = InstrumentationRegistry.getInstrumentation();
83         mActivity = mActivityRule.getActivity();
84     }
85 
86     @Test
testConstructor()87     public void testConstructor() {
88         new LinearLayout(mActivity);
89 
90         new LinearLayout(mActivity, null);
91 
92         XmlPullParser parser = mActivity.getResources().getXml(R.layout.linearlayout_layout);
93         AttributeSet attrs = Xml.asAttributeSet(parser);
94         new LinearLayout(mActivity, attrs);
95     }
96 
97     @Test(expected=NullPointerException.class)
testConstructorNullContext()98     public void testConstructorNullContext() {
99         new LinearLayout(null, null);
100     }
101 
102     @UiThreadTest
103     @Test
testAccessBaselineAligned()104     public void testAccessBaselineAligned() {
105         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_empty);
106         parent.setBaselineAligned(true);
107         assertTrue(parent.isBaselineAligned());
108 
109         parent.setBaselineAligned(false);
110         assertFalse(parent.isBaselineAligned());
111 
112         // android:baselineAligned="false" in LinearLayout weightsum
113         parent = (LinearLayout) mActivity.findViewById(R.id.linear_weightsum);
114         assertFalse(parent.isBaselineAligned());
115 
116         // default mBaselineAligned is true.
117         parent = (LinearLayout) mActivity.findViewById(R.id.linear_horizontal);
118         assertTrue(parent.isBaselineAligned());
119 
120         // default mBaselineAligned is true.
121         // Only applicable if {@link #mOrientation} is horizontal
122         parent = (LinearLayout) mActivity.findViewById(R.id.linear_vertical);
123         assertTrue(parent.isBaselineAligned());
124     }
125 
126     @UiThreadTest
127     @Test
testGetBaseline()128     public void testGetBaseline() {
129         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_empty);
130 
131         ListView lv1 = new ListView(mActivity);
132         parent.addView(lv1);
133         assertEquals(-1, parent.getBaseline());
134 
135         ListView lv2 = new ListView(mActivity);
136         parent.addView(lv2);
137         parent.setBaselineAlignedChildIndex(1);
138         try {
139             parent.getBaseline();
140             fail("LinearLayout.getBaseline() should throw exception here.");
141         } catch (RuntimeException e) {
142         }
143 
144         ListView lv3 = new MockListView(mActivity);
145         parent.addView(lv3);
146         parent.setBaselineAlignedChildIndex(2);
147         assertEquals(lv3.getBaseline(), parent.getBaseline());
148     }
149 
150     @UiThreadTest
151     @Test
testAccessBaselineAlignedChildIndex()152     public void testAccessBaselineAlignedChildIndex() {
153         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_empty);
154 
155         // set BaselineAlignedChildIndex
156         ListView lv1 = new ListView(mActivity);
157         ListView lv2 = new ListView(mActivity);
158         ListView lv3 = new ListView(mActivity);
159         parent.addView(lv1);
160         parent.addView(lv2);
161         parent.addView(lv3);
162         parent.setBaselineAlignedChildIndex(1);
163         assertEquals(1, parent.getBaselineAlignedChildIndex());
164 
165         parent.setBaselineAlignedChildIndex(2);
166         assertEquals(2, parent.getBaselineAlignedChildIndex());
167 
168         try {
169             parent.setBaselineAlignedChildIndex(-1);
170             fail("LinearLayout should throw IllegalArgumentException here.");
171         } catch (IllegalArgumentException e) {
172         }
173         try {
174             parent.setBaselineAlignedChildIndex(3);
175             fail("LinearLayout should throw IllegalArgumentException here.");
176         } catch (IllegalArgumentException e) {
177         }
178 
179         parent = (LinearLayout) mActivity.findViewById(R.id.linear_baseline_aligned_child_index);
180         assertEquals(1, parent.getBaselineAlignedChildIndex());
181     }
182 
183     /**
184      * weightsum is a horizontal LinearLayout. There are three children in it.
185      */
186     @Test
testAccessWeightSum()187     public void testAccessWeightSum() {
188         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_weightsum);
189         TextView weight02 = (TextView) parent.findViewById(R.id.weight_0_2);
190         TextView weight05 = (TextView) parent.findViewById(R.id.weight_0_5);
191         TextView weight03 = (TextView) parent.findViewById(R.id.weight_0_3);
192 
193         assertNotNull(parent);
194         assertNotNull(weight02);
195         assertNotNull(weight05);
196         assertNotNull(weight03);
197 
198         assertEquals(mActivity.getResources().getString(R.string.horizontal_text_1),
199                 weight02.getText().toString());
200         assertEquals(mActivity.getResources().getString(R.string.horizontal_text_2),
201                 weight05.getText().toString());
202         assertEquals(mActivity.getResources().getString(R.string.horizontal_text_3),
203                 weight03.getText().toString());
204 
205         assertEquals(LinearLayout.HORIZONTAL, parent.getOrientation());
206         assertEquals(1.0f, parent.getWeightSum(), 0.0f);
207 
208         int parentWidth = parent.getWidth();
209         assertEquals(Math.ceil(parentWidth * 0.2), weight02.getWidth(), 1.0);
210         assertEquals(Math.ceil(parentWidth * 0.5), weight05.getWidth(), 1.0);
211         assertEquals(Math.ceil(parentWidth * 0.3), weight03.getWidth(), 1.0);
212     }
213 
214     @UiThreadTest
215     @Test
testWeightDistribution()216     public void testWeightDistribution() {
217         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_empty);
218 
219         for (int i = 0; i < 3; i++) {
220             parent.addView(new View(mActivity), new LayoutParams(0, 0, 1));
221         }
222 
223         int size = 100;
224         int spec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
225 
226         for (int i = 0; i < 3; i++) {
227             View child = parent.getChildAt(i);
228             LayoutParams lp = (LayoutParams) child.getLayoutParams();
229             lp.height = 0;
230             lp.width = LayoutParams.MATCH_PARENT;
231             child.setLayoutParams(lp);
232         }
233         parent.setOrientation(LinearLayout.VERTICAL);
234         parent.measure(spec, spec);
235         parent.layout(0, 0, size, size);
236         assertEquals(100, parent.getWidth());
237         assertEquals(100, parent.getChildAt(0).getWidth());
238         assertEquals(100, parent.getChildAt(1).getWidth());
239         assertEquals(100, parent.getChildAt(2).getWidth());
240         assertEquals(100, parent.getHeight());
241         assertEquals(33, parent.getChildAt(0).getHeight());
242         assertEquals(33, parent.getChildAt(1).getHeight());
243         assertEquals(34, parent.getChildAt(2).getHeight());
244 
245         for (int i = 0; i < 3; i++) {
246             View child = parent.getChildAt(i);
247             LayoutParams lp = (LayoutParams) child.getLayoutParams();
248             lp.height = LayoutParams.MATCH_PARENT;
249             lp.width = 0;
250             child.setLayoutParams(lp);
251         }
252         parent.setOrientation(LinearLayout.HORIZONTAL);
253         parent.measure(spec, spec);
254         parent.layout(0, 0, size, size);
255         assertEquals(100, parent.getWidth());
256         assertEquals(33, parent.getChildAt(0).getWidth());
257         assertEquals(33, parent.getChildAt(1).getWidth());
258         assertEquals(34, parent.getChildAt(2).getWidth());
259         assertEquals(100, parent.getHeight());
260         assertEquals(100, parent.getChildAt(0).getHeight());
261         assertEquals(100, parent.getChildAt(1).getHeight());
262         assertEquals(100, parent.getChildAt(2).getHeight());
263     }
264 
265     @UiThreadTest
266     @Test
testGenerateLayoutParams()267     public void testGenerateLayoutParams() {
268         ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(320, 240);
269         MockLinearLayout parent = (MockLinearLayout) mActivity.findViewById(R.id.linear_custom);
270         LayoutParams layoutParams1 = parent.generateLayoutParams(lp);
271         assertEquals(320, layoutParams1.width);
272         assertEquals(240, layoutParams1.height);
273     }
274 
275     @UiThreadTest
276     @Test
testCheckLayoutParams()277     public void testCheckLayoutParams() {
278         MockLinearLayout parent = (MockLinearLayout) mActivity.findViewById(R.id.linear_custom);
279 
280         ViewGroup.LayoutParams params = new AbsoluteLayout.LayoutParams(240, 320, 0, 0);
281         assertFalse(parent.checkLayoutParams(params));
282 
283         params = new LinearLayout.LayoutParams(240, 320);
284         assertTrue(parent.checkLayoutParams(params));
285     }
286 
287     @UiThreadTest
288     @Test
testGenerateDefaultLayoutParams()289     public void testGenerateDefaultLayoutParams() {
290         MockLinearLayout parent = (MockLinearLayout) mActivity.findViewById(R.id.linear_custom);
291 
292         parent.setOrientation(LinearLayout.HORIZONTAL);
293         ViewGroup.LayoutParams param = parent.generateDefaultLayoutParams();
294         assertNotNull(param);
295         assertTrue(param instanceof LinearLayout.LayoutParams);
296         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, param.width);
297         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, param.height);
298 
299         parent.setOrientation(LinearLayout.VERTICAL);
300         param = parent.generateDefaultLayoutParams();
301         assertNotNull(param);
302         assertTrue(param instanceof LinearLayout.LayoutParams);
303         assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, param.width);
304         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, param.height);
305 
306         parent.setOrientation(-1);
307         assertNull(parent.generateDefaultLayoutParams());
308     }
309 
310     @UiThreadTest
311     @Test
testGenerateLayoutParamsFromMarginParams()312     public void testGenerateLayoutParamsFromMarginParams() {
313         MockLinearLayout parent = (MockLinearLayout) mActivity.findViewById(R.id.linear_custom);
314 
315         ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(3, 5);
316         lp.leftMargin = 1;
317         lp.topMargin = 2;
318         lp.rightMargin = 3;
319         lp.bottomMargin = 4;
320         LinearLayout.LayoutParams generated = parent.generateLayoutParams(lp);
321         assertNotNull(generated);
322         assertEquals(3, generated.width);
323         assertEquals(5, generated.height);
324 
325         assertEquals(1, generated.leftMargin);
326         assertEquals(2, generated.topMargin);
327         assertEquals(3, generated.rightMargin);
328         assertEquals(4, generated.bottomMargin);
329     }
330 
331     /**
332      * layout of horizontal LinearLayout.
333      * ----------------------------------------------------
334      * | ------------ |                 |                 |
335      * | | top view | | --------------- |                 |
336      * | |          | | | center view | | --------------- |
337      * | ------------ | |             | | | bottom view | |
338      * |              | --------------- | |             | |
339      * |     parent   |                 | --------------- |
340      * ----------------------------------------------------
341      */
342     @Test
testLayoutHorizontal()343     public void testLayoutHorizontal() {
344         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_horizontal);
345         TextView topView = (TextView) mActivity.findViewById(R.id.gravity_top);
346         TextView centerView = (TextView) mActivity.findViewById(R.id.gravity_center_vertical);
347         TextView bottomView = (TextView) mActivity.findViewById(R.id.gravity_bottom);
348 
349         assertNotNull(parent);
350         assertNotNull(topView);
351         assertNotNull(centerView);
352         assertNotNull(bottomView);
353 
354         assertEquals(mActivity.getResources().getString(R.string.horizontal_text_1),
355                 topView.getText().toString());
356         assertEquals(mActivity.getResources().getString(R.string.horizontal_text_2),
357                 centerView.getText().toString());
358         assertEquals(mActivity.getResources().getString(R.string.horizontal_text_3),
359                 bottomView.getText().toString());
360 
361         assertEquals(LinearLayout.HORIZONTAL, parent.getOrientation());
362 
363         ViewAsserts.assertTopAligned(parent, topView);
364         ViewAsserts.assertVerticalCenterAligned(parent, centerView);
365         ViewAsserts.assertBottomAligned(parent, bottomView);
366 
367         assertEquals(0, topView.getTop());
368         assertEquals(topView.getHeight(), topView.getBottom());
369         assertEquals(0, topView.getLeft());
370         assertEquals(centerView.getLeft(), topView.getRight());
371 
372         int offset = (parent.getHeight() - centerView.getHeight()) / 2;
373         assertEquals(offset, centerView.getTop());
374         assertEquals(offset + centerView.getHeight(), centerView.getBottom());
375         assertEquals(topView.getRight(), centerView.getLeft());
376         assertEquals(bottomView.getLeft(), centerView.getRight());
377 
378         assertEquals(parent.getHeight() - bottomView.getHeight(), bottomView.getTop());
379         assertEquals(parent.getHeight(), bottomView.getBottom());
380         assertEquals(centerView.getRight(), bottomView.getLeft());
381         assertEquals(parent.getWidth(), bottomView.getRight());
382     }
383 
384     /**
385      * layout of vertical LinearLayout.
386      * -----------------------------------
387      * | -------------                   |
388      * | | left view |                   |
389      * | -------------                   |
390      * | - - - - - - - - - - - - - - - - |
391      * |        ---------------          |
392      * |        | center view |          |
393      * |        ---------------          |
394      * | - - - - - - - - - - - - - - - - |
395      * |                  -------------- |
396      * | parent           | right view | |
397      * |                  -------------- |
398      * -----------------------------------
399      */
400     @Test
testLayoutVertical()401     public void testLayoutVertical() {
402         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_vertical);
403         TextView leftView = (TextView) mActivity.findViewById(R.id.gravity_left);
404         TextView centerView = (TextView) mActivity.findViewById(R.id.gravity_center_horizontal);
405         TextView rightView = (TextView) mActivity.findViewById(R.id.gravity_right);
406 
407         assertNotNull(parent);
408         assertNotNull(leftView);
409         assertNotNull(centerView);
410         assertNotNull(rightView);
411 
412         assertEquals(mActivity.getResources().getString(R.string.vertical_text_1),
413                 leftView.getText().toString());
414         assertEquals(mActivity.getResources().getString(R.string.vertical_text_2),
415                 centerView.getText().toString());
416         assertEquals(mActivity.getResources().getString(R.string.vertical_text_3),
417                 rightView.getText().toString());
418 
419         assertEquals(LinearLayout.VERTICAL, parent.getOrientation());
420 
421         ViewAsserts.assertLeftAligned(parent, leftView);
422         ViewAsserts.assertHorizontalCenterAligned(parent, centerView);
423         ViewAsserts.assertRightAligned(parent, rightView);
424 
425         assertEquals(0, leftView.getTop());
426         assertEquals(centerView.getTop(), leftView.getBottom());
427         assertEquals(0, leftView.getLeft());
428         assertEquals(leftView.getWidth(), leftView.getRight());
429 
430         int offset = (parent.getWidth() - centerView.getWidth()) / 2;
431         assertEquals(leftView.getBottom(), centerView.getTop());
432         assertEquals(rightView.getTop(), centerView.getBottom());
433         assertEquals(offset, centerView.getLeft());
434         assertEquals(offset + centerView.getWidth(), centerView.getRight());
435 
436         assertEquals(centerView.getBottom(), rightView.getTop());
437         assertEquals(parent.getHeight(), rightView.getBottom());
438         assertEquals(parent.getWidth() - rightView.getWidth(), rightView.getLeft());
439         assertEquals(parent.getWidth(), rightView.getRight());
440     }
441 
442     @Test
testVerticalCenterGravityOnHorizontalLayout()443     public void testVerticalCenterGravityOnHorizontalLayout() throws Throwable {
444         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_weightsum);
445         TextView leftView = (TextView) parent.findViewById(R.id.weight_0_2);
446         TextView centerView = (TextView) parent.findViewById(R.id.weight_0_5);
447         TextView rightView = (TextView) parent.findViewById(R.id.weight_0_3);
448 
449         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
450                 () -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
451 
452         int originalLeftViewLeft = leftView.getLeft();
453         int originalLeftViewRight = leftView.getRight();
454         int originalCenterViewLeft = centerView.getLeft();
455         int originalCenterViewRight = centerView.getRight();
456         int originalRightViewLeft = rightView.getLeft();
457         int originalRightViewRight = rightView.getRight();
458 
459         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
460                 () -> parent.setVerticalGravity(Gravity.CENTER_VERTICAL));
461 
462         assertEquals(Gravity.CENTER_VERTICAL, parent.getGravity() & Gravity.VERTICAL_GRAVITY_MASK);
463 
464         ViewAsserts.assertVerticalCenterAligned(parent, leftView);
465         ViewAsserts.assertVerticalCenterAligned(parent, centerView);
466         ViewAsserts.assertVerticalCenterAligned(parent, rightView);
467 
468         final int parentHeight = parent.getHeight();
469 
470         int verticalOffset = (parentHeight - leftView.getHeight()) / 2;
471         assertEquals(verticalOffset, leftView.getTop());
472         assertEquals(verticalOffset + leftView.getHeight(), leftView.getBottom());
473         assertEquals(originalLeftViewLeft, leftView.getLeft());
474         assertEquals(originalLeftViewRight, leftView.getRight());
475 
476         verticalOffset = (parentHeight - centerView.getHeight()) / 2;
477         assertEquals(verticalOffset, centerView.getTop());
478         assertEquals(verticalOffset + centerView.getHeight(), centerView.getBottom());
479         assertEquals(originalCenterViewLeft, centerView.getLeft());
480         assertEquals(originalCenterViewRight, centerView.getRight());
481 
482         verticalOffset = (parentHeight - rightView.getHeight()) / 2;
483         assertEquals(verticalOffset, rightView.getTop());
484         assertEquals(verticalOffset + rightView.getHeight(), rightView.getBottom());
485         assertEquals(originalRightViewLeft, rightView.getLeft());
486         assertEquals(originalRightViewRight, rightView.getRight());
487     }
488 
489     @Test
testBottomGravityOnHorizontalLayout()490     public void testBottomGravityOnHorizontalLayout() throws Throwable {
491         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_weightsum);
492         TextView leftView = (TextView) parent.findViewById(R.id.weight_0_2);
493         TextView centerView = (TextView) parent.findViewById(R.id.weight_0_5);
494         TextView rightView = (TextView) parent.findViewById(R.id.weight_0_3);
495 
496         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
497                 () -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
498 
499         int originalLeftViewLeft = leftView.getLeft();
500         int originalLeftViewRight = leftView.getRight();
501         int originalCenterViewLeft = centerView.getLeft();
502         int originalCenterViewRight = centerView.getRight();
503         int originalRightViewLeft = rightView.getLeft();
504         int originalRightViewRight = rightView.getRight();
505 
506         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
507                 () -> parent.setVerticalGravity(Gravity.BOTTOM));
508 
509         assertEquals(Gravity.BOTTOM, parent.getGravity() & Gravity.VERTICAL_GRAVITY_MASK);
510 
511         ViewAsserts.assertBottomAligned(parent, leftView);
512         ViewAsserts.assertBottomAligned(parent, centerView);
513         ViewAsserts.assertBottomAligned(parent, rightView);
514 
515         final int parentHeight = parent.getHeight();
516 
517         assertEquals(parentHeight - leftView.getHeight(), leftView.getTop());
518         assertEquals(parentHeight, leftView.getBottom());
519         assertEquals(originalLeftViewLeft, leftView.getLeft());
520         assertEquals(originalLeftViewRight, leftView.getRight());
521 
522         assertEquals(parentHeight - centerView.getHeight(), centerView.getTop());
523         assertEquals(parentHeight, centerView.getBottom());
524         assertEquals(originalCenterViewLeft, centerView.getLeft());
525         assertEquals(originalCenterViewRight, centerView.getRight());
526 
527         assertEquals(parentHeight - rightView.getHeight(), rightView.getTop());
528         assertEquals(parentHeight, rightView.getBottom());
529         assertEquals(originalRightViewLeft, rightView.getLeft());
530         assertEquals(originalRightViewRight, rightView.getRight());
531     }
532 
533     @Test
testHorizontalCenterGravityOnVerticalLayout()534     public void testHorizontalCenterGravityOnVerticalLayout() throws Throwable {
535         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_weightsum_vertical);
536         TextView topView = (TextView) parent.findViewById(R.id.weight_0_1);
537         TextView centerView = (TextView) parent.findViewById(R.id.weight_0_4);
538         TextView bottomView = (TextView) parent.findViewById(R.id.weight_0_5);
539 
540         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
541                 () -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
542 
543         final int parentWidth = parent.getHeight();
544 
545         int originalTopViewTop = topView.getTop();
546         int originalTopViewBottom = topView.getBottom();
547         int originalCenterViewTop = centerView.getTop();
548         int originalCenterViewBottom = centerView.getBottom();
549         int originalBottomViewTop = bottomView.getTop();
550         int originalBottomViewBottom = bottomView.getBottom();
551 
552         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
553                 () -> parent.setHorizontalGravity(Gravity.CENTER_HORIZONTAL));
554 
555         assertEquals(Gravity.CENTER_HORIZONTAL,
556                 parent.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK);
557 
558         ViewAsserts.assertHorizontalCenterAligned(parent, topView);
559         ViewAsserts.assertHorizontalCenterAligned(parent, centerView);
560         ViewAsserts.assertHorizontalCenterAligned(parent, bottomView);
561 
562         int horizontalOffset = (parentWidth - topView.getWidth()) / 2;
563         assertEquals(originalTopViewTop, topView.getTop());
564         assertEquals(originalTopViewBottom, topView.getBottom());
565         assertEquals(horizontalOffset, topView.getLeft());
566         assertEquals(horizontalOffset + topView.getWidth(), topView.getRight());
567 
568         horizontalOffset = (parentWidth - centerView.getWidth()) / 2;
569         assertEquals(originalCenterViewTop, centerView.getTop());
570         assertEquals(originalCenterViewBottom, centerView.getBottom());
571         assertEquals(horizontalOffset, centerView.getLeft());
572         assertEquals(horizontalOffset + centerView.getWidth(), centerView.getRight());
573 
574         horizontalOffset = (parentWidth - bottomView.getWidth()) / 2;
575         assertEquals(originalBottomViewTop, bottomView.getTop());
576         assertEquals(originalBottomViewBottom, bottomView.getBottom());
577         assertEquals(horizontalOffset, bottomView.getLeft());
578         assertEquals(horizontalOffset + bottomView.getWidth(), bottomView.getRight());
579     }
580 
581     @Test
testRightGravityOnVerticalLayout()582     public void testRightGravityOnVerticalLayout() throws Throwable {
583         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_weightsum_vertical);
584         TextView topView = (TextView) parent.findViewById(R.id.weight_0_1);
585         TextView centerView = (TextView) parent.findViewById(R.id.weight_0_4);
586         TextView bottomView = (TextView) parent.findViewById(R.id.weight_0_5);
587 
588         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
589                 () -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
590 
591         final int parentWidth = parent.getHeight();
592 
593         int originalTopViewTop = topView.getTop();
594         int originalTopViewBottom = topView.getBottom();
595         int originalCenterViewTop = centerView.getTop();
596         int originalCenterViewBottom = centerView.getBottom();
597         int originalBottomViewTop = bottomView.getTop();
598         int originalBottomViewBottom = bottomView.getBottom();
599 
600         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
601                 () -> parent.setHorizontalGravity(Gravity.RIGHT));
602 
603         assertEquals(Gravity.RIGHT, parent.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK);
604 
605         ViewAsserts.assertRightAligned(parent, topView);
606         ViewAsserts.assertRightAligned(parent, centerView);
607         ViewAsserts.assertRightAligned(parent, bottomView);
608 
609         assertEquals(originalTopViewTop, topView.getTop());
610         assertEquals(originalTopViewBottom, topView.getBottom());
611         assertEquals(parentWidth - topView.getWidth(), topView.getLeft());
612         assertEquals(parentWidth, topView.getRight());
613 
614         assertEquals(originalCenterViewTop, centerView.getTop());
615         assertEquals(originalCenterViewBottom, centerView.getBottom());
616         assertEquals(parentWidth - centerView.getWidth(), centerView.getLeft());
617         assertEquals(parentWidth, centerView.getRight());
618 
619         assertEquals(originalBottomViewTop, bottomView.getTop());
620         assertEquals(originalBottomViewBottom, bottomView.getBottom());
621         assertEquals(parentWidth - bottomView.getWidth(), bottomView.getLeft());
622         assertEquals(parentWidth, bottomView.getRight());
623     }
624 
verifyBounds(final ViewGroup viewGroup, final View view, final CountDownLatch countDownLatch, final int left, final int top, final int width, final int height)625     private void verifyBounds(final ViewGroup viewGroup, final View view,
626             final CountDownLatch countDownLatch, final int left, final int top,
627             final int width, final int height) {
628         viewGroup.getViewTreeObserver().addOnPreDrawListener(
629                 new ViewTreeObserver.OnPreDrawListener() {
630                     @Override
631                     public boolean onPreDraw() {
632                         assertEquals(left, view.getLeft());
633                         assertEquals(top, view.getTop());
634                         assertEquals(width, view.getWidth());
635                         assertEquals(height, view.getHeight());
636                         countDownLatch.countDown();
637                         viewGroup.getViewTreeObserver().removeOnPreDrawListener(this);
638                         return true;
639                     }
640                 });
641     }
642 
643     @Test
testVisibilityAffectsLayout()644     public void testVisibilityAffectsLayout() throws Throwable {
645         // Toggling view visibility between GONE/VISIBLE can affect the position of
646         // other children in that container. This test verifies that these changes
647         // on the first child of a LinearLayout affects the position of a second child
648         final int childWidth = 100;
649         final int childHeight = 200;
650         final LinearLayout parent = new LinearLayout(mActivity);
651         ViewGroup.LayoutParams parentParams = new ViewGroup.LayoutParams(
652                 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
653         parent.setLayoutParams(parentParams);
654         final View child1 = new View(mActivity);
655         child1.setBackgroundColor(Color.GREEN);
656         ViewGroup.LayoutParams childParams = new ViewGroup.LayoutParams(childWidth, childHeight);
657         child1.setLayoutParams(childParams);
658         final View child2 = new View(mActivity);
659         child2.setBackgroundColor(Color.RED);
660         childParams = new ViewGroup.LayoutParams(childWidth, childHeight);
661         child2.setLayoutParams(childParams);
662         final ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.linearlayout_root);
663 
664         final CountDownLatch countDownLatch1 = new CountDownLatch(1);
665         mActivityRule.runOnUiThread(() -> {
666             viewGroup.removeAllViews();
667             viewGroup.addView(parent);
668             parent.addView(child1);
669             parent.addView(child2);
670             verifyBounds(viewGroup, child1, countDownLatch1, 0, 0, childWidth, childHeight);
671             verifyBounds(viewGroup, child2, countDownLatch1,
672                     childWidth, 0, childWidth, childHeight);
673         });
674         try {
675             assertTrue(countDownLatch1.await(500, TimeUnit.MILLISECONDS));
676         } catch (InterruptedException ie) {
677             fail(ie.getMessage());
678         }
679 
680         final CountDownLatch countDownLatch2 = new CountDownLatch(1);
681         mActivityRule.runOnUiThread(() -> {
682             child1.setVisibility(View.GONE);
683             verifyBounds(viewGroup, child2, countDownLatch2, 0, 0, childWidth, childHeight);
684         });
685         try {
686             assertTrue(countDownLatch2.await(500, TimeUnit.MILLISECONDS));
687         } catch (InterruptedException ie) {
688             fail(ie.getMessage());
689         }
690 
691         final CountDownLatch countDownLatch3 = new CountDownLatch(2);
692         mActivityRule.runOnUiThread(() -> {
693             child1.setVisibility(View.VISIBLE);
694             verifyBounds(viewGroup, child1, countDownLatch3, 0, 0, childWidth, childHeight);
695             verifyBounds(viewGroup, child2, countDownLatch3,
696                     childWidth, 0, childWidth, childHeight);
697         });
698         try {
699             assertTrue(countDownLatch3.await(500, TimeUnit.MILLISECONDS));
700         } catch (InterruptedException ie) {
701             fail(ie.getMessage());
702         }
703     }
704 
verifyVisualsOfVerticalLayoutWithDivider(LinearLayout parent, int expectedDividerPositionMask, int expectedDividerSize, @ColorInt int expectedDividerColor, int expectedDividerPadding)705     private void verifyVisualsOfVerticalLayoutWithDivider(LinearLayout parent,
706             int expectedDividerPositionMask,
707             int expectedDividerSize, @ColorInt int expectedDividerColor,
708             int expectedDividerPadding) {
709         final int parentWidth = parent.getWidth();
710         final int parentHeight = parent.getHeight();
711 
712         final boolean expectingTopDivider =
713                 (expectedDividerPositionMask & LinearLayout.SHOW_DIVIDER_BEGINNING) != 0;
714         final boolean expectingMiddleDivider =
715                 (expectedDividerPositionMask & LinearLayout.SHOW_DIVIDER_MIDDLE) != 0;
716         final boolean expectingBottomDivider =
717                 (expectedDividerPositionMask & LinearLayout.SHOW_DIVIDER_END) != 0;
718         final int expectedDividerCount = (expectingTopDivider ? 1 : 0)
719                 + (expectingMiddleDivider ? 1 : 0) + (expectingBottomDivider ? 1 : 0);
720 
721         final int expectedChildHeight =
722                 (parentHeight - expectedDividerCount * expectedDividerSize) / 2;
723 
724         final int expectedTopChildTop = expectingTopDivider ? expectedDividerSize : 0;
725         TestUtils.assertRegionPixelsOfColor("Region of first child is blue", parent,
726                 new Rect(0, expectedTopChildTop, parentWidth,
727                         expectedTopChildTop + expectedChildHeight),
728                 Color.BLUE, 1, true);
729 
730         final int expectedBottomChildBottom =
731                 expectingBottomDivider ? parentHeight - expectedDividerSize : parentHeight;
732         TestUtils.assertRegionPixelsOfColor("Region of second child is green", parent,
733                 new Rect(0, expectedBottomChildBottom - expectedChildHeight, parentWidth,
734                         expectedBottomChildBottom),
735                 Color.GREEN, 1, true);
736 
737         if (expectedDividerSize == 0) {
738             return;
739         }
740 
741         // Do we expect top divider?
742         if (expectingTopDivider) {
743             TestUtils.assertRegionPixelsOfColor(
744                     "Region of top divider is " + TestUtils.formatColorToHex(expectedDividerColor),
745                     parent,
746                     new Rect(expectedDividerPadding, 0, parentWidth - expectedDividerPadding,
747                             expectedDividerSize),
748                     expectedDividerColor, 1, true);
749             TestUtils.assertRegionPixelsOfColor("Region of left padding of top divider is yellow",
750                     parent,
751                     new Rect(0, 0, expectedDividerPadding, expectedDividerSize),
752                     Color.YELLOW, 1, true);
753             TestUtils.assertRegionPixelsOfColor("Region of right padding of top divider is yellow",
754                     parent,
755                     new Rect(parentWidth - expectedDividerPadding, 0, parentWidth,
756                             expectedDividerSize),
757                     Color.YELLOW, 1, true);
758         }
759 
760         // Do we expect middle divider?
761         if (expectingMiddleDivider) {
762             final int expectedMiddleDividerTop = expectedTopChildTop + expectedChildHeight;
763             TestUtils.assertRegionPixelsOfColor(
764                     "Region of middle divider is " +
765                             TestUtils.formatColorToHex(expectedDividerColor),
766                     parent,
767                     new Rect(expectedDividerPadding, expectedMiddleDividerTop,
768                             parentWidth - expectedDividerPadding,
769                             expectedMiddleDividerTop + expectedDividerSize),
770                     expectedDividerColor, 1, true);
771             TestUtils.assertRegionPixelsOfColor(
772                     "Region of left padding of middle divider is yellow",
773                     parent,
774                     new Rect(0, expectedMiddleDividerTop, expectedDividerPadding,
775                             expectedMiddleDividerTop + expectedDividerSize),
776                     Color.YELLOW, 1, true);
777             TestUtils.assertRegionPixelsOfColor(
778                     "Region of right padding of middle divider is yellow",
779                     parent,
780                     new Rect(parentWidth - expectedDividerPadding, expectedMiddleDividerTop,
781                             parentWidth, expectedMiddleDividerTop + expectedDividerSize),
782                     Color.YELLOW, 1, true);
783         }
784 
785         // Do we expect bottom divider?
786         if (expectingBottomDivider) {
787             final int expectedBottomDividerTop = expectedBottomChildBottom;
788             TestUtils.assertRegionPixelsOfColor(
789                     "Region of bottom divider is " +
790                             TestUtils.formatColorToHex(expectedDividerColor),
791                     parent,
792                     new Rect(expectedDividerPadding, expectedBottomDividerTop,
793                             parentWidth - expectedDividerPadding,
794                             expectedBottomDividerTop + expectedDividerSize),
795                     expectedDividerColor, 1, true);
796             TestUtils.assertRegionPixelsOfColor(
797                     "Region of left padding of bottom divider is yellow",
798                     parent,
799                     new Rect(0, expectedBottomDividerTop, expectedDividerPadding,
800                             expectedBottomDividerTop + expectedDividerSize),
801                     Color.YELLOW, 1, true);
802             TestUtils.assertRegionPixelsOfColor(
803                     "Region of right padding of bottom divider is yellow",
804                     parent,
805                     new Rect(parentWidth - expectedDividerPadding, expectedBottomDividerTop,
806                             parentWidth, expectedBottomDividerTop + expectedDividerSize),
807                     Color.YELLOW, 1, true);
808         }
809     }
810 
811     /**
812      * layout of vertical LinearLayout.
813      * -----------------------------------
814      * | ------------------------------- |
815      * | |            child1           | |
816      * | ------------------------------- |
817      * | - - - - - - divider - - - - - - |
818      * | ------------------------------- |
819      * | |            child2           | |
820      * | ------------------------------- |
821      * -----------------------------------
822      *
823      * Parent is filled with yellow color. Child 1 is filled with green and child 2 is filled
824      * with blue. Divider is red at the beginning. Throughout this method we reconfigure the
825      * visibility, drawable and paddings of the divider and verify the overall visuals of the
826      * container.
827      */
828     @Test
testDividersInVerticalLayout()829     public void testDividersInVerticalLayout() throws Throwable {
830         final LinearLayout parent =
831                 (LinearLayout) mActivity.findViewById(R.id.linear_vertical_with_divider);
832 
833         final Resources res = mActivity.getResources();
834         final int dividerSize = res.getDimensionPixelSize(R.dimen.linear_layout_divider_size);
835         final int dividerPadding = res.getDimensionPixelSize(R.dimen.linear_layout_divider_padding);
836 
837         assertEquals(LinearLayout.SHOW_DIVIDER_MIDDLE, parent.getShowDividers());
838         assertEquals(dividerPadding, parent.getDividerPadding());
839         final Drawable dividerDrawable = parent.getDividerDrawable();
840         TestUtils.assertAllPixelsOfColor("Divider is red", dividerDrawable,
841                 dividerDrawable.getIntrinsicWidth(), dividerDrawable.getIntrinsicHeight(),
842                 false, Color.RED, 1, true);
843 
844         // Test the initial visuals of the entire parent
845         verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
846                 dividerSize, Color.RED, dividerPadding);
847 
848         // Change the divider to magenta
849         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
850                 () -> parent.setDividerDrawable(
851                         mActivity.getDrawable(R.drawable.linear_layout_divider_magenta)));
852         verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
853                 dividerSize, Color.MAGENTA, dividerPadding);
854 
855         // Change the divider to null (no divider effectively)
856         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
857                 () -> parent.setDividerDrawable(null));
858         verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
859                 0, Color.TRANSPARENT, 0);
860 
861         // Change the divider back to red
862         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
863                 () -> parent.setDividerDrawable(
864                         mActivity.getDrawable(R.drawable.linear_layout_divider_red)));
865         verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
866                 dividerSize, Color.RED, dividerPadding);
867 
868         // Change the padding to half the original size
869         final int halfPadding = dividerPadding / 2;
870         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
871                 () -> parent.setDividerPadding(halfPadding));
872         assertEquals(halfPadding, parent.getDividerPadding());
873         verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
874                 dividerSize, Color.RED, halfPadding);
875 
876         // Change the padding to twice the original size
877         final int doublePadding = dividerPadding * 2;
878         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
879                 () -> parent.setDividerPadding(doublePadding));
880         assertEquals(doublePadding, parent.getDividerPadding());
881         verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
882                 dividerSize, Color.RED, doublePadding);
883 
884         // And back to the original padding
885         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
886                 () -> parent.setDividerPadding(dividerPadding));
887         assertEquals(dividerPadding, parent.getDividerPadding());
888         verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
889                 dividerSize, Color.RED, dividerPadding);
890 
891         // Set show dividers to NONE (no divider effectively)
892         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
893                 () -> parent.setShowDividers(LinearLayout.SHOW_DIVIDER_NONE));
894         assertEquals(LinearLayout.SHOW_DIVIDER_NONE, parent.getShowDividers());
895         verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_NONE,
896                 0, Color.TRANSPARENT, 0);
897 
898         // Show only top divider
899         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
900                 () -> parent.setShowDividers(LinearLayout.SHOW_DIVIDER_BEGINNING));
901         assertEquals(LinearLayout.SHOW_DIVIDER_BEGINNING, parent.getShowDividers());
902         verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_BEGINNING,
903                 dividerSize, Color.RED, dividerPadding);
904 
905         // Show only bottom divider
906         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
907                 () -> parent.setShowDividers(LinearLayout.SHOW_DIVIDER_END));
908         assertEquals(LinearLayout.SHOW_DIVIDER_END, parent.getShowDividers());
909         verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_END,
910                 dividerSize, Color.RED, dividerPadding);
911 
912         // Show top and bottom dividers
913         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
914                 () -> parent.setShowDividers(
915                         LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_END));
916         assertEquals(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_END,
917                 parent.getShowDividers());
918         verifyVisualsOfVerticalLayoutWithDivider(parent,
919                 LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_END,
920                 dividerSize, Color.RED, dividerPadding);
921 
922         // Show top and middle dividers
923         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
924                 () -> parent.setShowDividers(
925                         LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE));
926         assertEquals(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE,
927                 parent.getShowDividers());
928         verifyVisualsOfVerticalLayoutWithDivider(parent,
929                 LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE,
930                 dividerSize, Color.RED, dividerPadding);
931 
932         // Show middle and bottom dividers
933         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
934                 () -> parent.setShowDividers(
935                         LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_END));
936         assertEquals(LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_END,
937                 parent.getShowDividers());
938         verifyVisualsOfVerticalLayoutWithDivider(parent,
939                 LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_END,
940                 dividerSize, Color.RED, dividerPadding);
941 
942         // Show top, middle and bottom dividers
943         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
944                 () -> parent.setShowDividers(
945                         LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE
946                                 | LinearLayout.SHOW_DIVIDER_END));
947         assertEquals(
948                 LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE
949                         | LinearLayout.SHOW_DIVIDER_END,
950                 parent.getShowDividers());
951         verifyVisualsOfVerticalLayoutWithDivider(parent,
952                 LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE
953                         | LinearLayout.SHOW_DIVIDER_END,
954                 dividerSize, Color.RED, dividerPadding);
955     }
956 
verifyVisualsOfHorizontalLayoutWithDivider(LinearLayout parent, int expectedDividerPositionMask, int expectedDividerSize, @ColorInt int expectedDividerColor, int expectedDividerPadding)957     private void verifyVisualsOfHorizontalLayoutWithDivider(LinearLayout parent,
958             int expectedDividerPositionMask,
959             int expectedDividerSize, @ColorInt int expectedDividerColor,
960             int expectedDividerPadding) {
961         final int parentWidth = parent.getWidth();
962         final int parentHeight = parent.getHeight();
963 
964         final boolean expectingLeftDivider =
965                 (expectedDividerPositionMask & LinearLayout.SHOW_DIVIDER_BEGINNING) != 0;
966         final boolean expectingMiddleDivider =
967                 (expectedDividerPositionMask & LinearLayout.SHOW_DIVIDER_MIDDLE) != 0;
968         final boolean expectingRightDivider =
969                 (expectedDividerPositionMask & LinearLayout.SHOW_DIVIDER_END) != 0;
970         final int expectedDividerCount = (expectingLeftDivider ? 1 : 0)
971                 + (expectingMiddleDivider ? 1 : 0) + (expectingRightDivider ? 1 : 0);
972 
973         final int expectedChildWidth =
974                 (parentWidth - expectedDividerCount * expectedDividerSize) / 2;
975 
976         final int expectedLeftChildLeft = expectingLeftDivider ? expectedDividerSize : 0;
977         TestUtils.assertRegionPixelsOfColor("Region of first child is blue", parent,
978                 new Rect(expectedLeftChildLeft, 0,
979                         expectedLeftChildLeft + expectedChildWidth, parentHeight),
980                 Color.BLUE, 1, true);
981 
982         final int expectedRightChildRight =
983                 expectingRightDivider ? parentWidth - expectedDividerSize : parentWidth;
984         TestUtils.assertRegionPixelsOfColor("Region of second child is green", parent,
985                 new Rect(expectedRightChildRight - expectedChildWidth, 0, expectedRightChildRight,
986                         parentHeight),
987                 Color.GREEN, 1, true);
988 
989         if (expectedDividerSize == 0) {
990             return;
991         }
992 
993         // Do we expect left divider?
994         if (expectingLeftDivider) {
995             TestUtils.assertRegionPixelsOfColor(
996                     "Region of left divider is " + TestUtils.formatColorToHex(expectedDividerColor),
997                     parent,
998                     new Rect(0, expectedDividerPadding, expectedDividerSize,
999                             parentHeight - expectedDividerPadding),
1000                     expectedDividerColor, 1, true);
1001             TestUtils.assertRegionPixelsOfColor(
1002                     "Region of top padding of left divider is yellow",
1003                     parent,
1004                     new Rect(0, 0, expectedDividerSize, expectedDividerPadding),
1005                     Color.YELLOW, 1, true);
1006             TestUtils.assertRegionPixelsOfColor(
1007                     "Region of bottom padding of left divider is yellow",
1008                     parent,
1009                     new Rect(0, parentHeight - expectedDividerPadding, expectedDividerSize,
1010                             parentHeight),
1011                     Color.YELLOW, 1, true);
1012         }
1013 
1014         // Do we expect middle divider?
1015         if (expectingMiddleDivider) {
1016             final int expectedMiddleDividerLeft = expectedLeftChildLeft + expectedChildWidth;
1017             TestUtils.assertRegionPixelsOfColor(
1018                     "Region of middle divider is " +
1019                             TestUtils.formatColorToHex(expectedDividerColor),
1020                     parent,
1021                     new Rect(expectedMiddleDividerLeft, expectedDividerPadding,
1022                             expectedMiddleDividerLeft + expectedDividerSize,
1023                             parentHeight - expectedDividerPadding),
1024                     expectedDividerColor, 1, true);
1025             TestUtils.assertRegionPixelsOfColor(
1026                     "Region of top padding of middle divider is yellow",
1027                     parent,
1028                     new Rect(expectedMiddleDividerLeft, 0,
1029                             expectedMiddleDividerLeft + expectedDividerSize,
1030                             expectedDividerPadding),
1031                     Color.YELLOW, 1, true);
1032             TestUtils.assertRegionPixelsOfColor(
1033                     "Region of bottom padding of middle divider is yellow",
1034                     parent,
1035                     new Rect(expectedMiddleDividerLeft, parentHeight - expectedDividerPadding,
1036                             expectedMiddleDividerLeft + expectedDividerSize, parentHeight),
1037                     Color.YELLOW, 1, true);
1038         }
1039 
1040         // Do we expect right divider?
1041         if (expectingRightDivider) {
1042             final int expectedRightDividerLeft = expectedRightChildRight;
1043             TestUtils.assertRegionPixelsOfColor(
1044                     "Region of right divider is " +
1045                             TestUtils.formatColorToHex(expectedDividerColor),
1046                     parent,
1047                     new Rect(expectedRightDividerLeft, expectedDividerPadding,
1048                             expectedRightDividerLeft + expectedDividerSize,
1049                             parentHeight - expectedDividerPadding),
1050                     expectedDividerColor, 1, true);
1051             TestUtils.assertRegionPixelsOfColor(
1052                     "Region of top padding of right divider is yellow",
1053                     parent,
1054                     new Rect(expectedRightDividerLeft, 0,
1055                             expectedRightDividerLeft + expectedDividerSize,
1056                             expectedDividerPadding),
1057                     Color.YELLOW, 1, true);
1058             TestUtils.assertRegionPixelsOfColor(
1059                     "Region of bottom padding of right divider is yellow",
1060                     parent,
1061                     new Rect(expectedRightDividerLeft, parentHeight - expectedDividerPadding,
1062                             expectedRightDividerLeft + expectedDividerSize, parentHeight),
1063                     Color.YELLOW, 1, true);
1064         }
1065     }
1066 
1067     /**
1068      * layout of horizontal LinearLayout.
1069      * -----------------------------------
1070      * | ------------  |  -------------  |
1071      * | |          |     |           |  |
1072      * | |          |  d  |           |  |
1073      * | |          |  i  |           |  |
1074      * | |          |  v  |           |  |
1075      * | |  child1  |  i  |  child2   |  |
1076      * | |          |  d  |           |  |
1077      * | |          |  e  |           |  |
1078      * | |          |  r  |           |  |
1079      * | |          |     |           |  |
1080      * | ------------  |  -------------  |
1081      * -----------------------------------
1082      *
1083      * Parent is filled with yellow color. Child 1 is filled with green and child 2 is filled
1084      * with blue. Divider is red at the beginning. Throughout this method we reconfigure the
1085      * visibility, drawable and paddings of the divider and verify the overall visuals of the
1086      * container.
1087      */
1088     @Test
testDividersInHorizontalLayout()1089     public void testDividersInHorizontalLayout() throws Throwable {
1090         final LinearLayout parent =
1091                 (LinearLayout) mActivity.findViewById(R.id.linear_horizontal_with_divider);
1092 
1093         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1094                 () -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
1095 
1096         final Resources res = mActivity.getResources();
1097         final int dividerSize = res.getDimensionPixelSize(R.dimen.linear_layout_divider_size);
1098         final int dividerPadding = res.getDimensionPixelSize(R.dimen.linear_layout_divider_padding);
1099 
1100         assertEquals(LinearLayout.SHOW_DIVIDER_MIDDLE, parent.getShowDividers());
1101         assertEquals(dividerPadding, parent.getDividerPadding());
1102         final Drawable dividerDrawable = parent.getDividerDrawable();
1103         TestUtils.assertAllPixelsOfColor("Divider is red", dividerDrawable,
1104                 dividerDrawable.getIntrinsicWidth(), dividerDrawable.getIntrinsicHeight(),
1105                 false, Color.RED, 1, true);
1106 
1107         // Test the initial visuals of the entire parent
1108         verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
1109                 dividerSize, Color.RED, dividerPadding);
1110 
1111         // Change the divider to magenta
1112         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1113                 () -> parent.setDividerDrawable(
1114                         mActivity.getDrawable(R.drawable.linear_layout_divider_magenta)));
1115         verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
1116                 dividerSize, Color.MAGENTA, dividerPadding);
1117 
1118         // Change the divider to null (no divider effectively)
1119         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1120                 () -> parent.setDividerDrawable(null));
1121         verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
1122                 0, Color.TRANSPARENT, 0);
1123 
1124         // Change the divider back to red
1125         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1126                 () -> parent.setDividerDrawable(
1127                         mActivity.getDrawable(R.drawable.linear_layout_divider_red)));
1128         verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
1129                 dividerSize, Color.RED, dividerPadding);
1130 
1131         // Change the padding to half the original size
1132         final int halfPadding = dividerPadding / 2;
1133         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1134                 () -> parent.setDividerPadding(halfPadding));
1135         assertEquals(halfPadding, parent.getDividerPadding());
1136         verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
1137                 dividerSize, Color.RED, halfPadding);
1138 
1139         // Change the padding to twice the original size
1140         final int doublePadding = dividerPadding * 2;
1141         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1142                 () -> parent.setDividerPadding(doublePadding));
1143         assertEquals(doublePadding, parent.getDividerPadding());
1144         verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
1145                 dividerSize, Color.RED, doublePadding);
1146 
1147         // And back to the original padding
1148         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1149                 () -> parent.setDividerPadding(dividerPadding));
1150         assertEquals(dividerPadding, parent.getDividerPadding());
1151         verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
1152                 dividerSize, Color.RED, dividerPadding);
1153 
1154         // Set show dividers to NONE (no divider effectively)
1155         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1156                 () -> parent.setShowDividers(LinearLayout.SHOW_DIVIDER_NONE));
1157         assertEquals(LinearLayout.SHOW_DIVIDER_NONE, parent.getShowDividers());
1158         verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_NONE,
1159                 0, Color.TRANSPARENT, 0);
1160 
1161         // Show only left divider
1162         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1163                 () -> parent.setShowDividers(LinearLayout.SHOW_DIVIDER_BEGINNING));
1164         assertEquals(LinearLayout.SHOW_DIVIDER_BEGINNING, parent.getShowDividers());
1165         verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_BEGINNING,
1166                 dividerSize, Color.RED, dividerPadding);
1167 
1168         // Show only right divider
1169         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1170                 () -> parent.setShowDividers(LinearLayout.SHOW_DIVIDER_END));
1171         assertEquals(LinearLayout.SHOW_DIVIDER_END, parent.getShowDividers());
1172         verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_END,
1173                 dividerSize, Color.RED, dividerPadding);
1174 
1175         // Show left and right dividers
1176         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1177                 () -> parent.setShowDividers(
1178                         LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_END));
1179         assertEquals(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_END,
1180                 parent.getShowDividers());
1181         verifyVisualsOfHorizontalLayoutWithDivider(parent,
1182                 LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_END,
1183                 dividerSize, Color.RED, dividerPadding);
1184 
1185         // Show left and middle dividers
1186         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1187                 () -> parent.setShowDividers(
1188                         LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE));
1189         assertEquals(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE,
1190                 parent.getShowDividers());
1191         verifyVisualsOfHorizontalLayoutWithDivider(parent,
1192                 LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE,
1193                 dividerSize, Color.RED, dividerPadding);
1194 
1195         // Show middle and right dividers
1196         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1197                 () -> parent.setShowDividers(
1198                         LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_END));
1199         assertEquals(LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_END,
1200                 parent.getShowDividers());
1201         verifyVisualsOfHorizontalLayoutWithDivider(parent,
1202                 LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_END,
1203                 dividerSize, Color.RED, dividerPadding);
1204 
1205         // Show left, middle and right dividers
1206         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, parent,
1207                 () -> parent.setShowDividers(
1208                         LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE
1209                                 | LinearLayout.SHOW_DIVIDER_END));
1210         assertEquals(
1211                 LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE
1212                         | LinearLayout.SHOW_DIVIDER_END,
1213                 parent.getShowDividers());
1214         verifyVisualsOfHorizontalLayoutWithDivider(parent,
1215                 LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE
1216                         | LinearLayout.SHOW_DIVIDER_END,
1217                 dividerSize, Color.RED, dividerPadding);
1218     }
1219 
1220     @Test
testZeroWeightDistributionHorizontal()1221     public void testZeroWeightDistributionHorizontal() throws Throwable {
1222         // Ensure that weight is correctly distributed when there is no excess space.
1223         final View content = mActivity.findViewById(android.R.id.content);
1224         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, content,
1225                 () -> mActivity.setContentView(R.layout.linearlayout_zero_weight_horizontal));
1226 
1227         final View parent = mActivity.findViewById(R.id.container1);
1228         assertEquals(0, mActivity.findViewById(R.id.view1).getWidth());
1229         assertEquals(0, mActivity.findViewById(R.id.view2).getWidth());
1230         assertEquals(parent.getWidth(), mActivity.findViewById(R.id.view3).getWidth());
1231     }
1232 
1233     @Test
testZeroWeightDistributionVertical()1234     public void testZeroWeightDistributionVertical() throws Throwable {
1235         // Ensure that weight is correctly distributed when there is no excess space.
1236         final View content = mActivity.findViewById(android.R.id.content);
1237         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, content,
1238                 () -> mActivity.setContentView(R.layout.linearlayout_zero_weight_vertical));
1239 
1240         final View parent = mActivity.findViewById(R.id.container1);
1241         assertEquals(0, mActivity.findViewById(R.id.view1).getHeight());
1242         assertEquals(0, mActivity.findViewById(R.id.view2).getHeight());
1243         assertEquals(parent.getHeight(), mActivity.findViewById(R.id.view3).getHeight());
1244     }
1245 
1246     private class MockListView extends ListView {
1247         private final static int DEFAULT_CHILD_BASE_LINE = 1;
1248 
MockListView(Context context)1249         public MockListView(Context context) {
1250             super(context);
1251         }
1252 
getBaseline()1253         public int getBaseline() {
1254             return DEFAULT_CHILD_BASE_LINE;
1255         }
1256     }
1257 
1258     /**
1259      * Add MockLinearLayout to help for testing protected methods in LinearLayout.
1260      * Because we can not access protected methods in LinearLayout directly, we have to
1261      * extends from it and override protected methods so that we can access them in
1262      * our test codes.
1263      */
1264     public static class MockLinearLayout extends LinearLayout {
MockLinearLayout(Context c)1265         public MockLinearLayout(Context c) {
1266             super(c);
1267         }
1268 
MockLinearLayout(Context context, @Nullable AttributeSet attrs)1269         public MockLinearLayout(Context context, @Nullable AttributeSet attrs) {
1270             super(context, attrs);
1271         }
1272 
1273         @Override
checkLayoutParams(ViewGroup.LayoutParams p)1274         protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1275             return super.checkLayoutParams(p);
1276         }
1277 
1278         @Override
generateDefaultLayoutParams()1279         protected LinearLayout.LayoutParams generateDefaultLayoutParams() {
1280             return super.generateDefaultLayoutParams();
1281         }
1282 
1283         @Override
generateLayoutParams(ViewGroup.LayoutParams p)1284         protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1285             return super.generateLayoutParams(p);
1286         }
1287     }
1288 }
1289