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.annotation.ColorInt;
27 import android.annotation.Nullable;
28 import android.app.Activity;
29 import android.app.Instrumentation;
30 import android.content.Context;
31 import android.content.res.Resources;
32 import android.graphics.Color;
33 import android.graphics.Rect;
34 import android.graphics.drawable.Drawable;
35 import android.support.test.InstrumentationRegistry;
36 import android.support.test.annotation.UiThreadTest;
37 import android.support.test.filters.MediumTest;
38 import android.support.test.rule.ActivityTestRule;
39 import android.support.test.runner.AndroidJUnit4;
40 import android.test.ViewAsserts;
41 import android.util.AttributeSet;
42 import android.util.Xml;
43 import android.view.Gravity;
44 import android.view.View;
45 import android.view.View.MeasureSpec;
46 import android.view.ViewGroup;
47 import android.view.ViewTreeObserver;
48 import android.widget.AbsoluteLayout;
49 import android.widget.LinearLayout;
50 import android.widget.LinearLayout.LayoutParams;
51 import android.widget.ListView;
52 import android.widget.TextView;
53 import android.widget.cts.util.TestUtils;
54 
55 import com.android.compatibility.common.util.WidgetTestUtils;
56 
57 import org.junit.Before;
58 import org.junit.Rule;
59 import org.junit.Test;
60 import org.junit.runner.RunWith;
61 import org.xmlpull.v1.XmlPullParser;
62 
63 import java.util.concurrent.CountDownLatch;
64 import java.util.concurrent.TimeUnit;
65 
66 /**
67  * Test {@link LinearLayout}.
68  */
69 @MediumTest
70 @RunWith(AndroidJUnit4.class)
71 public class LinearLayoutTest {
72     private Instrumentation mInstrumentation;
73     private Activity mActivity;
74 
75     @Rule
76     public ActivityTestRule<LinearLayoutCtsActivity> mActivityRule =
77             new ActivityTestRule<>(LinearLayoutCtsActivity.class);
78 
79     @Before
setup()80     public void setup() {
81         mInstrumentation = InstrumentationRegistry.getInstrumentation();
82         mActivity = mActivityRule.getActivity();
83     }
84 
85     @Test
testConstructor()86     public void testConstructor() {
87         new LinearLayout(mActivity);
88 
89         new LinearLayout(mActivity, null);
90 
91         XmlPullParser parser = mActivity.getResources().getXml(R.layout.linearlayout_layout);
92         AttributeSet attrs = Xml.asAttributeSet(parser);
93         new LinearLayout(mActivity, attrs);
94     }
95 
96     @Test(expected=NullPointerException.class)
testConstructorNullContext()97     public void testConstructorNullContext() {
98         new LinearLayout(null, null);
99     }
100 
101     @UiThreadTest
102     @Test
testAccessBaselineAligned()103     public void testAccessBaselineAligned() {
104         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_empty);
105         parent.setBaselineAligned(true);
106         assertTrue(parent.isBaselineAligned());
107 
108         parent.setBaselineAligned(false);
109         assertFalse(parent.isBaselineAligned());
110 
111         // android:baselineAligned="false" in LinearLayout weightsum
112         parent = (LinearLayout) mActivity.findViewById(R.id.linear_weightsum);
113         assertFalse(parent.isBaselineAligned());
114 
115         // default mBaselineAligned is true.
116         parent = (LinearLayout) mActivity.findViewById(R.id.linear_horizontal);
117         assertTrue(parent.isBaselineAligned());
118 
119         // default mBaselineAligned is true.
120         // Only applicable if {@link #mOrientation} is horizontal
121         parent = (LinearLayout) mActivity.findViewById(R.id.linear_vertical);
122         assertTrue(parent.isBaselineAligned());
123     }
124 
125     @UiThreadTest
126     @Test
testGetBaseline()127     public void testGetBaseline() {
128         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_empty);
129 
130         ListView lv1 = new ListView(mActivity);
131         parent.addView(lv1);
132         assertEquals(-1, parent.getBaseline());
133 
134         ListView lv2 = new ListView(mActivity);
135         parent.addView(lv2);
136         parent.setBaselineAlignedChildIndex(1);
137         try {
138             parent.getBaseline();
139             fail("LinearLayout.getBaseline() should throw exception here.");
140         } catch (RuntimeException e) {
141         }
142 
143         ListView lv3 = new MockListView(mActivity);
144         parent.addView(lv3);
145         parent.setBaselineAlignedChildIndex(2);
146         assertEquals(lv3.getBaseline(), parent.getBaseline());
147     }
148 
149     @UiThreadTest
150     @Test
testAccessBaselineAlignedChildIndex()151     public void testAccessBaselineAlignedChildIndex() {
152         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_empty);
153 
154         // set BaselineAlignedChildIndex
155         ListView lv1 = new ListView(mActivity);
156         ListView lv2 = new ListView(mActivity);
157         ListView lv3 = new ListView(mActivity);
158         parent.addView(lv1);
159         parent.addView(lv2);
160         parent.addView(lv3);
161         parent.setBaselineAlignedChildIndex(1);
162         assertEquals(1, parent.getBaselineAlignedChildIndex());
163 
164         parent.setBaselineAlignedChildIndex(2);
165         assertEquals(2, parent.getBaselineAlignedChildIndex());
166 
167         try {
168             parent.setBaselineAlignedChildIndex(-1);
169             fail("LinearLayout should throw IllegalArgumentException here.");
170         } catch (IllegalArgumentException e) {
171         }
172         try {
173             parent.setBaselineAlignedChildIndex(3);
174             fail("LinearLayout should throw IllegalArgumentException here.");
175         } catch (IllegalArgumentException e) {
176         }
177 
178         parent = (LinearLayout) mActivity.findViewById(R.id.linear_baseline_aligned_child_index);
179         assertEquals(1, parent.getBaselineAlignedChildIndex());
180     }
181 
182     /**
183      * weightsum is a horizontal LinearLayout. There are three children in it.
184      */
185     @Test
testAccessWeightSum()186     public void testAccessWeightSum() {
187         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_weightsum);
188         TextView weight02 = (TextView) parent.findViewById(R.id.weight_0_2);
189         TextView weight05 = (TextView) parent.findViewById(R.id.weight_0_5);
190         TextView weight03 = (TextView) parent.findViewById(R.id.weight_0_3);
191 
192         assertNotNull(parent);
193         assertNotNull(weight02);
194         assertNotNull(weight05);
195         assertNotNull(weight03);
196 
197         assertEquals(mActivity.getResources().getString(R.string.horizontal_text_1),
198                 weight02.getText().toString());
199         assertEquals(mActivity.getResources().getString(R.string.horizontal_text_2),
200                 weight05.getText().toString());
201         assertEquals(mActivity.getResources().getString(R.string.horizontal_text_3),
202                 weight03.getText().toString());
203 
204         assertEquals(LinearLayout.HORIZONTAL, parent.getOrientation());
205         assertEquals(1.0f, parent.getWeightSum(), 0.0f);
206 
207         int parentWidth = parent.getWidth();
208         assertEquals(Math.ceil(parentWidth * 0.2), weight02.getWidth(), 1.0);
209         assertEquals(Math.ceil(parentWidth * 0.5), weight05.getWidth(), 1.0);
210         assertEquals(Math.ceil(parentWidth * 0.3), weight03.getWidth(), 1.0);
211     }
212 
213     @UiThreadTest
214     @Test
testWeightDistribution()215     public void testWeightDistribution() {
216         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_empty);
217 
218         for (int i = 0; i < 3; i++) {
219             parent.addView(new View(mActivity), new LayoutParams(0, 0, 1));
220         }
221 
222         int size = 100;
223         int spec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
224 
225         for (int i = 0; i < 3; i++) {
226             View child = parent.getChildAt(i);
227             LayoutParams lp = (LayoutParams) child.getLayoutParams();
228             lp.height = 0;
229             lp.width = LayoutParams.MATCH_PARENT;
230             child.setLayoutParams(lp);
231         }
232         parent.setOrientation(LinearLayout.VERTICAL);
233         parent.measure(spec, spec);
234         parent.layout(0, 0, size, size);
235         assertEquals(100, parent.getWidth());
236         assertEquals(100, parent.getChildAt(0).getWidth());
237         assertEquals(100, parent.getChildAt(1).getWidth());
238         assertEquals(100, parent.getChildAt(2).getWidth());
239         assertEquals(100, parent.getHeight());
240         assertEquals(33, parent.getChildAt(0).getHeight());
241         assertEquals(33, parent.getChildAt(1).getHeight());
242         assertEquals(34, parent.getChildAt(2).getHeight());
243 
244         for (int i = 0; i < 3; i++) {
245             View child = parent.getChildAt(i);
246             LayoutParams lp = (LayoutParams) child.getLayoutParams();
247             lp.height = LayoutParams.MATCH_PARENT;
248             lp.width = 0;
249             child.setLayoutParams(lp);
250         }
251         parent.setOrientation(LinearLayout.HORIZONTAL);
252         parent.measure(spec, spec);
253         parent.layout(0, 0, size, size);
254         assertEquals(100, parent.getWidth());
255         assertEquals(33, parent.getChildAt(0).getWidth());
256         assertEquals(33, parent.getChildAt(1).getWidth());
257         assertEquals(34, parent.getChildAt(2).getWidth());
258         assertEquals(100, parent.getHeight());
259         assertEquals(100, parent.getChildAt(0).getHeight());
260         assertEquals(100, parent.getChildAt(1).getHeight());
261         assertEquals(100, parent.getChildAt(2).getHeight());
262     }
263 
264     @UiThreadTest
265     @Test
testGenerateLayoutParams()266     public void testGenerateLayoutParams() {
267         ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(320, 240);
268         MockLinearLayout parent = (MockLinearLayout) mActivity.findViewById(R.id.linear_custom);
269         LayoutParams layoutParams1 = parent.generateLayoutParams(lp);
270         assertEquals(320, layoutParams1.width);
271         assertEquals(240, layoutParams1.height);
272     }
273 
274     @UiThreadTest
275     @Test
testCheckLayoutParams()276     public void testCheckLayoutParams() {
277         MockLinearLayout parent = (MockLinearLayout) mActivity.findViewById(R.id.linear_custom);
278 
279         ViewGroup.LayoutParams params = new AbsoluteLayout.LayoutParams(240, 320, 0, 0);
280         assertFalse(parent.checkLayoutParams(params));
281 
282         params = new LinearLayout.LayoutParams(240, 320);
283         assertTrue(parent.checkLayoutParams(params));
284     }
285 
286     @UiThreadTest
287     @Test
testGenerateDefaultLayoutParams()288     public void testGenerateDefaultLayoutParams() {
289         MockLinearLayout parent = (MockLinearLayout) mActivity.findViewById(R.id.linear_custom);
290 
291         parent.setOrientation(LinearLayout.HORIZONTAL);
292         ViewGroup.LayoutParams param = parent.generateDefaultLayoutParams();
293         assertNotNull(param);
294         assertTrue(param instanceof LinearLayout.LayoutParams);
295         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, param.width);
296         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, param.height);
297 
298         parent.setOrientation(LinearLayout.VERTICAL);
299         param = parent.generateDefaultLayoutParams();
300         assertNotNull(param);
301         assertTrue(param instanceof LinearLayout.LayoutParams);
302         assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, param.width);
303         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, param.height);
304 
305         parent.setOrientation(-1);
306         assertNull(parent.generateDefaultLayoutParams());
307     }
308 
309     @UiThreadTest
310     @Test
testGenerateLayoutParamsFromMarginParams()311     public void testGenerateLayoutParamsFromMarginParams() {
312         MockLinearLayout parent = (MockLinearLayout) mActivity.findViewById(R.id.linear_custom);
313 
314         ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(3, 5);
315         lp.leftMargin = 1;
316         lp.topMargin = 2;
317         lp.rightMargin = 3;
318         lp.bottomMargin = 4;
319         LinearLayout.LayoutParams generated = parent.generateLayoutParams(lp);
320         assertNotNull(generated);
321         assertEquals(3, generated.width);
322         assertEquals(5, generated.height);
323 
324         assertEquals(1, generated.leftMargin);
325         assertEquals(2, generated.topMargin);
326         assertEquals(3, generated.rightMargin);
327         assertEquals(4, generated.bottomMargin);
328     }
329 
330     /**
331      * layout of horizontal LinearLayout.
332      * ----------------------------------------------------
333      * | ------------ |                 |                 |
334      * | | top view | | --------------- |                 |
335      * | |          | | | center view | | --------------- |
336      * | ------------ | |             | | | bottom view | |
337      * |              | --------------- | |             | |
338      * |     parent   |                 | --------------- |
339      * ----------------------------------------------------
340      */
341     @Test
testLayoutHorizontal()342     public void testLayoutHorizontal() {
343         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_horizontal);
344         TextView topView = (TextView) mActivity.findViewById(R.id.gravity_top);
345         TextView centerView = (TextView) mActivity.findViewById(R.id.gravity_center_vertical);
346         TextView bottomView = (TextView) mActivity.findViewById(R.id.gravity_bottom);
347 
348         assertNotNull(parent);
349         assertNotNull(topView);
350         assertNotNull(centerView);
351         assertNotNull(bottomView);
352 
353         assertEquals(mActivity.getResources().getString(R.string.horizontal_text_1),
354                 topView.getText().toString());
355         assertEquals(mActivity.getResources().getString(R.string.horizontal_text_2),
356                 centerView.getText().toString());
357         assertEquals(mActivity.getResources().getString(R.string.horizontal_text_3),
358                 bottomView.getText().toString());
359 
360         assertEquals(LinearLayout.HORIZONTAL, parent.getOrientation());
361 
362         ViewAsserts.assertTopAligned(parent, topView);
363         ViewAsserts.assertVerticalCenterAligned(parent, centerView);
364         ViewAsserts.assertBottomAligned(parent, bottomView);
365 
366         assertEquals(0, topView.getTop());
367         assertEquals(topView.getHeight(), topView.getBottom());
368         assertEquals(0, topView.getLeft());
369         assertEquals(centerView.getLeft(), topView.getRight());
370 
371         int offset = (parent.getHeight() - centerView.getHeight()) / 2;
372         assertEquals(offset, centerView.getTop());
373         assertEquals(offset + centerView.getHeight(), centerView.getBottom());
374         assertEquals(topView.getRight(), centerView.getLeft());
375         assertEquals(bottomView.getLeft(), centerView.getRight());
376 
377         assertEquals(parent.getHeight() - bottomView.getHeight(), bottomView.getTop());
378         assertEquals(parent.getHeight(), bottomView.getBottom());
379         assertEquals(centerView.getRight(), bottomView.getLeft());
380         assertEquals(parent.getWidth(), bottomView.getRight());
381     }
382 
383     /**
384      * layout of vertical LinearLayout.
385      * -----------------------------------
386      * | -------------                   |
387      * | | left view |                   |
388      * | -------------                   |
389      * | - - - - - - - - - - - - - - - - |
390      * |        ---------------          |
391      * |        | center view |          |
392      * |        ---------------          |
393      * | - - - - - - - - - - - - - - - - |
394      * |                  -------------- |
395      * | parent           | right view | |
396      * |                  -------------- |
397      * -----------------------------------
398      */
399     @Test
testLayoutVertical()400     public void testLayoutVertical() {
401         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_vertical);
402         TextView leftView = (TextView) mActivity.findViewById(R.id.gravity_left);
403         TextView centerView = (TextView) mActivity.findViewById(R.id.gravity_center_horizontal);
404         TextView rightView = (TextView) mActivity.findViewById(R.id.gravity_right);
405 
406         assertNotNull(parent);
407         assertNotNull(leftView);
408         assertNotNull(centerView);
409         assertNotNull(rightView);
410 
411         assertEquals(mActivity.getResources().getString(R.string.vertical_text_1),
412                 leftView.getText().toString());
413         assertEquals(mActivity.getResources().getString(R.string.vertical_text_2),
414                 centerView.getText().toString());
415         assertEquals(mActivity.getResources().getString(R.string.vertical_text_3),
416                 rightView.getText().toString());
417 
418         assertEquals(LinearLayout.VERTICAL, parent.getOrientation());
419 
420         ViewAsserts.assertLeftAligned(parent, leftView);
421         ViewAsserts.assertHorizontalCenterAligned(parent, centerView);
422         ViewAsserts.assertRightAligned(parent, rightView);
423 
424         assertEquals(0, leftView.getTop());
425         assertEquals(centerView.getTop(), leftView.getBottom());
426         assertEquals(0, leftView.getLeft());
427         assertEquals(leftView.getWidth(), leftView.getRight());
428 
429         int offset = (parent.getWidth() - centerView.getWidth()) / 2;
430         assertEquals(leftView.getBottom(), centerView.getTop());
431         assertEquals(rightView.getTop(), centerView.getBottom());
432         assertEquals(offset, centerView.getLeft());
433         assertEquals(offset + centerView.getWidth(), centerView.getRight());
434 
435         assertEquals(centerView.getBottom(), rightView.getTop());
436         assertEquals(parent.getHeight(), rightView.getBottom());
437         assertEquals(parent.getWidth() - rightView.getWidth(), rightView.getLeft());
438         assertEquals(parent.getWidth(), rightView.getRight());
439     }
440 
441     @Test
testVerticalCenterGravityOnHorizontalLayout()442     public void testVerticalCenterGravityOnHorizontalLayout() throws Throwable {
443         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_weightsum);
444         TextView leftView = (TextView) parent.findViewById(R.id.weight_0_2);
445         TextView centerView = (TextView) parent.findViewById(R.id.weight_0_5);
446         TextView rightView = (TextView) parent.findViewById(R.id.weight_0_3);
447 
448         mActivityRule.runOnUiThread(() -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
449         mInstrumentation.waitForIdleSync();
450 
451         int originalLeftViewLeft = leftView.getLeft();
452         int originalLeftViewRight = leftView.getRight();
453         int originalCenterViewLeft = centerView.getLeft();
454         int originalCenterViewRight = centerView.getRight();
455         int originalRightViewLeft = rightView.getLeft();
456         int originalRightViewRight = rightView.getRight();
457 
458         mActivityRule.runOnUiThread(() -> parent.setVerticalGravity(Gravity.CENTER_VERTICAL));
459         mInstrumentation.waitForIdleSync();
460 
461         assertEquals(Gravity.CENTER_VERTICAL, parent.getGravity() & Gravity.VERTICAL_GRAVITY_MASK);
462 
463         ViewAsserts.assertVerticalCenterAligned(parent, leftView);
464         ViewAsserts.assertVerticalCenterAligned(parent, centerView);
465         ViewAsserts.assertVerticalCenterAligned(parent, rightView);
466 
467         final int parentHeight = parent.getHeight();
468 
469         int verticalOffset = (parentHeight - leftView.getHeight()) / 2;
470         assertEquals(verticalOffset, leftView.getTop());
471         assertEquals(verticalOffset + leftView.getHeight(), leftView.getBottom());
472         assertEquals(originalLeftViewLeft, leftView.getLeft());
473         assertEquals(originalLeftViewRight, leftView.getRight());
474 
475         verticalOffset = (parentHeight - centerView.getHeight()) / 2;
476         assertEquals(verticalOffset, centerView.getTop());
477         assertEquals(verticalOffset + centerView.getHeight(), centerView.getBottom());
478         assertEquals(originalCenterViewLeft, centerView.getLeft());
479         assertEquals(originalCenterViewRight, centerView.getRight());
480 
481         verticalOffset = (parentHeight - rightView.getHeight()) / 2;
482         assertEquals(verticalOffset, rightView.getTop());
483         assertEquals(verticalOffset + rightView.getHeight(), rightView.getBottom());
484         assertEquals(originalRightViewLeft, rightView.getLeft());
485         assertEquals(originalRightViewRight, rightView.getRight());
486     }
487 
488     @Test
testBottomGravityOnHorizontalLayout()489     public void testBottomGravityOnHorizontalLayout() throws Throwable {
490         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_weightsum);
491         TextView leftView = (TextView) parent.findViewById(R.id.weight_0_2);
492         TextView centerView = (TextView) parent.findViewById(R.id.weight_0_5);
493         TextView rightView = (TextView) parent.findViewById(R.id.weight_0_3);
494 
495         mActivityRule.runOnUiThread(() -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
496         mInstrumentation.waitForIdleSync();
497 
498         int originalLeftViewLeft = leftView.getLeft();
499         int originalLeftViewRight = leftView.getRight();
500         int originalCenterViewLeft = centerView.getLeft();
501         int originalCenterViewRight = centerView.getRight();
502         int originalRightViewLeft = rightView.getLeft();
503         int originalRightViewRight = rightView.getRight();
504 
505         mActivityRule.runOnUiThread(() -> parent.setVerticalGravity(Gravity.BOTTOM));
506         mInstrumentation.waitForIdleSync();
507 
508         assertEquals(Gravity.BOTTOM, parent.getGravity() & Gravity.VERTICAL_GRAVITY_MASK);
509 
510         ViewAsserts.assertBottomAligned(parent, leftView);
511         ViewAsserts.assertBottomAligned(parent, centerView);
512         ViewAsserts.assertBottomAligned(parent, rightView);
513 
514         final int parentHeight = parent.getHeight();
515 
516         assertEquals(parentHeight - leftView.getHeight(), leftView.getTop());
517         assertEquals(parentHeight, leftView.getBottom());
518         assertEquals(originalLeftViewLeft, leftView.getLeft());
519         assertEquals(originalLeftViewRight, leftView.getRight());
520 
521         assertEquals(parentHeight - centerView.getHeight(), centerView.getTop());
522         assertEquals(parentHeight, centerView.getBottom());
523         assertEquals(originalCenterViewLeft, centerView.getLeft());
524         assertEquals(originalCenterViewRight, centerView.getRight());
525 
526         assertEquals(parentHeight - rightView.getHeight(), rightView.getTop());
527         assertEquals(parentHeight, rightView.getBottom());
528         assertEquals(originalRightViewLeft, rightView.getLeft());
529         assertEquals(originalRightViewRight, rightView.getRight());
530     }
531 
532     @Test
testHorizontalCenterGravityOnVerticalLayout()533     public void testHorizontalCenterGravityOnVerticalLayout() throws Throwable {
534         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.linear_weightsum_vertical);
535         TextView topView = (TextView) parent.findViewById(R.id.weight_0_1);
536         TextView centerView = (TextView) parent.findViewById(R.id.weight_0_4);
537         TextView bottomView = (TextView) parent.findViewById(R.id.weight_0_5);
538 
539         mActivityRule.runOnUiThread(() -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
540         mInstrumentation.waitForIdleSync();
541 
542         final int parentWidth = parent.getHeight();
543 
544         int originalTopViewTop = topView.getTop();
545         int originalTopViewBottom = topView.getBottom();
546         int originalCenterViewTop = centerView.getTop();
547         int originalCenterViewBottom = centerView.getBottom();
548         int originalBottomViewTop = bottomView.getTop();
549         int originalBottomViewBottom = bottomView.getBottom();
550 
551         mActivityRule.runOnUiThread(
552                 () -> parent.setHorizontalGravity(Gravity.CENTER_HORIZONTAL));
553         mInstrumentation.waitForIdleSync();
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         mActivityRule.runOnUiThread(() -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
589         mInstrumentation.waitForIdleSync();
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         mActivityRule.runOnUiThread(() -> parent.setHorizontalGravity(Gravity.RIGHT));
601         mInstrumentation.waitForIdleSync();
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         mActivityRule.runOnUiThread(() -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
1094         mInstrumentation.waitForIdleSync();
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     private class MockListView extends ListView {
1221         private final static int DEFAULT_CHILD_BASE_LINE = 1;
1222 
MockListView(Context context)1223         public MockListView(Context context) {
1224             super(context);
1225         }
1226 
getBaseline()1227         public int getBaseline() {
1228             return DEFAULT_CHILD_BASE_LINE;
1229         }
1230     }
1231 
1232     /**
1233      * Add MockLinearLayout to help for testing protected methods in LinearLayout.
1234      * Because we can not access protected methods in LinearLayout directly, we have to
1235      * extends from it and override protected methods so that we can access them in
1236      * our test codes.
1237      */
1238     public static class MockLinearLayout extends LinearLayout {
MockLinearLayout(Context c)1239         public MockLinearLayout(Context c) {
1240             super(c);
1241         }
1242 
MockLinearLayout(Context context, @Nullable AttributeSet attrs)1243         public MockLinearLayout(Context context, @Nullable AttributeSet attrs) {
1244             super(context, attrs);
1245         }
1246 
1247         @Override
checkLayoutParams(ViewGroup.LayoutParams p)1248         protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1249             return super.checkLayoutParams(p);
1250         }
1251 
1252         @Override
generateDefaultLayoutParams()1253         protected LinearLayout.LayoutParams generateDefaultLayoutParams() {
1254             return super.generateDefaultLayoutParams();
1255         }
1256 
1257         @Override
generateLayoutParams(ViewGroup.LayoutParams p)1258         protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1259             return super.generateLayoutParams(p);
1260         }
1261     }
1262 }
1263