Home | History | Annotate | Download | only in cts
      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.assertSame;
     22 import static org.junit.Assert.assertTrue;
     23 import static org.junit.Assert.fail;
     24 import static org.mockito.Matchers.eq;
     25 import static org.mockito.Mockito.spy;
     26 import static org.mockito.Mockito.verify;
     27 
     28 import android.app.Activity;
     29 import android.app.Instrumentation;
     30 import android.content.Context;
     31 import android.graphics.Color;
     32 import android.graphics.Rect;
     33 import android.util.AttributeSet;
     34 import android.util.Xml;
     35 import android.view.View;
     36 import android.view.View.MeasureSpec;
     37 import android.view.ViewGroup;
     38 import android.widget.EdgeEffect;
     39 import android.widget.FrameLayout;
     40 import android.widget.ScrollView;
     41 import android.widget.TextView;
     42 import android.widget.cts.util.TestUtils;
     43 
     44 import androidx.test.InstrumentationRegistry;
     45 import androidx.test.annotation.UiThreadTest;
     46 import androidx.test.filters.MediumTest;
     47 import androidx.test.rule.ActivityTestRule;
     48 import androidx.test.runner.AndroidJUnit4;
     49 
     50 import com.android.compatibility.common.util.PollingCheck;
     51 
     52 import org.junit.Before;
     53 import org.junit.Rule;
     54 import org.junit.Test;
     55 import org.junit.runner.RunWith;
     56 import org.xmlpull.v1.XmlPullParser;
     57 
     58 /**
     59  * Test {@link ScrollView}.
     60  */
     61 @MediumTest
     62 @RunWith(AndroidJUnit4.class)
     63 public class ScrollViewTest {
     64     // view dpi constants. Must match those defined in scroll_view layout
     65     private static final int ITEM_WIDTH_DPI  = 250;
     66     private static final int ITEM_HEIGHT_DPI = 100;
     67     private static final int ITEM_COUNT  = 15;
     68     private static final int PAGE_WIDTH_DPI  = 100;
     69     private static final int PAGE_HEIGHT_DPI = 100;
     70     private static final int TOLERANCE = 2;
     71 
     72     private int mItemWidth;
     73     private int mItemHeight;
     74     private int mPageWidth;
     75     private int mPageHeight;
     76     private int mScrollBottom;
     77     private int mScrollRight;
     78 
     79     private Instrumentation mInstrumentation;
     80     private Activity mActivity;
     81     private ScrollView mScrollViewRegular;
     82     private ScrollView mScrollViewCustom;
     83     private MyScrollView mScrollViewCustomEmpty;
     84 
     85     @Rule
     86     public ActivityTestRule<ScrollViewCtsActivity> mActivityRule =
     87             new ActivityTestRule<>(ScrollViewCtsActivity.class);
     88 
     89     @Before
     90     public void setup() {
     91         mInstrumentation = InstrumentationRegistry.getInstrumentation();
     92         mActivity = mActivityRule.getActivity();
     93         mScrollViewRegular = (ScrollView) mActivity.findViewById(R.id.scroll_view_regular);
     94         mScrollViewCustom = (ScrollView) mActivity.findViewById(R.id.scroll_view_custom);
     95         mScrollViewCustomEmpty = (MyScrollView) mActivity.findViewById(
     96                 R.id.scroll_view_custom_empty);
     97 
     98         // calculate pixel positions from dpi constants.
     99         mItemWidth = TestUtils.dpToPx(mActivity, ITEM_WIDTH_DPI);
    100         mItemHeight = TestUtils.dpToPx(mActivity, ITEM_HEIGHT_DPI);
    101         mPageWidth = TestUtils.dpToPx(mActivity, PAGE_WIDTH_DPI);
    102         mPageHeight = TestUtils.dpToPx(mActivity, PAGE_HEIGHT_DPI);
    103 
    104         mScrollBottom = mItemHeight * ITEM_COUNT - mPageHeight;
    105         mScrollRight = mItemWidth - mPageWidth;
    106     }
    107 
    108     @Test
    109     public void testConstructor() {
    110         XmlPullParser parser = mActivity.getResources().getLayout(R.layout.scrollview_layout);
    111         AttributeSet attrs = Xml.asAttributeSet(parser);
    112         new ScrollView(mActivity);
    113 
    114         new ScrollView(mActivity, attrs);
    115 
    116         new ScrollView(mActivity, attrs, 0);
    117     }
    118 
    119     @UiThreadTest
    120     @Test
    121     public void testGetMaxScrollAmount() {
    122         // the value is half of total layout height
    123         mScrollViewRegular.layout(0, 0, 100, 200);
    124         assertEquals((200 - 0) / 2, mScrollViewRegular.getMaxScrollAmount());
    125 
    126         mScrollViewRegular.layout(0, 0, 150, 100);
    127         assertEquals((100 - 0) / 2, mScrollViewRegular.getMaxScrollAmount());
    128     }
    129 
    130     @UiThreadTest
    131     @Test
    132     public void testAddView() {
    133         TextView child0 = new TextView(mActivity);
    134         mScrollViewRegular.addView(child0);
    135         assertSame(child0, mScrollViewRegular.getChildAt(0));
    136 
    137         assertEquals(1, mScrollViewRegular.getChildCount());
    138         TextView child1 = new TextView(mActivity);
    139         try {
    140             mScrollViewRegular.addView(child1);
    141             fail("ScrollView can host only one direct child");
    142         } catch (IllegalStateException e) {
    143             // expected
    144         }
    145         assertEquals(1, mScrollViewRegular.getChildCount());
    146     }
    147 
    148     @UiThreadTest
    149     @Test
    150     public void testAddViewWithIndex() {
    151         TextView child0 = new TextView(mActivity);
    152         mScrollViewRegular.addView(child0, 0);
    153         assertSame(child0, mScrollViewRegular.getChildAt(0));
    154 
    155         assertEquals(1, mScrollViewRegular.getChildCount());
    156         TextView child1 = new TextView(mActivity);
    157         try {
    158             mScrollViewRegular.addView(child1, 1);
    159             fail("ScrollView can host only one direct child");
    160         } catch (IllegalStateException e) {
    161             // expected
    162         }
    163         assertEquals(1, mScrollViewRegular.getChildCount());
    164 
    165         mScrollViewRegular.removeAllViews();
    166         mScrollViewRegular = new ScrollView(mActivity);
    167         mScrollViewRegular.addView(child0, -1);
    168         assertSame(child0, mScrollViewRegular.getChildAt(0));
    169 
    170         assertEquals(1, mScrollViewRegular.getChildCount());
    171         child1 = new TextView(mActivity);
    172         try {
    173             mScrollViewRegular.addView(child1, -1);
    174             fail("ScrollView can host only one direct child");
    175         } catch (IllegalStateException e) {
    176             // expected
    177         }
    178         assertEquals(1, mScrollViewRegular.getChildCount());
    179 
    180         mScrollViewRegular.removeAllViews();
    181         mScrollViewRegular = new ScrollView(mActivity);
    182         try {
    183             mScrollViewRegular.addView(child0, 1);
    184             fail("ScrollView can host only one direct child");
    185         } catch (IndexOutOfBoundsException e) {
    186             // expected
    187         }
    188     }
    189 
    190     @UiThreadTest
    191     @Test
    192     public void testAddViewWithLayoutParams() {
    193         TextView child0 = new TextView(mActivity);
    194         mScrollViewRegular.addView(child0, new ViewGroup.LayoutParams(200, 100));
    195         assertSame(child0, mScrollViewRegular.getChildAt(0));
    196         assertEquals(200, child0.getLayoutParams().width);
    197         assertEquals(100, child0.getLayoutParams().height);
    198 
    199         assertEquals(1, mScrollViewRegular.getChildCount());
    200         TextView child1 = new TextView(mActivity);
    201         try {
    202             mScrollViewRegular.addView(child1, new ViewGroup.LayoutParams(200, 100));
    203             fail("ScrollView can host only one direct child");
    204         } catch (IllegalStateException e) {
    205             // expected
    206         }
    207         assertEquals(1, mScrollViewRegular.getChildCount());
    208 
    209         mScrollViewRegular.removeAllViews();
    210         mScrollViewRegular = new ScrollView(mActivity);
    211         child0 = new TextView(mActivity);
    212         try {
    213             mScrollViewRegular.addView(child0, null);
    214             fail("The LayoutParams should not be null!");
    215         } catch (NullPointerException e) {
    216             // expected
    217         }
    218     }
    219 
    220     @UiThreadTest
    221     @Test
    222     public void testAddViewWithIndexAndLayoutParams() {
    223         TextView child0 = new TextView(mActivity);
    224         mScrollViewRegular.addView(child0, 0, new ViewGroup.LayoutParams(200, 100));
    225         assertSame(child0, mScrollViewRegular.getChildAt(0));
    226         assertEquals(200, child0.getLayoutParams().width);
    227         assertEquals(100, child0.getLayoutParams().height);
    228 
    229         assertEquals(1, mScrollViewRegular.getChildCount());
    230         TextView child1 = new TextView(mActivity);
    231         try {
    232             mScrollViewRegular.addView(child1, 0, new ViewGroup.LayoutParams(200, 100));
    233             fail("ScrollView can host only one direct child");
    234         } catch (IllegalStateException e) {
    235             // expected
    236         }
    237         assertEquals(1, mScrollViewRegular.getChildCount());
    238 
    239         mScrollViewRegular.removeAllViews();
    240         child0 = new TextView(mActivity);
    241         try {
    242             mScrollViewRegular.addView(child0, null);
    243             fail("The LayoutParams should not be null!");
    244         } catch (NullPointerException e) {
    245             // expected
    246         }
    247 
    248         mScrollViewRegular.removeAllViews();
    249         mScrollViewRegular.addView(child0, -1, new ViewGroup.LayoutParams(300, 150));
    250         assertSame(child0, mScrollViewRegular.getChildAt(0));
    251         assertEquals(300, child0.getLayoutParams().width);
    252         assertEquals(150, child0.getLayoutParams().height);
    253 
    254         assertEquals(1, mScrollViewRegular.getChildCount());
    255         child1 = new TextView(mActivity);
    256         try {
    257             mScrollViewRegular.addView(child1, -1, new ViewGroup.LayoutParams(200, 100));
    258             fail("ScrollView can host only one direct child");
    259         } catch (IllegalStateException e) {
    260             // expected
    261         }
    262         assertEquals(1, mScrollViewRegular.getChildCount());
    263 
    264         mScrollViewRegular.removeAllViews();
    265         try {
    266             mScrollViewRegular.addView(child0, 1, new ViewGroup.LayoutParams(200, 100));
    267             fail("ScrollView can host only one direct child");
    268         } catch (IndexOutOfBoundsException e) {
    269             // expected
    270         }
    271     }
    272 
    273     @UiThreadTest
    274     @Test
    275     public void testAccessFillViewport() {
    276         assertFalse(mScrollViewRegular.isFillViewport());
    277         mScrollViewRegular.layout(0, 0, 100, 100);
    278         assertFalse(mScrollViewRegular.isLayoutRequested());
    279 
    280         mScrollViewRegular.setFillViewport(false);
    281         assertFalse(mScrollViewRegular.isFillViewport());
    282         assertFalse(mScrollViewRegular.isLayoutRequested());
    283 
    284         mScrollViewRegular.setFillViewport(true);
    285         assertTrue(mScrollViewRegular.isFillViewport());
    286         assertTrue(mScrollViewRegular.isLayoutRequested());
    287 
    288         mScrollViewRegular.layout(0, 0, 100, 100);
    289         assertFalse(mScrollViewRegular.isLayoutRequested());
    290 
    291         mScrollViewRegular.setFillViewport(false);
    292         assertFalse(mScrollViewRegular.isFillViewport());
    293         assertTrue(mScrollViewRegular.isLayoutRequested());
    294     }
    295 
    296     @Test
    297     public void testAccessSmoothScrollingEnabled() throws Throwable {
    298         assertTrue(mScrollViewCustom.isSmoothScrollingEnabled());
    299 
    300         // scroll immediately
    301         mScrollViewCustom.setSmoothScrollingEnabled(false);
    302         assertFalse(mScrollViewCustom.isSmoothScrollingEnabled());
    303 
    304         mActivityRule.runOnUiThread(() -> mScrollViewCustom.fullScroll(View.FOCUS_DOWN));
    305 
    306         assertEquals(mScrollBottom, mScrollViewCustom.getScrollY(), TOLERANCE);
    307 
    308         mActivityRule.runOnUiThread(() -> mScrollViewCustom.fullScroll(View.FOCUS_UP));
    309         assertEquals(0, mScrollViewCustom.getScrollY());
    310 
    311         // smooth scroll
    312         mScrollViewCustom.setSmoothScrollingEnabled(true);
    313         assertTrue(mScrollViewCustom.isSmoothScrollingEnabled());
    314 
    315         mActivityRule.runOnUiThread(() -> mScrollViewCustom.fullScroll(View.FOCUS_DOWN));
    316         pollingCheckSmoothScrolling(0, 0, 0, mScrollBottom);
    317         assertEquals(mScrollBottom, mScrollViewCustom.getScrollY(), TOLERANCE);
    318 
    319         mActivityRule.runOnUiThread(() -> mScrollViewCustom.fullScroll(View.FOCUS_UP));
    320         pollingCheckSmoothScrolling(0, 0, mScrollBottom, 0);
    321         assertEquals(0, mScrollViewCustom.getScrollY());
    322     }
    323 
    324     @UiThreadTest
    325     @Test
    326     public void testMeasureChild() {
    327         MyView child = new MyView(mActivity);
    328         child.setBackgroundDrawable(null);
    329         child.setPadding(0, 0, 0, 0);
    330         child.setMinimumHeight(30);
    331         child.setLayoutParams(new ViewGroup.LayoutParams(100, 100));
    332         child.measure(MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY),
    333                 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY));
    334 
    335         assertEquals(100, child.getMeasuredHeight());
    336         assertEquals(100, child.getMeasuredWidth());
    337 
    338         mScrollViewCustomEmpty.measureChild(child,
    339                 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY),
    340                 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY));
    341 
    342         assertEquals(30, child.getMeasuredHeight());
    343         assertEquals(100, child.getMeasuredWidth());
    344     }
    345 
    346     @UiThreadTest
    347     @Test
    348     public void testMeasureChildWithMargins() {
    349         MyView child = new MyView(mActivity);
    350         child.setBackgroundDrawable(null);
    351         child.setPadding(0, 0, 0, 0);
    352         child.setMinimumHeight(30);
    353         child.setLayoutParams(new ViewGroup.MarginLayoutParams(100, 100));
    354         child.measure(MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY),
    355                 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY));
    356 
    357         assertEquals(100, child.getMeasuredHeight());
    358         assertEquals(100, child.getMeasuredWidth());
    359 
    360         mScrollViewCustomEmpty.measureChildWithMargins(child,
    361                 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 5,
    362                 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 5);
    363 
    364         assertEquals(30, child.getMeasuredHeight());
    365         assertEquals(100, child.getMeasuredWidth());
    366     }
    367 
    368     @UiThreadTest
    369     @Test
    370     public void testMeasureSpecs() {
    371         MyView child = spy(new MyView(mActivity));
    372         mScrollViewCustomEmpty.addView(child);
    373 
    374         mScrollViewCustomEmpty.measureChild(child,
    375                 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY),
    376                 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY));
    377         verify(child).onMeasure(
    378                 eq(MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY)),
    379                 eq(MeasureSpec.makeMeasureSpec(100, MeasureSpec.UNSPECIFIED)));
    380     }
    381 
    382     @UiThreadTest
    383     @Test
    384     public void testMeasureSpecsWithPadding() {
    385         MyView child = spy(new MyView(mActivity));
    386         mScrollViewCustomEmpty.setPadding(3, 5, 7, 11);
    387         mScrollViewCustomEmpty.addView(child);
    388 
    389         mScrollViewCustomEmpty.measureChild(child,
    390                 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY),
    391                 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY));
    392         verify(child).onMeasure(
    393                 eq(MeasureSpec.makeMeasureSpec(140, MeasureSpec.EXACTLY)),
    394                 eq(MeasureSpec.makeMeasureSpec(84, MeasureSpec.UNSPECIFIED)));
    395     }
    396 
    397     @UiThreadTest
    398     @Test
    399     public void testMeasureSpecsWithMargins() {
    400         MyView child = spy(new MyView(mActivity));
    401         mScrollViewCustomEmpty.addView(child);
    402 
    403         mScrollViewCustomEmpty.measureChildWithMargins(child,
    404                 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY), 20,
    405                 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 15);
    406         verify(child).onMeasure(
    407                 eq(MeasureSpec.makeMeasureSpec(130, MeasureSpec.EXACTLY)),
    408                 eq(MeasureSpec.makeMeasureSpec(85, MeasureSpec.UNSPECIFIED)));
    409     }
    410 
    411     @UiThreadTest
    412     @Test
    413     public void testMeasureSpecsWithMarginsAndPadding() {
    414         MyView child = spy(new MyView(mActivity));
    415         mScrollViewCustomEmpty.setPadding(3, 5, 7, 11);
    416         mScrollViewCustomEmpty.addView(child);
    417 
    418         mScrollViewCustomEmpty.measureChildWithMargins(child,
    419                 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY), 20,
    420                 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 15);
    421         verify(child).onMeasure(
    422                 eq(MeasureSpec.makeMeasureSpec(120, MeasureSpec.EXACTLY)),
    423                 eq(MeasureSpec.makeMeasureSpec(69, MeasureSpec.UNSPECIFIED)));
    424     }
    425 
    426     @UiThreadTest
    427     @Test
    428     public void testMeasureSpecsWithMarginsAndNoHintWidth() {
    429         MyView child = spy(new MyView(mActivity));
    430         mScrollViewCustomEmpty.addView(child);
    431 
    432         mScrollViewCustomEmpty.measureChildWithMargins(child,
    433                 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY), 20,
    434                 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 15);
    435         verify(child).onMeasure(
    436                 eq(MeasureSpec.makeMeasureSpec(130, MeasureSpec.EXACTLY)),
    437                 eq(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)));
    438     }
    439 
    440     @UiThreadTest
    441     @Test
    442     public void testFillViewport() {
    443         mScrollViewRegular.setFillViewport(true);
    444 
    445         MyView child = new MyView(mActivity);
    446         child.setLayoutParams(new ViewGroup.LayoutParams(100, 100));
    447 
    448         mScrollViewRegular.addView(child);
    449         mScrollViewRegular.measure(
    450                 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY),
    451                 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY));
    452 
    453         assertEquals(150, child.getMeasuredHeight());
    454         assertEquals(100, child.getMeasuredWidth());
    455 
    456         mScrollViewRegular.layout(0, 0, 100, 150);
    457         assertEquals(0, child.getTop());
    458     }
    459 
    460     @UiThreadTest
    461     @Test
    462     public void testFillViewportWithScrollViewPadding() {
    463         mScrollViewRegular.setFillViewport(true);
    464         mScrollViewRegular.setPadding(3, 10, 5, 7);
    465 
    466         MyView child = new MyView(mActivity);
    467         child.setLayoutParams(new ViewGroup.LayoutParams(10,10));
    468         child.setDesiredHeight(30);
    469 
    470         mScrollViewRegular.addView(child);
    471         mScrollViewRegular.measure(
    472                 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY),
    473                 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY));
    474 
    475         assertEquals(133, child.getMeasuredHeight());
    476         assertEquals(10, child.getMeasuredWidth());
    477 
    478         mScrollViewRegular.layout(0, 0, 100, 150);
    479         assertEquals(10, child.getTop());
    480     }
    481 
    482     @UiThreadTest
    483     @Test
    484     public void testFillViewportWithChildMargins() {
    485         mScrollViewRegular.setFillViewport(true);
    486 
    487         MyView child = new MyView(mActivity);
    488         FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(10, 10);
    489         lp.leftMargin = 3;
    490         lp.topMargin = 10;
    491         lp.rightMargin = 5;
    492         lp.bottomMargin = 7;
    493         child.setDesiredHeight(30);
    494         child.setLayoutParams(lp);
    495 
    496         mScrollViewRegular.addView(child);
    497         mScrollViewRegular.measure(MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY),
    498                 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY));
    499 
    500         assertEquals(133, child.getMeasuredHeight());
    501         assertEquals(10, child.getMeasuredWidth());
    502 
    503         mScrollViewRegular.layout(0, 0, 100, 150);
    504         assertEquals(10, child.getTop());
    505     }
    506 
    507     @UiThreadTest
    508     @Test
    509     public void testFillViewportWithScrollViewPaddingAlreadyFills() {
    510         mScrollViewRegular.setFillViewport(true);
    511         mScrollViewRegular.setPadding(3, 10, 5, 7);
    512 
    513         MyView child = new MyView(mActivity);
    514         child.setDesiredHeight(175);
    515 
    516         mScrollViewRegular.addView(child);
    517         mScrollViewRegular.measure(MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY),
    518                 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY));
    519 
    520 
    521         assertEquals(92, child.getMeasuredWidth());
    522         assertEquals(175, child.getMeasuredHeight());
    523 
    524         mScrollViewRegular.layout(0, 0, 100, 150);
    525         assertEquals(10, child.getTop());
    526     }
    527 
    528     @UiThreadTest
    529     @Test
    530     public void testFillViewportWithChildMarginsAlreadyFills() {
    531         mScrollViewRegular.setFillViewport(true);
    532         MyView child = new MyView(mActivity);
    533         FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
    534                 ViewGroup.LayoutParams.MATCH_PARENT,
    535                 ViewGroup.LayoutParams.WRAP_CONTENT);
    536 
    537         lp.leftMargin = 3;
    538         lp.topMargin = 10;
    539         lp.rightMargin = 5;
    540         lp.bottomMargin = 7;
    541         child.setLayoutParams(lp);
    542         child.setDesiredHeight(175);
    543 
    544         mScrollViewRegular.addView(child);
    545         mScrollViewRegular.measure(MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY),
    546                 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY));
    547 
    548         assertEquals(92, child.getMeasuredWidth());
    549         assertEquals(175, child.getMeasuredHeight());
    550 
    551         mScrollViewRegular.layout(0, 0, 100, 150);
    552         assertEquals(10, child.getTop());
    553     }
    554 
    555     @UiThreadTest
    556     @Test
    557     public void testPageScroll() {
    558         mScrollViewCustom.setSmoothScrollingEnabled(false);
    559         assertEquals(0, mScrollViewCustom.getScrollY());
    560 
    561         assertTrue(mScrollViewCustom.pageScroll(View.FOCUS_DOWN));
    562         assertEquals(mPageHeight, mScrollViewCustom.getScrollY(), TOLERANCE);
    563 
    564         assertTrue(mScrollViewCustom.pageScroll(View.FOCUS_DOWN));
    565         assertEquals(mPageHeight * 2, mScrollViewCustom.getScrollY(), TOLERANCE);
    566 
    567         mScrollViewCustom.scrollTo(mPageWidth, mScrollBottom);
    568         assertFalse(mScrollViewCustom.pageScroll(View.FOCUS_DOWN));
    569         assertEquals(mScrollBottom, mScrollViewCustom.getScrollY(), TOLERANCE);
    570 
    571         assertTrue(mScrollViewCustom.pageScroll(View.FOCUS_UP));
    572         assertEquals(mScrollBottom - mPageHeight, mScrollViewCustom.getScrollY(), TOLERANCE);
    573 
    574         assertTrue(mScrollViewCustom.pageScroll(View.FOCUS_UP));
    575         assertEquals(mScrollBottom -mPageHeight * 2, mScrollViewCustom.getScrollY(), TOLERANCE);
    576 
    577         mScrollViewCustom.scrollTo(mPageWidth, 0);
    578         assertFalse(mScrollViewCustom.pageScroll(View.FOCUS_UP));
    579         assertEquals(0, mScrollViewCustom.getScrollY());
    580     }
    581 
    582     @UiThreadTest
    583     @Test
    584     public void testFullScroll() {
    585         mScrollViewCustom.setSmoothScrollingEnabled(false);
    586         assertEquals(0, mScrollViewCustom.getScrollY());
    587 
    588         assertTrue(mScrollViewCustom.fullScroll(View.FOCUS_DOWN));
    589         assertEquals(mScrollBottom, mScrollViewCustom.getScrollY());
    590 
    591         assertFalse(mScrollViewCustom.fullScroll(View.FOCUS_DOWN));
    592         assertEquals(mScrollBottom, mScrollViewCustom.getScrollY());
    593 
    594         assertTrue(mScrollViewCustom.fullScroll(View.FOCUS_UP));
    595         assertEquals(0, mScrollViewCustom.getScrollY());
    596 
    597         assertFalse(mScrollViewCustom.fullScroll(View.FOCUS_UP));
    598         assertEquals(0, mScrollViewCustom.getScrollY());
    599     }
    600 
    601     @UiThreadTest
    602     @Test
    603     public void testArrowScroll() {
    604         mScrollViewCustom.setSmoothScrollingEnabled(false);
    605         assertEquals(0, mScrollViewCustom.getScrollY());
    606 
    607         int y = mScrollViewCustom.getScrollY();
    608         while (mScrollBottom != y) {
    609             assertTrue(mScrollViewCustom.arrowScroll(View.FOCUS_DOWN));
    610             assertTrue(y <= mScrollViewCustom.getScrollY());
    611             y = mScrollViewCustom.getScrollY();
    612         }
    613 
    614         assertFalse(mScrollViewCustom.arrowScroll(View.FOCUS_DOWN));
    615         assertEquals(mScrollBottom, mScrollViewCustom.getScrollY());
    616 
    617         y = mScrollViewCustom.getScrollY();
    618         while (0 != y) {
    619             assertTrue(mScrollViewCustom.arrowScroll(View.FOCUS_UP));
    620             assertTrue(y >= mScrollViewCustom.getScrollY());
    621             y = mScrollViewCustom.getScrollY();
    622         }
    623 
    624         assertFalse(mScrollViewCustom.arrowScroll(View.FOCUS_UP));
    625         assertEquals(0, mScrollViewCustom.getScrollY());
    626     }
    627 
    628     @Test
    629     public void testSmoothScrollBy() throws Throwable {
    630         assertEquals(0, mScrollViewCustom.getScrollX());
    631         assertEquals(0, mScrollViewCustom.getScrollY());
    632 
    633         mActivityRule.runOnUiThread(
    634                 () -> mScrollViewCustom.smoothScrollBy(mScrollRight, mScrollBottom));
    635         // smoothScrollBy doesn't scroll in X
    636         pollingCheckSmoothScrolling(0, 0, 0, mScrollBottom);
    637         assertEquals(0, mScrollViewCustom.getScrollX());
    638         assertEquals(mScrollBottom, mScrollViewCustom.getScrollY());
    639 
    640         mActivityRule.runOnUiThread(
    641                 () -> mScrollViewCustom.smoothScrollBy(-mScrollRight, -mScrollBottom));
    642         pollingCheckSmoothScrolling(mScrollRight, 0, mScrollBottom, 0);
    643         assertEquals(0, mScrollViewCustom.getScrollX());
    644         assertEquals(0, mScrollViewCustom.getScrollY());
    645     }
    646 
    647     @Test
    648     public void testSmoothScrollTo() throws Throwable {
    649         assertEquals(0, mScrollViewCustom.getScrollX());
    650         assertEquals(0, mScrollViewCustom.getScrollY());
    651 
    652         mActivityRule.runOnUiThread(
    653                 () -> mScrollViewCustom.smoothScrollTo(mScrollRight, mScrollBottom));
    654         // smoothScrollTo doesn't scroll in X
    655         pollingCheckSmoothScrolling(0, 0, 0, mScrollBottom);
    656         assertEquals(0, mScrollViewCustom.getScrollX());
    657         assertEquals(mScrollBottom, mScrollViewCustom.getScrollY());
    658 
    659         mActivityRule.runOnUiThread(
    660                 () -> mScrollViewCustom.smoothScrollTo(mPageWidth, mPageHeight));
    661         pollingCheckSmoothScrolling(0, 0, mScrollBottom, mPageHeight);
    662         assertEquals(0, mScrollViewCustom.getScrollX());
    663         assertEquals(mPageHeight, mScrollViewCustom.getScrollY());
    664     }
    665 
    666     @UiThreadTest
    667     @Test
    668     public void testComputeScrollDeltaToGetChildRectOnScreen() {
    669         mScrollViewCustom.setSmoothScrollingEnabled(false);
    670         int edge = mScrollViewCustom.getVerticalFadingEdgeLength();
    671 
    672         // Rect's height is smaller than scroll view
    673         Rect rect = new Rect(0, 0, 0, 0);
    674         assertEquals(0,
    675                 ((MyScrollView) mScrollViewCustom).computeScrollDeltaToGetChildRectOnScreen(rect));
    676 
    677         rect = new Rect(0, edge, 0, mPageHeight);
    678         assertEquals(0,
    679                 ((MyScrollView) mScrollViewCustom).computeScrollDeltaToGetChildRectOnScreen(rect));
    680 
    681         mScrollViewCustom.scrollTo(0, 0);
    682         rect = new Rect(0, edge + 1, 0, mPageHeight);
    683         assertEquals(edge,
    684                 ((MyScrollView) mScrollViewCustom).computeScrollDeltaToGetChildRectOnScreen(rect));
    685     }
    686 
    687     @UiThreadTest
    688     @Test
    689     public void testComputeVerticalScrollRange() {
    690         assertTrue(mScrollViewCustom.getChildCount() > 0);
    691         assertEquals(mItemHeight * ITEM_COUNT,
    692                 ((MyScrollView) mScrollViewCustom).computeVerticalScrollRange(), TOLERANCE);
    693 
    694         MyScrollView myScrollView = new MyScrollView(mActivity);
    695         assertEquals(0, myScrollView.getChildCount());
    696         assertEquals(0, myScrollView.computeVerticalScrollRange());
    697     }
    698 
    699     @UiThreadTest
    700     @Test
    701     public void testRequestChildFocus() {
    702         mScrollViewCustom.setSmoothScrollingEnabled(false);
    703 
    704         View firstChild = mScrollViewCustom.findViewById(R.id.first_child);
    705         View lastChild = mScrollViewCustom.findViewById(R.id.last_child);
    706         firstChild.requestFocus();
    707 
    708         int scrollY = mScrollViewCustom.getScrollY();
    709         mScrollViewCustom.requestChildFocus(lastChild, lastChild);
    710         // check scrolling to the child which wants focus
    711         assertTrue(mScrollViewCustom.getScrollY() > scrollY);
    712 
    713         scrollY = mScrollViewCustom.getScrollY();
    714         mScrollViewCustom.requestChildFocus(firstChild, firstChild);
    715         // check scrolling to the child which wants focus
    716         assertTrue(mScrollViewCustom.getScrollY() < scrollY);
    717     }
    718 
    719     @UiThreadTest
    720     @Test
    721     public void testRequestChildRectangleOnScreen() {
    722         mScrollViewCustom.setSmoothScrollingEnabled(false);
    723         mScrollViewCustom.setVerticalFadingEdgeEnabled(true);
    724         int edge = mScrollViewCustom.getVerticalFadingEdgeLength();
    725 
    726         View child = mScrollViewCustom.findViewById(R.id.first_child);
    727         int orgRectSize = (int)(10 * mActivity.getResources().getDisplayMetrics().density);
    728         final Rect originalRect = new Rect(0, 0, orgRectSize, orgRectSize);
    729         final Rect newRect = new Rect(mItemWidth - orgRectSize, mItemHeight - orgRectSize,
    730                 mItemWidth, mItemHeight);
    731 
    732         assertFalse(mScrollViewCustom.requestChildRectangleOnScreen(child, originalRect, true));
    733         assertEquals(0, mScrollViewCustom.getScrollX());
    734         assertEquals(0, mScrollViewCustom.getScrollY());
    735 
    736         assertTrue(mScrollViewCustom.requestChildRectangleOnScreen(child, newRect, true));
    737         assertEquals(0, mScrollViewCustom.getScrollX());
    738         assertEquals(edge, mScrollViewCustom.getScrollY());
    739     }
    740 
    741     @UiThreadTest
    742     @Test
    743     public void testRequestLayout() {
    744         mScrollViewCustom.requestLayout();
    745 
    746         assertTrue(mScrollViewCustom.isLayoutRequested());
    747     }
    748 
    749     @Test
    750     public void testFling() throws Throwable {
    751         mScrollViewCustom.setSmoothScrollingEnabled(true);
    752         assertEquals(0, mScrollViewCustom.getScrollY());
    753 
    754         // fling towards bottom
    755         mActivityRule.runOnUiThread(() -> mScrollViewCustom.fling(2000));
    756         pollingCheckFling(0, true);
    757 
    758         final int currentY = mScrollViewCustom.getScrollY();
    759         // fling towards top
    760         mActivityRule.runOnUiThread(() -> mScrollViewCustom.fling(-2000));
    761         pollingCheckFling(currentY, false);
    762     }
    763 
    764     @UiThreadTest
    765     @Test
    766     public void testScrollTo() {
    767         mScrollViewCustom.setSmoothScrollingEnabled(false);
    768 
    769         mScrollViewCustom.scrollTo(10, 10);
    770         assertEquals(10, mScrollViewCustom.getScrollY());
    771         assertEquals(10, mScrollViewCustom.getScrollX());
    772 
    773         mScrollViewCustom.scrollTo(mPageWidth, mPageHeight);
    774         assertEquals(mPageHeight, mScrollViewCustom.getScrollY());
    775         assertEquals(mPageWidth, mScrollViewCustom.getScrollX());
    776 
    777         mScrollViewCustom.scrollTo(mScrollRight, mScrollBottom);
    778         assertEquals(mScrollBottom, mScrollViewCustom.getScrollY());
    779         assertEquals(mScrollRight, mScrollViewCustom.getScrollX());
    780 
    781         // reach the top and left
    782         mScrollViewCustom.scrollTo(-10, -10);
    783         assertEquals(0, mScrollViewCustom.getScrollY());
    784         assertEquals(0, mScrollViewCustom.getScrollX());
    785     }
    786 
    787     @UiThreadTest
    788     @Test
    789     public void testGetVerticalFadingEdgeStrengths() {
    790         MyScrollView myScrollViewCustom = (MyScrollView) mScrollViewCustom;
    791 
    792         assertTrue(myScrollViewCustom.getChildCount() > 0);
    793         assertTrue(myScrollViewCustom.getTopFadingEdgeStrength() <= 1.0f);
    794         assertTrue(myScrollViewCustom.getTopFadingEdgeStrength() >= 0.0f);
    795         assertTrue(myScrollViewCustom.getBottomFadingEdgeStrength() <= 1.0f);
    796         assertTrue(myScrollViewCustom.getBottomFadingEdgeStrength() >= 0.0f);
    797 
    798         MyScrollView myScrollView = new MyScrollView(mActivity);
    799         assertEquals(0, myScrollView.getChildCount());
    800         assertTrue(myScrollView.getTopFadingEdgeStrength() <= 1.0f);
    801         assertTrue(myScrollView.getTopFadingEdgeStrength() >= 0.0f);
    802         assertTrue(myScrollView.getBottomFadingEdgeStrength() <= 1.0f);
    803         assertTrue(myScrollView.getBottomFadingEdgeStrength() >= 0.0f);
    804     }
    805 
    806     @Test
    807     public void testScrollDescendant() throws Throwable {
    808         assertEquals(0, mScrollViewCustom.getScrollX());
    809         assertEquals(0, mScrollViewCustom.getScrollY());
    810 
    811         View lastChild = mScrollViewCustom.findViewById(R.id.last_child);
    812         int lastChildTop = (ITEM_COUNT - 1) * mItemHeight;
    813 
    814         mActivityRule.runOnUiThread(() -> mScrollViewCustom.scrollToDescendant(lastChild));
    815         // smoothScrollBy doesn't scroll in X
    816         pollingCheckSmoothScrolling(0, 0, 0, lastChildTop);
    817 
    818         assertEquals(0, mScrollViewCustom.getScrollX());
    819         assertEquals(lastChildTop, mScrollViewCustom.getScrollY());
    820     }
    821 
    822     @UiThreadTest
    823     @Test
    824     public void testEdgeEffectColors() {
    825         int defaultColor = new EdgeEffect(mScrollViewRegular.getContext()).getColor();
    826         assertEquals(mScrollViewRegular.getTopEdgeEffectColor(), defaultColor);
    827         assertEquals(mScrollViewRegular.getBottomEdgeEffectColor(), defaultColor);
    828 
    829         mScrollViewRegular.setEdgeEffectColor(Color.BLUE);
    830         assertEquals(mScrollViewRegular.getTopEdgeEffectColor(), Color.BLUE);
    831         assertEquals(mScrollViewRegular.getBottomEdgeEffectColor(), Color.BLUE);
    832 
    833         mScrollViewRegular.setTopEdgeEffectColor(Color.RED);
    834         assertEquals(mScrollViewRegular.getTopEdgeEffectColor(), Color.RED);
    835         assertEquals(mScrollViewRegular.getBottomEdgeEffectColor(), Color.BLUE);
    836 
    837         mScrollViewRegular.setBottomEdgeEffectColor(Color.GREEN);
    838         assertEquals(mScrollViewRegular.getTopEdgeEffectColor(), Color.RED);
    839         assertEquals(mScrollViewRegular.getBottomEdgeEffectColor(), Color.GREEN);
    840     }
    841 
    842     private boolean isInRange(int current, int from, int to) {
    843         if (from < to) {
    844             return current >= from && current <= to;
    845         }
    846         return current <= from && current >= to;
    847     }
    848 
    849     private void pollingCheckSmoothScrolling(final int fromX, final int toX,
    850             final int fromY, final int toY) {
    851 
    852         if (fromX == toX && fromY == toY) {
    853             return;
    854         }
    855 
    856         if (fromY != toY) {
    857             PollingCheck.waitFor(() -> isInRange(mScrollViewCustom.getScrollY(), fromY, toY));
    858         }
    859 
    860         if (fromX != toX) {
    861             PollingCheck.waitFor(() -> isInRange(mScrollViewCustom.getScrollX(), fromX, toX));
    862         }
    863 
    864         PollingCheck.waitFor(
    865                 () -> toX == mScrollViewCustom.getScrollX()
    866                         && toY == mScrollViewCustom.getScrollY());
    867     }
    868 
    869     private void pollingCheckFling(final int startPosition, final boolean movingDown) {
    870         PollingCheck.waitFor(() -> {
    871             if (movingDown) {
    872                 return mScrollViewCustom.getScrollY() > startPosition;
    873             }
    874             return mScrollViewCustom.getScrollY() < startPosition;
    875         });
    876 
    877         final int[] previousScrollY = new int[] { mScrollViewCustom.getScrollY() };
    878         PollingCheck.waitFor(() -> {
    879             if (mScrollViewCustom.getScrollY() == previousScrollY[0]) {
    880                 return true;
    881             } else {
    882                 previousScrollY[0] = mScrollViewCustom.getScrollY();
    883                 return false;
    884             }
    885         });
    886     }
    887 
    888     public static class MyView extends View {
    889         // measure in this height if set
    890         private Integer mDesiredHeight;
    891         public MyView(Context context) {
    892             super(context);
    893         }
    894 
    895         public void setDesiredHeight(Integer desiredHeight) {
    896             mDesiredHeight = desiredHeight;
    897         }
    898 
    899         @Override
    900         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    901             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    902             if (mDesiredHeight != null) {
    903                 int mode = MeasureSpec.getMode(heightMeasureSpec);
    904                 int size = MeasureSpec.getSize(heightMeasureSpec);
    905                 int newHeight = size;
    906                 if (mode == MeasureSpec.AT_MOST) {
    907                     newHeight = Math.max(size, mDesiredHeight);
    908                 } else if (mode == MeasureSpec.UNSPECIFIED) {
    909                     newHeight = mDesiredHeight;
    910                 }
    911                 setMeasuredDimension(getMeasuredWidth(), newHeight);
    912             }
    913         }
    914     }
    915 
    916     public static class MyScrollView extends ScrollView {
    917         public MyScrollView(Context context) {
    918             super(context);
    919         }
    920 
    921         public MyScrollView(Context context, AttributeSet attrs) {
    922             super(context, attrs);
    923         }
    924 
    925         public MyScrollView(Context context, AttributeSet attrs, int defStyle) {
    926             super(context, attrs, defStyle);
    927         }
    928 
    929         @Override
    930         protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
    931             return super.computeScrollDeltaToGetChildRectOnScreen(rect);
    932         }
    933 
    934         @Override
    935         protected int computeVerticalScrollRange() {
    936             return super.computeVerticalScrollRange();
    937         }
    938 
    939         @Override
    940         protected float getBottomFadingEdgeStrength() {
    941             return super.getBottomFadingEdgeStrength();
    942         }
    943 
    944         @Override
    945         protected float getTopFadingEdgeStrength() {
    946             return super.getTopFadingEdgeStrength();
    947         }
    948 
    949         @Override
    950         protected void measureChild(View c, int pWidthMeasureSpec, int pHeightMeasureSpec) {
    951             super.measureChild(c, pWidthMeasureSpec, pHeightMeasureSpec);
    952         }
    953 
    954         @Override
    955         protected void measureChildWithMargins(View child, int parentWidthMeasureSpec,
    956                 int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
    957             super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
    958                     parentHeightMeasureSpec, heightUsed);
    959         }
    960     }
    961 }
    962