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