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