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 org.xmlpull.v1.XmlPullParser;
20 
21 import android.app.Activity;
22 import android.content.Context;
23 import android.graphics.Color;
24 import android.test.ActivityInstrumentationTestCase;
25 import android.test.ViewAsserts;
26 import android.util.AttributeSet;
27 import android.util.Xml;
28 import android.view.View;
29 import android.view.View.MeasureSpec;
30 import android.view.ViewGroup;
31 import android.view.ViewTreeObserver;
32 import android.widget.AbsoluteLayout;
33 import android.widget.LinearLayout;
34 import android.widget.LinearLayout.LayoutParams;
35 import android.widget.ListView;
36 import android.widget.TextView;
37 import android.widget.cts.R;
38 
39 import java.util.concurrent.CountDownLatch;
40 import java.util.concurrent.TimeUnit;
41 
42 /**
43  * Test {@link LinearLayout}.
44  */
45 public class LinearLayoutTest extends ActivityInstrumentationTestCase<LinearLayoutCtsActivity> {
46     private Context mContext;
47     private Activity mActivity;
48 
LinearLayoutTest()49     public LinearLayoutTest() {
50         super("android.widget.cts", LinearLayoutCtsActivity.class);
51     }
52 
53     @Override
setUp()54     protected void setUp() throws Exception {
55         super.setUp();
56         mActivity = getActivity();
57         mContext = getInstrumentation().getTargetContext();
58     }
59 
testConstructor()60     public void testConstructor() {
61         new LinearLayout(mContext);
62 
63         new LinearLayout(mContext, null);
64 
65         XmlPullParser parser = mContext.getResources().getXml(R.layout.linearlayout_layout);
66         AttributeSet attrs = Xml.asAttributeSet(parser);
67         new LinearLayout(mContext, attrs);
68 
69         try {
70             new LinearLayout(null, null);
71             fail("should throw NullPointerException.");
72         } catch (NullPointerException e) {
73         }
74     }
75 
testAccessBaselineAligned()76     public void testAccessBaselineAligned() {
77         LinearLayout linearLayout = new LinearLayout(mContext);
78         linearLayout.setBaselineAligned(true);
79         assertTrue(linearLayout.isBaselineAligned());
80 
81         linearLayout.setBaselineAligned(false);
82         assertFalse(linearLayout.isBaselineAligned());
83 
84         // android:baselineAligned="false" in LinearLayout weightsum
85         linearLayout = (LinearLayout) mActivity.findViewById(R.id.weightsum);
86         assertFalse(linearLayout.isBaselineAligned());
87 
88         // default mBaselineAligned is true.
89         linearLayout = (LinearLayout) mActivity.findViewById(R.id.horizontal);
90         assertTrue(linearLayout.isBaselineAligned());
91 
92         // default mBaselineAligned is true.
93         // Only applicable if {@link #mOrientation} is horizontal
94         linearLayout = (LinearLayout) mActivity.findViewById(R.id.vertical);
95         assertTrue(linearLayout.isBaselineAligned());
96     }
97 
testGetBaseline()98     public void testGetBaseline() {
99         LinearLayout linearLayout = new LinearLayout(mContext);
100 
101         ListView lv1 = new ListView(mContext);
102         linearLayout.addView(lv1);
103         assertEquals(-1, linearLayout.getBaseline());
104 
105         ListView lv2 = new ListView(mContext);
106         linearLayout.addView(lv2);
107         linearLayout.setBaselineAlignedChildIndex(1);
108         try {
109             linearLayout.getBaseline();
110             fail("LinearLayout.getBaseline() should throw exception here.");
111         } catch (RuntimeException e) {
112         }
113 
114         MockListView lv3 = new MockListView(mContext);
115         linearLayout.addView(lv3);
116         linearLayout.setBaselineAlignedChildIndex(2);
117         assertEquals(lv3.getBaseline(), linearLayout.getBaseline());
118     }
119 
testAccessBaselineAlignedChildIndex()120     public void testAccessBaselineAlignedChildIndex() {
121         LinearLayout linearLayout = new LinearLayout(mContext);
122         // set BaselineAlignedChildIndex
123         ListView lv1 = new ListView(mContext);
124         ListView lv2 = new ListView(mContext);
125         ListView lv3 = new ListView(mContext);
126         linearLayout.addView(lv1);
127         linearLayout.addView(lv2);
128         linearLayout.addView(lv3);
129         linearLayout.setBaselineAlignedChildIndex(1);
130         assertEquals(1, linearLayout.getBaselineAlignedChildIndex());
131 
132         linearLayout.setBaselineAlignedChildIndex(2);
133         assertEquals(2, linearLayout.getBaselineAlignedChildIndex());
134 
135         try {
136             linearLayout.setBaselineAlignedChildIndex(-1);
137             fail("LinearLayout should throw IllegalArgumentException here.");
138         } catch (IllegalArgumentException e) {
139         }
140         try {
141             linearLayout.setBaselineAlignedChildIndex(3);
142             fail("LinearLayout should throw IllegalArgumentException here.");
143         } catch (IllegalArgumentException e) {
144         }
145 
146         linearLayout = (LinearLayout) mActivity.findViewById(R.id.baseline_aligned_child_index);
147         assertEquals(1, linearLayout.getBaselineAlignedChildIndex());
148     }
149 
150     /**
151      * weightsum is a horizontal LinearLayout. There are three children in it.
152      */
testAccessWeightSum()153     public void testAccessWeightSum() {
154         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.weightsum);
155         TextView weight02 = (TextView) mActivity.findViewById(R.id.weight_0_2);
156         TextView weight05 = (TextView) mActivity.findViewById(R.id.weight_0_5);
157         TextView weight03 = (TextView) mActivity.findViewById(R.id.weight_0_3);
158 
159         assertNotNull(parent);
160         assertNotNull(weight02);
161         assertNotNull(weight05);
162         assertNotNull(weight03);
163 
164         assertEquals(mContext.getResources().getString(R.string.horizontal_text_1),
165                 weight02.getText().toString());
166         assertEquals(mContext.getResources().getString(R.string.horizontal_text_2),
167                 weight05.getText().toString());
168         assertEquals(mContext.getResources().getString(R.string.horizontal_text_3),
169                 weight03.getText().toString());
170 
171         assertEquals(LinearLayout.HORIZONTAL, parent.getOrientation());
172         assertEquals(1.0f, parent.getWeightSum());
173 
174         int parentWidth = parent.getWidth();
175         assertEquals(Math.ceil(parentWidth * 0.2), weight02.getWidth(), 1.0);
176         assertEquals(Math.ceil(parentWidth * 0.5), weight05.getWidth(), 1.0);
177         assertEquals(Math.ceil(parentWidth * 0.3), weight03.getWidth(), 1.0);
178     }
179 
testWeightDistribution()180     public void testWeightDistribution() {
181         LinearLayout layout = new LinearLayout(mActivity);
182         for (int i = 0; i < 3; i++) {
183             layout.addView(new View(mActivity), new LayoutParams(0, 0, 1));
184         }
185 
186         int size = 100;
187         int spec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
188 
189         for (int i = 0; i < 3; i++) {
190             View child = layout.getChildAt(i);
191             LayoutParams lp = (LayoutParams) child.getLayoutParams();
192             lp.height = 0;
193             lp.width = LayoutParams.MATCH_PARENT;
194             child.setLayoutParams(lp);
195         }
196         layout.setOrientation(LinearLayout.VERTICAL);
197         layout.measure(spec, spec);
198         layout.layout(0, 0, size, size);
199         assertEquals(100, layout.getWidth());
200         assertEquals(100, layout.getChildAt(0).getWidth());
201         assertEquals(100, layout.getChildAt(1).getWidth());
202         assertEquals(100, layout.getChildAt(2).getWidth());
203         assertEquals(100, layout.getHeight());
204         assertEquals(33, layout.getChildAt(0).getHeight());
205         assertEquals(33, layout.getChildAt(1).getHeight());
206         assertEquals(34, layout.getChildAt(2).getHeight());
207 
208         for (int i = 0; i < 3; i++) {
209             View child = layout.getChildAt(i);
210             LayoutParams lp = (LayoutParams) child.getLayoutParams();
211             lp.height = LayoutParams.MATCH_PARENT;
212             lp.width = 0;
213             child.setLayoutParams(lp);
214         }
215         layout.setOrientation(LinearLayout.HORIZONTAL);
216         layout.measure(spec, spec);
217         layout.layout(0, 0, size, size);
218         assertEquals(100, layout.getWidth());
219         assertEquals(33, layout.getChildAt(0).getWidth());
220         assertEquals(33, layout.getChildAt(1).getWidth());
221         assertEquals(34, layout.getChildAt(2).getWidth());
222         assertEquals(100, layout.getHeight());
223         assertEquals(100, layout.getChildAt(0).getHeight());
224         assertEquals(100, layout.getChildAt(1).getHeight());
225         assertEquals(100, layout.getChildAt(2).getHeight());
226     }
227 
testGenerateLayoutParams()228     public void testGenerateLayoutParams() {
229         ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(320, 240);
230         MockLinearLayout mockLinearLayout = new MockLinearLayout(mContext);
231         LayoutParams layoutParams1 = mockLinearLayout.generateLayoutParams(lp);
232         assertEquals(320, layoutParams1.width);
233         assertEquals(240, layoutParams1.height);
234 
235         // generateLayoutParams() always throw  a RuntimeException.
236 //        XmlPullParser parser = mContext.getResources().getXml(R.layout.linearlayout_layout);
237 //        AttributeSet attrs = Xml.asAttributeSet(parser);
238 //        LinearLayout linearLayout = new LinearLayout(mContext, attrs);
239 //        LayoutParams layoutParams2 = linearLayout.generateLayoutParams(attrs);
240 //        assertEquals(LayoutParams.MATCH_PARENT, layoutParams2.width);
241 //        assertEquals(LayoutParams.WRAP_CONTENT, layoutParams2.height);
242     }
243 
testCheckLayoutParams()244     public void testCheckLayoutParams() {
245         MockLinearLayout mockLinearLayout = new MockLinearLayout(mContext);
246 
247         ViewGroup.LayoutParams params = new AbsoluteLayout.LayoutParams(240, 320, 0, 0);
248         assertFalse(mockLinearLayout.checkLayoutParams(params));
249 
250         params = new LinearLayout.LayoutParams(240, 320);
251         assertTrue(mockLinearLayout.checkLayoutParams(params));
252     }
253 
testGenerateDefaultLayoutParams()254     public void testGenerateDefaultLayoutParams() {
255         MockLinearLayout mockLinearLayout = new MockLinearLayout(mContext);
256 
257         mockLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
258         ViewGroup.LayoutParams param = mockLinearLayout.generateDefaultLayoutParams();
259         assertNotNull(param);
260         assertTrue(param instanceof LinearLayout.LayoutParams);
261         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, param.width);
262         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, param.height);
263 
264         mockLinearLayout.setOrientation(LinearLayout.VERTICAL);
265         param = mockLinearLayout.generateDefaultLayoutParams();
266         assertNotNull(param);
267         assertTrue(param instanceof LinearLayout.LayoutParams);
268         assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, param.width);
269         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, param.height);
270 
271         mockLinearLayout.setOrientation(-1);
272         assertNull(mockLinearLayout.generateDefaultLayoutParams());
273     }
274 
testGenerateLayoutParamsFromMarginParams()275     public void testGenerateLayoutParamsFromMarginParams() {
276         MockLinearLayout layout = new MockLinearLayout(mContext);
277         ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(3, 5);
278         lp.leftMargin = 1;
279         lp.topMargin = 2;
280         lp.rightMargin = 3;
281         lp.bottomMargin = 4;
282         LinearLayout.LayoutParams generated = layout.generateLayoutParams(lp);
283         assertNotNull(generated);
284         assertEquals(3, generated.width);
285         assertEquals(5, generated.height);
286 
287         assertEquals(1, generated.leftMargin);
288         assertEquals(2, generated.topMargin);
289         assertEquals(3, generated.rightMargin);
290         assertEquals(4, generated.bottomMargin);
291     }
292 
293     /**
294      * layout of horizontal LinearLayout.
295      * ----------------------------------------------------
296      * | ------------ |                 |                 |
297      * | | top view | | --------------- |                 |
298      * | |          | | | center view | | --------------- |
299      * | ------------ | |             | | | bottom view | |
300      * |              | --------------- | |             | |
301      * |     parent   |                 | --------------- |
302      * ----------------------------------------------------
303      */
testLayoutHorizontal()304     public void testLayoutHorizontal() {
305         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.horizontal);
306         TextView topView = (TextView) mActivity.findViewById(R.id.gravity_top);
307         TextView centerView = (TextView) mActivity.findViewById(R.id.gravity_center_vertical);
308         TextView bottomView = (TextView) mActivity.findViewById(R.id.gravity_bottom);
309 
310         assertNotNull(parent);
311         assertNotNull(topView);
312         assertNotNull(centerView);
313         assertNotNull(bottomView);
314 
315         assertEquals(mContext.getResources().getString(R.string.horizontal_text_1),
316                 topView.getText().toString());
317         assertEquals(mContext.getResources().getString(R.string.horizontal_text_2),
318                 centerView.getText().toString());
319         assertEquals(mContext.getResources().getString(R.string.horizontal_text_3),
320                 bottomView.getText().toString());
321 
322         assertEquals(LinearLayout.HORIZONTAL, parent.getOrientation());
323 
324         ViewAsserts.assertTopAligned(parent, topView);
325         ViewAsserts.assertVerticalCenterAligned(parent, centerView);
326         ViewAsserts.assertBottomAligned(parent, bottomView);
327 
328         assertEquals(0, topView.getTop());
329         assertEquals(topView.getHeight(), topView.getBottom());
330         assertEquals(0, topView.getLeft());
331         assertEquals(centerView.getLeft(), topView.getRight());
332 
333         int offset = (parent.getHeight() - centerView.getHeight()) / 2;
334         assertEquals(offset, centerView.getTop());
335         assertEquals(offset + centerView.getHeight(), centerView.getBottom());
336         assertEquals(topView.getRight(), centerView.getLeft());
337         assertEquals(bottomView.getLeft(), centerView.getRight());
338 
339         assertEquals(parent.getHeight() - bottomView.getHeight(), bottomView.getTop());
340         assertEquals(parent.getHeight(), bottomView.getBottom());
341         assertEquals(centerView.getRight(), bottomView.getLeft());
342         assertEquals(parent.getWidth(), bottomView.getRight());
343     }
344 
345     /**
346      * layout of vertical LinearLayout.
347      * -----------------------------------
348      * | -------------                   |
349      * | | left view |                   |
350      * | -------------                   |
351      * | - - - - - - - - - - - - - - - - |
352      * |        ---------------          |
353      * |        | center view |          |
354      * |        ---------------          |
355      * | - - - - - - - - - - - - - - - - |
356      * |                  -------------- |
357      * | parent           | right view | |
358      * |                  -------------- |
359      * -----------------------------------
360      */
testLayoutVertical()361     public void testLayoutVertical() {
362         LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.vertical);
363         TextView leftView = (TextView) mActivity.findViewById(R.id.gravity_left);
364         TextView centerView = (TextView) mActivity.findViewById(R.id.gravity_center_horizontal);
365         TextView rightView = (TextView) mActivity.findViewById(R.id.gravity_right);
366 
367         assertNotNull(parent);
368         assertNotNull(leftView);
369         assertNotNull(centerView);
370         assertNotNull(rightView);
371 
372         assertEquals(mContext.getResources().getString(R.string.vertical_text_1),
373                 leftView.getText().toString());
374         assertEquals(mContext.getResources().getString(R.string.vertical_text_2),
375                 centerView.getText().toString());
376         assertEquals(mContext.getResources().getString(R.string.vertical_text_3),
377                 rightView.getText().toString());
378 
379         assertEquals(LinearLayout.VERTICAL, parent.getOrientation());
380 
381         ViewAsserts.assertLeftAligned(parent, leftView);
382         ViewAsserts.assertHorizontalCenterAligned(parent, centerView);
383         ViewAsserts.assertRightAligned(parent, rightView);
384 
385         assertEquals(0, leftView.getTop());
386         assertEquals(centerView.getTop(), leftView.getBottom());
387         assertEquals(0, leftView.getLeft());
388         assertEquals(leftView.getWidth(), leftView.getRight());
389 
390         int offset = (parent.getWidth() - centerView.getWidth()) / 2;
391         assertEquals(leftView.getBottom(), centerView.getTop());
392         assertEquals(rightView.getTop(), centerView.getBottom());
393         assertEquals(offset, centerView.getLeft());
394         assertEquals(offset + centerView.getWidth(), centerView.getRight());
395 
396         assertEquals(centerView.getBottom(), rightView.getTop());
397         assertEquals(parent.getHeight(), rightView.getBottom());
398         assertEquals(parent.getWidth() - rightView.getWidth(), rightView.getLeft());
399         assertEquals(parent.getWidth(), rightView.getRight());
400     }
401 
checkBounds(final ViewGroup viewGroup, final View view, final CountDownLatch countDownLatch, final int left, final int top, final int width, final int height)402     private void checkBounds(final ViewGroup viewGroup, final View view,
403             final CountDownLatch countDownLatch, final int left, final int top,
404             final int width, final int height) {
405         viewGroup.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
406             @Override
407             public boolean onPreDraw() {
408                 assertEquals(left, view.getLeft());
409                 assertEquals(top, view.getTop());
410                 assertEquals(width, view.getWidth());
411                 assertEquals(height, view.getHeight());
412                 countDownLatch.countDown();
413                 viewGroup.getViewTreeObserver().removeOnPreDrawListener(this);
414                 return true;
415             }
416         });
417     }
418 
testVisibilityAffectsLayout()419     public void testVisibilityAffectsLayout() throws Throwable {
420         // Toggling view visibility between GONE/VISIBLE can affect the position of
421         // other children in that container. This test verifies that these changes
422         // on the first child of a LinearLayout affects the position of a second child
423         final int childWidth = 100;
424         final int childHeight = 200;
425         final LinearLayout parent = new LinearLayout(mActivity);
426         ViewGroup.LayoutParams parentParams = new ViewGroup.LayoutParams(
427                 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
428         parent.setLayoutParams(parentParams);
429         final View child1 = new View(mActivity);
430         child1.setBackgroundColor(Color.GREEN);
431         ViewGroup.LayoutParams childParams = new ViewGroup.LayoutParams(childWidth, childHeight);
432         child1.setLayoutParams(childParams);
433         final View child2 = new View(mActivity);
434         child2.setBackgroundColor(Color.RED);
435         childParams = new ViewGroup.LayoutParams(childWidth, childHeight);
436         child2.setLayoutParams(childParams);
437         final ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.linearlayout_root);
438 
439         final CountDownLatch countDownLatch1 = new CountDownLatch(1);
440         runTestOnUiThread(new Runnable() {
441             public void run() {
442                 viewGroup.removeAllViews();
443                 viewGroup.addView(parent);
444                 parent.addView(child1);
445                 parent.addView(child2);
446                 checkBounds(viewGroup, child1, countDownLatch1, 0, 0, childWidth, childHeight);
447                 checkBounds(viewGroup, child2, countDownLatch1,
448                         childWidth, 0, childWidth, childHeight);
449             }
450         });
451         countDownLatch1.await(500, TimeUnit.MILLISECONDS);
452 
453         final CountDownLatch countDownLatch2 = new CountDownLatch(1);
454         runTestOnUiThread(new Runnable() {
455             public void run() {
456                 child1.setVisibility(View.GONE);
457                 checkBounds(viewGroup, child2, countDownLatch2, 0, 0, childWidth, childHeight);
458             }
459         });
460         countDownLatch2.await(500, TimeUnit.MILLISECONDS);
461 
462         final CountDownLatch countDownLatch3 = new CountDownLatch(2);
463         runTestOnUiThread(new Runnable() {
464             public void run() {
465                 child1.setVisibility(View.VISIBLE);
466                 checkBounds(viewGroup, child1, countDownLatch3, 0, 0, childWidth, childHeight);
467                 checkBounds(viewGroup, child2, countDownLatch3,
468                         childWidth, 0, childWidth, childHeight);
469             }
470         });
471         countDownLatch3.await(500, TimeUnit.MILLISECONDS);
472     }
473 
474     private class MockListView extends ListView {
475         private final static int DEFAULT_CHILD_BASE_LINE = 1;
476 
MockListView(Context context)477         public MockListView(Context context) {
478             super(context);
479         }
480 
getBaseline()481         public int getBaseline() {
482             return DEFAULT_CHILD_BASE_LINE;
483         }
484     }
485 
486     /**
487      * Add MockLinearLayout to help for testing protected methods in LinearLayout.
488      * Because we can not access protected methods in LinearLayout directly, we have to
489      * extends from it and override protected methods so that we can access them in
490      * our test codes.
491      */
492     private class MockLinearLayout extends LinearLayout {
MockLinearLayout(Context c)493         public MockLinearLayout(Context c) {
494             super(c);
495         }
496 
497         @Override
checkLayoutParams(ViewGroup.LayoutParams p)498         protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
499             return super.checkLayoutParams(p);
500         }
501 
502         @Override
generateDefaultLayoutParams()503         protected LinearLayout.LayoutParams generateDefaultLayoutParams() {
504             return super.generateDefaultLayoutParams();
505         }
506 
507         @Override
generateLayoutParams(ViewGroup.LayoutParams p)508         protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
509             return super.generateLayoutParams(p);
510         }
511     }
512 }
513