Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2018 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.view.cts;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertFalse;
     21 import static org.junit.Assert.assertNotEquals;
     22 import static org.junit.Assert.assertNotNull;
     23 import static org.junit.Assert.assertNotSame;
     24 import static org.junit.Assert.assertNull;
     25 import static org.junit.Assert.assertSame;
     26 import static org.junit.Assert.assertTrue;
     27 import static org.junit.Assert.fail;
     28 import static org.mockito.Matchers.anyInt;
     29 import static org.mockito.Matchers.eq;
     30 import static org.mockito.Mockito.any;
     31 import static org.mockito.Mockito.doAnswer;
     32 import static org.mockito.Mockito.doReturn;
     33 import static org.mockito.Mockito.mock;
     34 import static org.mockito.Mockito.never;
     35 import static org.mockito.Mockito.reset;
     36 import static org.mockito.Mockito.spy;
     37 import static org.mockito.Mockito.times;
     38 import static org.mockito.Mockito.verify;
     39 import static org.mockito.Mockito.verifyZeroInteractions;
     40 import static org.mockito.Mockito.when;
     41 
     42 import android.app.Instrumentation;
     43 import android.content.ClipData;
     44 import android.content.Context;
     45 import android.content.res.ColorStateList;
     46 import android.content.res.Resources;
     47 import android.content.res.XmlResourceParser;
     48 import android.graphics.Bitmap;
     49 import android.graphics.BitmapFactory;
     50 import android.graphics.Canvas;
     51 import android.graphics.Color;
     52 import android.graphics.ColorFilter;
     53 import android.graphics.Matrix;
     54 import android.graphics.Point;
     55 import android.graphics.PorterDuff;
     56 import android.graphics.Rect;
     57 import android.graphics.drawable.BitmapDrawable;
     58 import android.graphics.drawable.ColorDrawable;
     59 import android.graphics.drawable.Drawable;
     60 import android.graphics.drawable.StateListDrawable;
     61 import android.hardware.display.DisplayManager;
     62 import android.os.Bundle;
     63 import android.os.Parcelable;
     64 import android.os.SystemClock;
     65 import android.os.Vibrator;
     66 import android.text.format.DateUtils;
     67 import android.util.AttributeSet;
     68 import android.util.Log;
     69 import android.util.Pair;
     70 import android.util.SparseArray;
     71 import android.util.Xml;
     72 import android.view.ActionMode;
     73 import android.view.ContextMenu;
     74 import android.view.Display;
     75 import android.view.HapticFeedbackConstants;
     76 import android.view.InputDevice;
     77 import android.view.KeyEvent;
     78 import android.view.LayoutInflater;
     79 import android.view.Menu;
     80 import android.view.MenuInflater;
     81 import android.view.MotionEvent;
     82 import android.view.PointerIcon;
     83 import android.view.SoundEffectConstants;
     84 import android.view.TouchDelegate;
     85 import android.view.View;
     86 import android.view.View.BaseSavedState;
     87 import android.view.View.OnLongClickListener;
     88 import android.view.View.OnUnhandledKeyEventListener;
     89 import android.view.ViewConfiguration;
     90 import android.view.ViewGroup;
     91 import android.view.ViewParent;
     92 import android.view.ViewTreeObserver;
     93 import android.view.accessibility.AccessibilityEvent;
     94 import android.view.animation.AlphaAnimation;
     95 import android.view.animation.Animation;
     96 import android.view.cts.util.EventUtils;
     97 import android.view.cts.util.ScrollBarUtils;
     98 import android.view.inputmethod.EditorInfo;
     99 import android.view.inputmethod.InputConnection;
    100 import android.view.inputmethod.InputMethodManager;
    101 import android.widget.Button;
    102 import android.widget.EditText;
    103 import android.widget.LinearLayout;
    104 import android.widget.TextView;
    105 
    106 import androidx.test.InstrumentationRegistry;
    107 import androidx.test.annotation.UiThreadTest;
    108 import androidx.test.ext.junit.runners.AndroidJUnit4;
    109 import androidx.test.filters.MediumTest;
    110 import androidx.test.rule.ActivityTestRule;
    111 
    112 import com.android.compatibility.common.util.CtsMouseUtil;
    113 import com.android.compatibility.common.util.CtsTouchUtils;
    114 import com.android.compatibility.common.util.PollingCheck;
    115 
    116 import org.junit.Before;
    117 import org.junit.Rule;
    118 import org.junit.Test;
    119 import org.junit.runner.RunWith;
    120 import org.mockito.ArgumentCaptor;
    121 
    122 import java.lang.reflect.Constructor;
    123 import java.util.ArrayList;
    124 import java.util.Arrays;
    125 import java.util.Collections;
    126 import java.util.HashSet;
    127 import java.util.Set;
    128 import java.util.concurrent.BlockingQueue;
    129 import java.util.concurrent.CountDownLatch;
    130 import java.util.concurrent.LinkedBlockingQueue;
    131 import java.util.concurrent.TimeUnit;
    132 import java.util.concurrent.atomic.AtomicBoolean;
    133 
    134 /**
    135  * Test {@link View}.
    136  */
    137 @MediumTest
    138 @RunWith(AndroidJUnit4.class)
    139 public class ViewTest {
    140     /** timeout delta when wait in case the system is sluggish */
    141     private static final long TIMEOUT_DELTA = 10000;
    142 
    143     private static final String LOG_TAG = "ViewTest";
    144 
    145     private Instrumentation mInstrumentation;
    146     private ViewTestCtsActivity mActivity;
    147     private Resources mResources;
    148     private MockViewParent mMockParent;
    149     private Context mContext;
    150 
    151     @Rule
    152     public ActivityTestRule<ViewTestCtsActivity> mActivityRule =
    153             new ActivityTestRule<>(ViewTestCtsActivity.class);
    154 
    155     @Rule
    156     public ActivityTestRule<CtsActivity> mCtsActivityRule =
    157             new ActivityTestRule<>(CtsActivity.class, false, false);
    158 
    159     @Before
    160     public void setup() {
    161         mInstrumentation = InstrumentationRegistry.getInstrumentation();
    162         mContext = mInstrumentation.getTargetContext();
    163         mActivity = mActivityRule.getActivity();
    164         PollingCheck.waitFor(mActivity::hasWindowFocus);
    165         mResources = mActivity.getResources();
    166         mMockParent = new MockViewParent(mActivity);
    167         PollingCheck.waitFor(5 * DateUtils.SECOND_IN_MILLIS, mActivity::hasWindowFocus);
    168         assertTrue(mActivity.hasWindowFocus());
    169     }
    170 
    171     @Test
    172     public void testConstructor() {
    173         new View(mActivity);
    174 
    175         final XmlResourceParser parser = mResources.getLayout(R.layout.view_layout);
    176         final AttributeSet attrs = Xml.asAttributeSet(parser);
    177         new View(mActivity, attrs);
    178 
    179         new View(mActivity, null);
    180 
    181         new View(mActivity, attrs, 0);
    182 
    183         new View(mActivity, null, 1);
    184     }
    185 
    186     @Test(expected=NullPointerException.class)
    187     public void testConstructorNullContext1() {
    188         final XmlResourceParser parser = mResources.getLayout(R.layout.view_layout);
    189         final AttributeSet attrs = Xml.asAttributeSet(parser);
    190         new View(null, attrs);
    191     }
    192 
    193     @Test(expected=NullPointerException.class)
    194     public void testConstructorNullContext2() {
    195         new View(null, null, 1);
    196     }
    197 
    198     // Test that validates that Views can be constructed on a thread that
    199     // does not have a Looper. Necessary for async inflation
    200     private Pair<Class<?>, Throwable> sCtorException = null;
    201 
    202     @Test
    203     public void testConstructor2() throws Exception {
    204         final Object[] args = new Object[] { mActivity, null };
    205         final CountDownLatch latch = new CountDownLatch(1);
    206         sCtorException = null;
    207         new Thread() {
    208             @Override
    209             public void run() {
    210                 final Class<?>[] ctorSignature = new Class[] {
    211                         Context.class, AttributeSet.class};
    212                 for (Class<?> clazz : ASYNC_INFLATE_VIEWS) {
    213                     try {
    214                         Constructor<?> constructor = clazz.getConstructor(ctorSignature);
    215                         constructor.setAccessible(true);
    216                         constructor.newInstance(args);
    217                     } catch (Throwable t) {
    218                         sCtorException = new Pair<>(clazz, t);
    219                         break;
    220                     }
    221                 }
    222                 latch.countDown();
    223             }
    224         }.start();
    225         latch.await();
    226         if (sCtorException != null) {
    227             throw new AssertionError("Failed to inflate "
    228                     + sCtorException.first.getName(), sCtorException.second);
    229         }
    230     }
    231 
    232     @Test
    233     public void testGetContext() {
    234         View view = new View(mActivity);
    235         assertSame(mActivity, view.getContext());
    236     }
    237 
    238     @Test
    239     public void testGetResources() {
    240         View view = new View(mActivity);
    241         assertSame(mResources, view.getResources());
    242     }
    243 
    244     @Test
    245     public void testGetAnimation() {
    246         Animation animation = new AlphaAnimation(0.0f, 1.0f);
    247         View view = new View(mActivity);
    248         assertNull(view.getAnimation());
    249 
    250         view.setAnimation(animation);
    251         assertSame(animation, view.getAnimation());
    252 
    253         view.clearAnimation();
    254         assertNull(view.getAnimation());
    255     }
    256 
    257     @Test
    258     public void testSetAnimation() {
    259         Animation animation = new AlphaAnimation(0.0f, 1.0f);
    260         View view = new View(mActivity);
    261         assertNull(view.getAnimation());
    262 
    263         animation.initialize(100, 100, 100, 100);
    264         assertTrue(animation.isInitialized());
    265         view.setAnimation(animation);
    266         assertSame(animation, view.getAnimation());
    267         assertFalse(animation.isInitialized());
    268 
    269         view.setAnimation(null);
    270         assertNull(view.getAnimation());
    271     }
    272 
    273     @Test
    274     public void testClearAnimation() {
    275         Animation animation = new AlphaAnimation(0.0f, 1.0f);
    276         View view = new View(mActivity);
    277 
    278         assertNull(view.getAnimation());
    279         view.clearAnimation();
    280         assertNull(view.getAnimation());
    281 
    282         view.setAnimation(animation);
    283         assertNotNull(view.getAnimation());
    284         view.clearAnimation();
    285         assertNull(view.getAnimation());
    286     }
    287 
    288     @Test(expected=NullPointerException.class)
    289     public void testStartAnimationNull() {
    290         View view = new View(mActivity);
    291         view.startAnimation(null);
    292     }
    293 
    294     @Test
    295     public void testStartAnimation() {
    296         Animation animation = new AlphaAnimation(0.0f, 1.0f);
    297         View view = new View(mActivity);
    298 
    299         animation.setStartTime(1L);
    300         assertEquals(1L, animation.getStartTime());
    301         view.startAnimation(animation);
    302         assertEquals(Animation.START_ON_FIRST_FRAME, animation.getStartTime());
    303     }
    304 
    305     @Test
    306     public void testOnAnimation() throws Throwable {
    307         final Animation animation = new AlphaAnimation(0.0f, 1.0f);
    308         long duration = 2000L;
    309         animation.setDuration(duration);
    310         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
    311 
    312         // check whether it has started
    313         mActivityRule.runOnUiThread(() -> view.startAnimation(animation));
    314         mInstrumentation.waitForIdleSync();
    315 
    316         PollingCheck.waitFor(view::hasCalledOnAnimationStart);
    317 
    318         // check whether it has ended after duration, and alpha changed during this time.
    319         PollingCheck.waitFor(duration + TIMEOUT_DELTA,
    320                 () -> view.hasCalledOnSetAlpha() && view.hasCalledOnAnimationEnd());
    321     }
    322 
    323     @Test
    324     public void testGetParent() {
    325         MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
    326         ViewGroup parent = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
    327         assertSame(parent, view.getParent());
    328     }
    329 
    330     @Test
    331     public void testAccessScrollIndicators() {
    332         View view = mActivity.findViewById(R.id.viewlayout_root);
    333 
    334         assertEquals(View.SCROLL_INDICATOR_LEFT | View.SCROLL_INDICATOR_RIGHT,
    335                 view.getScrollIndicators());
    336     }
    337 
    338     @Test
    339     public void testSetScrollIndicators() {
    340         View view = new View(mActivity);
    341 
    342         view.setScrollIndicators(0);
    343         assertEquals(0, view.getScrollIndicators());
    344 
    345         view.setScrollIndicators(View.SCROLL_INDICATOR_LEFT | View.SCROLL_INDICATOR_RIGHT);
    346         assertEquals(View.SCROLL_INDICATOR_LEFT | View.SCROLL_INDICATOR_RIGHT,
    347                 view.getScrollIndicators());
    348 
    349         view.setScrollIndicators(View.SCROLL_INDICATOR_TOP, View.SCROLL_INDICATOR_TOP);
    350         assertEquals(View.SCROLL_INDICATOR_LEFT | View.SCROLL_INDICATOR_RIGHT
    351                         | View.SCROLL_INDICATOR_TOP, view.getScrollIndicators());
    352 
    353         view.setScrollIndicators(0, view.getScrollIndicators());
    354         assertEquals(0, view.getScrollIndicators());
    355     }
    356 
    357     @Test
    358     public void testFindViewById() {
    359         // verify view can find self
    360         View parent = mActivity.findViewById(R.id.viewlayout_root);
    361         assertSame(parent, parent.findViewById(R.id.viewlayout_root));
    362 
    363         // find expected view type
    364         View view = parent.findViewById(R.id.mock_view);
    365         assertTrue(view instanceof MockView);
    366     }
    367 
    368     @Test
    369     public void testRequireViewById() {
    370         View parent = mActivity.findViewById(R.id.viewlayout_root);
    371 
    372         View requiredView = parent.requireViewById(R.id.mock_view);
    373         View foundView = parent.findViewById(R.id.mock_view);
    374         assertSame(foundView, requiredView);
    375         assertTrue(requiredView instanceof MockView);
    376     }
    377 
    378     @Test(expected = IllegalArgumentException.class)
    379     public void testRequireViewByIdNoId() {
    380         View parent = mActivity.findViewById(R.id.viewlayout_root);
    381         parent.requireViewById(View.NO_ID);
    382     }
    383 
    384 
    385     @Test(expected = IllegalArgumentException.class)
    386     public void testRequireViewByIdInvalid() {
    387         View parent = mActivity.findViewById(R.id.viewlayout_root);
    388         parent.requireViewById(0);
    389     }
    390 
    391     @Test(expected = IllegalArgumentException.class)
    392     public void testRequireViewByIdNotFound() {
    393         View parent = mActivity.findViewById(R.id.viewlayout_root);
    394         parent.requireViewById(R.id.view); // id not present in view_layout
    395     }
    396 
    397     @Test
    398     public void testAccessTouchDelegate() throws Throwable {
    399         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
    400         Rect rect = new Rect();
    401         final Button button = new Button(mActivity);
    402         final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
    403         final int btnHeight = view.getHeight()/3;
    404         mActivityRule.runOnUiThread(() -> mActivity.addContentView(button,
    405                 new LinearLayout.LayoutParams(WRAP_CONTENT, btnHeight)));
    406         mInstrumentation.waitForIdleSync();
    407         button.getHitRect(rect);
    408         TouchDelegate delegate = spy(new TouchDelegate(rect, button));
    409 
    410         assertNull(view.getTouchDelegate());
    411 
    412         view.setTouchDelegate(delegate);
    413         assertSame(delegate, view.getTouchDelegate());
    414         verify(delegate, never()).onTouchEvent(any());
    415         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, view);
    416         assertTrue(view.hasCalledOnTouchEvent());
    417         verify(delegate, times(1)).onTouchEvent(any());
    418         CtsMouseUtil.emulateHoverOnView(mInstrumentation, view, view.getWidth() / 2,
    419                 view.getHeight() / 2);
    420         assertTrue(view.hasCalledOnHoverEvent());
    421         verifyZeroInteractions(delegate);
    422 
    423         view.setTouchDelegate(null);
    424         assertNull(view.getTouchDelegate());
    425     }
    426 
    427     @Test
    428     public void onHoverEvent_verticalCanScroll_awakenScrollBarsCalled() {
    429         onHoverEvent_awakensScrollBars(true, true, true);
    430     }
    431 
    432     @Test
    433     public void onHoverEvent_verticalCantScroll_awakenScrollBarsNotCalled() {
    434         onHoverEvent_awakensScrollBars(true, false, false);
    435     }
    436 
    437     @Test
    438     public void onHoverEvent_horizontalCanScroll_awakenScrollBarsCalled() {
    439         onHoverEvent_awakensScrollBars(false, true, true);
    440     }
    441 
    442     @Test
    443     public void onHoverEvent_horizontalCantScroll_awakenScrollBarsNotCalled() {
    444         onHoverEvent_awakensScrollBars(false, false, false);
    445     }
    446 
    447     private void onHoverEvent_awakensScrollBars(boolean vertical, boolean canScroll,
    448             boolean awakenScrollBarsCalled) {
    449 
    450         // Arrange
    451 
    452         final ScrollTestView view = spy(new ScrollTestView(mContext));
    453         view.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
    454         view.setHorizontalScrollBarEnabled(true);
    455         view.setVerticalScrollBarEnabled(true);
    456         view.setScrollBarSize(10);
    457         view.layout(0, 0, 100, 100);
    458 
    459         when(view.computeVerticalScrollExtent()).thenReturn(100);
    460         when(view.computeVerticalScrollRange()).thenReturn(canScroll ? 101 : 100);
    461         when(view.computeHorizontalScrollExtent()).thenReturn(100);
    462         when(view.computeHorizontalScrollRange()).thenReturn(canScroll ? 101 : 100);
    463 
    464         int x = vertical ? 95 : 50;
    465         int y = vertical ? 50 : 95;
    466 
    467         MotionEvent event = EventUtils.generateMouseEvent(x, y, MotionEvent.ACTION_HOVER_ENTER, 0);
    468 
    469         // Act
    470 
    471         view.onHoverEvent(event);
    472         event.recycle();
    473 
    474         // Assert
    475 
    476         if (awakenScrollBarsCalled) {
    477             verify(view).awakenScrollBars();
    478         } else {
    479             verify(view, never()).awakenScrollBars();
    480         }
    481     }
    482 
    483     @Test
    484     public void testMouseEventCallsGetPointerIcon() {
    485         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
    486 
    487         final int[] xy = new int[2];
    488         view.getLocationOnScreen(xy);
    489         final int viewWidth = view.getWidth();
    490         final int viewHeight = view.getHeight();
    491         float x = xy[0] + viewWidth / 2.0f;
    492         float y = xy[1] + viewHeight / 2.0f;
    493 
    494         long eventTime = SystemClock.uptimeMillis();
    495 
    496         MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[1];
    497         pointerCoords[0] = new MotionEvent.PointerCoords();
    498         pointerCoords[0].x = x;
    499         pointerCoords[0].y = y;
    500 
    501         final int[] pointerIds = new int[1];
    502         pointerIds[0] = 0;
    503 
    504         MotionEvent event = MotionEvent.obtain(0, eventTime, MotionEvent.ACTION_HOVER_MOVE,
    505                 1, pointerIds, pointerCoords, 0, 0, 0, 0, 0, InputDevice.SOURCE_MOUSE, 0);
    506         mInstrumentation.sendPointerSync(event);
    507         mInstrumentation.waitForIdleSync();
    508 
    509         assertTrue(view.hasCalledOnResolvePointerIcon());
    510 
    511         final MockView view2 = (MockView) mActivity.findViewById(R.id.scroll_view);
    512         assertFalse(view2.hasCalledOnResolvePointerIcon());
    513     }
    514 
    515     @Test
    516     public void testAccessPointerIcon() {
    517         View view = mActivity.findViewById(R.id.pointer_icon_layout);
    518         MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0);
    519 
    520         // First view has pointerIcon="help"
    521         assertEquals(PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_HELP),
    522                      view.onResolvePointerIcon(event, 0));
    523 
    524         // Second view inherits pointerIcon="crosshair" from the parent
    525         event.setLocation(0, 21);
    526         assertEquals(PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_CROSSHAIR),
    527                      view.onResolvePointerIcon(event, 0));
    528 
    529         // Third view has custom pointer icon defined in a resource.
    530         event.setLocation(0, 41);
    531         assertNotNull(view.onResolvePointerIcon(event, 0));
    532 
    533         // Parent view has pointerIcon="crosshair"
    534         event.setLocation(0, 61);
    535         assertEquals(PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_CROSSHAIR),
    536                      view.onResolvePointerIcon(event, 0));
    537 
    538         // Outside of the parent view, no pointer icon defined.
    539         event.setLocation(0, 71);
    540         assertNull(view.onResolvePointerIcon(event, 0));
    541 
    542         view.setPointerIcon(PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_TEXT));
    543         assertEquals(PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_TEXT),
    544                      view.onResolvePointerIcon(event, 0));
    545         event.recycle();
    546     }
    547 
    548     @Test
    549     public void testPointerIconOverlap() throws Throwable {
    550         View parent = mActivity.findViewById(R.id.pointer_icon_overlap);
    551         View child1 = mActivity.findViewById(R.id.pointer_icon_overlap_child1);
    552         View child2 = mActivity.findViewById(R.id.pointer_icon_overlap_child2);
    553         View child3 = mActivity.findViewById(R.id.pointer_icon_overlap_child3);
    554 
    555         PointerIcon iconParent = PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_HAND);
    556         PointerIcon iconChild1 = PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_HELP);
    557         PointerIcon iconChild2 = PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_TEXT);
    558         PointerIcon iconChild3 = PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_GRAB);
    559 
    560         parent.setPointerIcon(iconParent);
    561         child1.setPointerIcon(iconChild1);
    562         child2.setPointerIcon(iconChild2);
    563         child3.setPointerIcon(iconChild3);
    564 
    565         MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0);
    566 
    567         assertEquals(iconChild3, parent.onResolvePointerIcon(event, 0));
    568 
    569         setVisibilityOnUiThread(child3, View.GONE);
    570         assertEquals(iconChild2, parent.onResolvePointerIcon(event, 0));
    571 
    572         child2.setPointerIcon(null);
    573         assertEquals(iconChild1, parent.onResolvePointerIcon(event, 0));
    574 
    575         setVisibilityOnUiThread(child1, View.GONE);
    576         assertEquals(iconParent, parent.onResolvePointerIcon(event, 0));
    577 
    578         event.recycle();
    579     }
    580 
    581     @Test
    582     public void onResolvePointerIcon_verticalCanScroll_pointerIsArrow() {
    583         onResolvePointerIcon_scrollabilityAffectsPointerIcon(true, true, true);
    584     }
    585 
    586     @Test
    587     public void onResolvePointerIcon_verticalCantScroll_pointerIsProperty() {
    588         onResolvePointerIcon_scrollabilityAffectsPointerIcon(true, false, false);
    589     }
    590 
    591     @Test
    592     public void onResolvePointerIcon_horizontalCanScroll_pointerIsArrow() {
    593         onResolvePointerIcon_scrollabilityAffectsPointerIcon(false, true, true);
    594     }
    595 
    596     @Test
    597     public void onResolvePointerIcon_horizontalCantScroll_pointerIsProperty() {
    598         onResolvePointerIcon_scrollabilityAffectsPointerIcon(false, false, false);
    599     }
    600 
    601     private void onResolvePointerIcon_scrollabilityAffectsPointerIcon(boolean vertical,
    602             boolean canScroll, boolean pointerIsSystemArrow) {
    603 
    604         // Arrange
    605 
    606         int range = canScroll ? 101 : 100;
    607         int thumbLength = ScrollBarUtils.getThumbLength(1, 10, 100, range);
    608 
    609         PointerIcon expectedPointerIcon = PointerIcon.getSystemIcon(mContext,
    610                 PointerIcon.TYPE_HAND);
    611 
    612         final ScrollTestView view = spy(new ScrollTestView(mContext));
    613         view.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
    614         view.setHorizontalScrollBarEnabled(true);
    615         view.setVerticalScrollBarEnabled(true);
    616         view.setScrollBarSize(10);
    617         view.setPointerIcon(expectedPointerIcon);
    618         view.layout(0, 0, 100, 100);
    619 
    620         when(view.computeVerticalScrollExtent()).thenReturn(100);
    621         when(view.computeVerticalScrollRange()).thenReturn(range);
    622         when(view.computeHorizontalScrollExtent()).thenReturn(100);
    623         when(view.computeHorizontalScrollRange()).thenReturn(range);
    624 
    625         int touchX = vertical ? 95 : thumbLength / 2;
    626         int touchY = vertical ? thumbLength / 2 : 95;
    627         MotionEvent event =
    628                 EventUtils.generateMouseEvent(touchX, touchY, MotionEvent.ACTION_HOVER_ENTER, 0);
    629 
    630         // Act
    631 
    632         PointerIcon actualResult = view.onResolvePointerIcon(event, 0);
    633         event.recycle();
    634 
    635         // Assert
    636 
    637         if (pointerIsSystemArrow) {
    638             assertEquals(PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW), actualResult);
    639         } else {
    640             assertEquals(expectedPointerIcon, actualResult);
    641         }
    642     }
    643 
    644     @Test
    645     public void testCreatePointerIcons() {
    646         assertSystemPointerIcon(PointerIcon.TYPE_NULL);
    647         assertSystemPointerIcon(PointerIcon.TYPE_DEFAULT);
    648         assertSystemPointerIcon(PointerIcon.TYPE_ARROW);
    649         assertSystemPointerIcon(PointerIcon.TYPE_CONTEXT_MENU);
    650         assertSystemPointerIcon(PointerIcon.TYPE_HAND);
    651         assertSystemPointerIcon(PointerIcon.TYPE_HELP);
    652         assertSystemPointerIcon(PointerIcon.TYPE_WAIT);
    653         assertSystemPointerIcon(PointerIcon.TYPE_CELL);
    654         assertSystemPointerIcon(PointerIcon.TYPE_CROSSHAIR);
    655         assertSystemPointerIcon(PointerIcon.TYPE_TEXT);
    656         assertSystemPointerIcon(PointerIcon.TYPE_VERTICAL_TEXT);
    657         assertSystemPointerIcon(PointerIcon.TYPE_ALIAS);
    658         assertSystemPointerIcon(PointerIcon.TYPE_COPY);
    659         assertSystemPointerIcon(PointerIcon.TYPE_NO_DROP);
    660         assertSystemPointerIcon(PointerIcon.TYPE_ALL_SCROLL);
    661         assertSystemPointerIcon(PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW);
    662         assertSystemPointerIcon(PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW);
    663         assertSystemPointerIcon(PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW);
    664         assertSystemPointerIcon(PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW);
    665         assertSystemPointerIcon(PointerIcon.TYPE_ZOOM_IN);
    666         assertSystemPointerIcon(PointerIcon.TYPE_ZOOM_OUT);
    667         assertSystemPointerIcon(PointerIcon.TYPE_GRAB);
    668 
    669         assertNotNull(PointerIcon.load(mResources, R.drawable.custom_pointer_icon));
    670 
    671         Bitmap bitmap = BitmapFactory.decodeResource(mResources, R.drawable.icon_blue);
    672         assertNotNull(PointerIcon.create(bitmap, 0, 0));
    673         assertNotNull(PointerIcon.create(bitmap, bitmap.getWidth() / 2, bitmap.getHeight() / 2));
    674 
    675         try {
    676             PointerIcon.create(bitmap, -1, 0);
    677             fail("Hotspot x can not be < 0");
    678         } catch (IllegalArgumentException ignore) {
    679         }
    680 
    681         try {
    682             PointerIcon.create(bitmap, 0, -1);
    683             fail("Hotspot y can not be < 0");
    684         } catch (IllegalArgumentException ignore) {
    685         }
    686 
    687         try {
    688             PointerIcon.create(bitmap, bitmap.getWidth(), 0);
    689             fail("Hotspot x cannot be >= width");
    690         } catch (IllegalArgumentException ignore) {
    691         }
    692 
    693         try {
    694             PointerIcon.create(bitmap, 0, bitmap.getHeight());
    695             fail("Hotspot x cannot be >= height");
    696         } catch (IllegalArgumentException e) {
    697         }
    698     }
    699 
    700     private void assertSystemPointerIcon(int style) {
    701         assertNotNull(PointerIcon.getSystemIcon(mActivity, style));
    702     }
    703 
    704     @UiThreadTest
    705     @Test
    706     public void testAccessTag() {
    707         ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
    708         MockView mockView = (MockView) mActivity.findViewById(R.id.mock_view);
    709         MockView scrollView = (MockView) mActivity.findViewById(R.id.scroll_view);
    710 
    711         ViewData viewData = new ViewData();
    712         viewData.childCount = 3;
    713         viewData.tag = "linearLayout";
    714         viewData.firstChild = mockView;
    715         viewGroup.setTag(viewData);
    716         viewGroup.setFocusable(true);
    717         assertSame(viewData, viewGroup.getTag());
    718 
    719         final String tag = "mock";
    720         assertNull(mockView.getTag());
    721         mockView.setTag(tag);
    722         assertEquals(tag, mockView.getTag());
    723 
    724         scrollView.setTag(viewGroup);
    725         assertSame(viewGroup, scrollView.getTag());
    726 
    727         assertSame(viewGroup, viewGroup.findViewWithTag(viewData));
    728         assertSame(mockView, viewGroup.findViewWithTag(tag));
    729         assertSame(scrollView, viewGroup.findViewWithTag(viewGroup));
    730 
    731         mockView.setTag(null);
    732         assertNull(mockView.getTag());
    733     }
    734 
    735     @Test
    736     public void testOnSizeChanged() throws Throwable {
    737         final ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
    738         final MockView mockView = new MockView(mActivity);
    739         assertEquals(-1, mockView.getOldWOnSizeChanged());
    740         assertEquals(-1, mockView.getOldHOnSizeChanged());
    741         mActivityRule.runOnUiThread(() -> viewGroup.addView(mockView));
    742         mInstrumentation.waitForIdleSync();
    743         assertTrue(mockView.hasCalledOnSizeChanged());
    744         assertEquals(0, mockView.getOldWOnSizeChanged());
    745         assertEquals(0, mockView.getOldHOnSizeChanged());
    746 
    747         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
    748         assertTrue(view.hasCalledOnSizeChanged());
    749         view.reset();
    750         assertEquals(-1, view.getOldWOnSizeChanged());
    751         assertEquals(-1, view.getOldHOnSizeChanged());
    752         int oldw = view.getWidth();
    753         int oldh = view.getHeight();
    754         final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(200, 100);
    755         mActivityRule.runOnUiThread(() -> view.setLayoutParams(layoutParams));
    756         mInstrumentation.waitForIdleSync();
    757         assertTrue(view.hasCalledOnSizeChanged());
    758         assertEquals(oldw, view.getOldWOnSizeChanged());
    759         assertEquals(oldh, view.getOldHOnSizeChanged());
    760     }
    761 
    762 
    763     @Test(expected=NullPointerException.class)
    764     public void testGetHitRectNull() {
    765         MockView view = new MockView(mActivity);
    766         view.getHitRect(null);
    767     }
    768 
    769     @Test
    770     public void testGetHitRect() {
    771         Rect outRect = new Rect();
    772         View mockView = mActivity.findViewById(R.id.mock_view);
    773         mockView.getHitRect(outRect);
    774         assertEquals(0, outRect.left);
    775         assertEquals(0, outRect.top);
    776         assertEquals(mockView.getWidth(), outRect.right);
    777         assertEquals(mockView.getHeight(), outRect.bottom);
    778     }
    779 
    780     @Test
    781     public void testForceLayout() {
    782         View view = new View(mActivity);
    783 
    784         assertFalse(view.isLayoutRequested());
    785         view.forceLayout();
    786         assertTrue(view.isLayoutRequested());
    787 
    788         view.forceLayout();
    789         assertTrue(view.isLayoutRequested());
    790     }
    791 
    792     @Test
    793     public void testIsLayoutRequested() {
    794         View view = new View(mActivity);
    795 
    796         assertFalse(view.isLayoutRequested());
    797         view.forceLayout();
    798         assertTrue(view.isLayoutRequested());
    799 
    800         view.layout(0, 0, 0, 0);
    801         assertFalse(view.isLayoutRequested());
    802     }
    803 
    804     @Test
    805     public void testRequestLayout() {
    806         MockView view = new MockView(mActivity);
    807         assertFalse(view.isLayoutRequested());
    808         assertNull(view.getParent());
    809 
    810         view.requestLayout();
    811         assertTrue(view.isLayoutRequested());
    812 
    813         view.setParent(mMockParent);
    814         assertTrue(mMockParent.hasRequestLayout());
    815 
    816         mMockParent.reset();
    817         view.requestLayout();
    818         assertTrue(view.isLayoutRequested());
    819         assertTrue(mMockParent.hasRequestLayout());
    820     }
    821 
    822     @Test
    823     public void testLayout() throws Throwable {
    824         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
    825         assertTrue(view.hasCalledOnLayout());
    826 
    827         view.reset();
    828         assertFalse(view.hasCalledOnLayout());
    829         mActivityRule.runOnUiThread(view::requestLayout);
    830         mInstrumentation.waitForIdleSync();
    831         assertTrue(view.hasCalledOnLayout());
    832     }
    833 
    834     @Test
    835     public void testGetBaseline() {
    836         View view = new View(mActivity);
    837 
    838         assertEquals(-1, view.getBaseline());
    839     }
    840 
    841     @Test
    842     public void testAccessBackground() {
    843         View view = new View(mActivity);
    844         Drawable d1 = mResources.getDrawable(R.drawable.scenery);
    845         Drawable d2 = mResources.getDrawable(R.drawable.pass);
    846 
    847         assertNull(view.getBackground());
    848 
    849         view.setBackgroundDrawable(d1);
    850         assertEquals(d1, view.getBackground());
    851 
    852         view.setBackgroundDrawable(d2);
    853         assertEquals(d2, view.getBackground());
    854 
    855         view.setBackgroundDrawable(null);
    856         assertNull(view.getBackground());
    857     }
    858 
    859     @Test
    860     public void testSetBackgroundResource() {
    861         View view = new View(mActivity);
    862 
    863         assertNull(view.getBackground());
    864 
    865         view.setBackgroundResource(R.drawable.pass);
    866         assertNotNull(view.getBackground());
    867 
    868         view.setBackgroundResource(0);
    869         assertNull(view.getBackground());
    870     }
    871 
    872     @Test
    873     public void testAccessDrawingCacheBackgroundColor() {
    874         View view = new View(mActivity);
    875 
    876         assertEquals(0, view.getDrawingCacheBackgroundColor());
    877 
    878         view.setDrawingCacheBackgroundColor(0xFF00FF00);
    879         assertEquals(0xFF00FF00, view.getDrawingCacheBackgroundColor());
    880 
    881         view.setDrawingCacheBackgroundColor(-1);
    882         assertEquals(-1, view.getDrawingCacheBackgroundColor());
    883     }
    884 
    885     @Test
    886     public void testSetBackgroundColor() {
    887         View view = new View(mActivity);
    888         ColorDrawable colorDrawable;
    889         assertNull(view.getBackground());
    890 
    891         view.setBackgroundColor(0xFFFF0000);
    892         colorDrawable = (ColorDrawable) view.getBackground();
    893         assertNotNull(colorDrawable);
    894         assertEquals(0xFF, colorDrawable.getAlpha());
    895 
    896         view.setBackgroundColor(0);
    897         colorDrawable = (ColorDrawable) view.getBackground();
    898         assertNotNull(colorDrawable);
    899         assertEquals(0, colorDrawable.getAlpha());
    900     }
    901 
    902     @Test
    903     public void testVerifyDrawable() {
    904         MockView view = new MockView(mActivity);
    905         Drawable d1 = mResources.getDrawable(R.drawable.scenery);
    906         Drawable d2 = mResources.getDrawable(R.drawable.pass);
    907 
    908         assertNull(view.getBackground());
    909         assertTrue(view.verifyDrawable(null));
    910         assertFalse(view.verifyDrawable(d1));
    911 
    912         view.setBackgroundDrawable(d1);
    913         assertTrue(view.verifyDrawable(d1));
    914         assertFalse(view.verifyDrawable(d2));
    915     }
    916 
    917     @Test
    918     public void testGetDrawingRect() {
    919         MockView view = new MockView(mActivity);
    920         Rect outRect = new Rect();
    921 
    922         view.getDrawingRect(outRect);
    923         assertEquals(0, outRect.left);
    924         assertEquals(0, outRect.top);
    925         assertEquals(0, outRect.right);
    926         assertEquals(0, outRect.bottom);
    927 
    928         view.scrollTo(10, 100);
    929         view.getDrawingRect(outRect);
    930         assertEquals(10, outRect.left);
    931         assertEquals(100, outRect.top);
    932         assertEquals(10, outRect.right);
    933         assertEquals(100, outRect.bottom);
    934 
    935         View mockView = mActivity.findViewById(R.id.mock_view);
    936         mockView.getDrawingRect(outRect);
    937         assertEquals(0, outRect.left);
    938         assertEquals(0, outRect.top);
    939         assertEquals(mockView.getWidth(), outRect.right);
    940         assertEquals(mockView.getHeight(), outRect.bottom);
    941     }
    942 
    943     @Test
    944     public void testGetFocusedRect() {
    945         MockView view = new MockView(mActivity);
    946         Rect outRect = new Rect();
    947 
    948         view.getFocusedRect(outRect);
    949         assertEquals(0, outRect.left);
    950         assertEquals(0, outRect.top);
    951         assertEquals(0, outRect.right);
    952         assertEquals(0, outRect.bottom);
    953 
    954         view.scrollTo(10, 100);
    955         view.getFocusedRect(outRect);
    956         assertEquals(10, outRect.left);
    957         assertEquals(100, outRect.top);
    958         assertEquals(10, outRect.right);
    959         assertEquals(100, outRect.bottom);
    960     }
    961 
    962     @Test
    963     public void testGetGlobalVisibleRectPoint() throws Throwable {
    964         final View view = mActivity.findViewById(R.id.mock_view);
    965         final ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
    966         Rect rect = new Rect();
    967         Point point = new Point();
    968 
    969         assertTrue(view.getGlobalVisibleRect(rect, point));
    970         Rect rcParent = new Rect();
    971         Point ptParent = new Point();
    972         viewGroup.getGlobalVisibleRect(rcParent, ptParent);
    973         assertEquals(rcParent.left, rect.left);
    974         assertEquals(rcParent.top, rect.top);
    975         assertEquals(rect.left + view.getWidth(), rect.right);
    976         assertEquals(rect.top + view.getHeight(), rect.bottom);
    977         assertEquals(ptParent.x, point.x);
    978         assertEquals(ptParent.y, point.y);
    979 
    980         // width is 0
    981         final LinearLayout.LayoutParams layoutParams1 = new LinearLayout.LayoutParams(0, 300);
    982         mActivityRule.runOnUiThread(() -> view.setLayoutParams(layoutParams1));
    983         mInstrumentation.waitForIdleSync();
    984         assertFalse(view.getGlobalVisibleRect(rect, point));
    985 
    986         // height is -10
    987         final LinearLayout.LayoutParams layoutParams2 = new LinearLayout.LayoutParams(200, -10);
    988         mActivityRule.runOnUiThread(() -> view.setLayoutParams(layoutParams2));
    989         mInstrumentation.waitForIdleSync();
    990         assertFalse(view.getGlobalVisibleRect(rect, point));
    991 
    992         Display display = mActivity.getWindowManager().getDefaultDisplay();
    993         int halfWidth = display.getWidth() / 2;
    994         int halfHeight = display.getHeight() /2;
    995 
    996         final LinearLayout.LayoutParams layoutParams3 =
    997                 new LinearLayout.LayoutParams(halfWidth, halfHeight);
    998         mActivityRule.runOnUiThread(() -> view.setLayoutParams(layoutParams3));
    999         mInstrumentation.waitForIdleSync();
   1000         assertTrue(view.getGlobalVisibleRect(rect, point));
   1001         assertEquals(rcParent.left, rect.left);
   1002         assertEquals(rcParent.top, rect.top);
   1003         assertEquals(rect.left + halfWidth, rect.right);
   1004         assertEquals(rect.top + halfHeight, rect.bottom);
   1005         assertEquals(ptParent.x, point.x);
   1006         assertEquals(ptParent.y, point.y);
   1007     }
   1008 
   1009     @Test
   1010     public void testGetGlobalVisibleRect() throws Throwable {
   1011         final View view = mActivity.findViewById(R.id.mock_view);
   1012         final ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
   1013         Rect rect = new Rect();
   1014 
   1015         assertTrue(view.getGlobalVisibleRect(rect));
   1016         Rect rcParent = new Rect();
   1017         viewGroup.getGlobalVisibleRect(rcParent);
   1018         assertEquals(rcParent.left, rect.left);
   1019         assertEquals(rcParent.top, rect.top);
   1020         assertEquals(rect.left + view.getWidth(), rect.right);
   1021         assertEquals(rect.top + view.getHeight(), rect.bottom);
   1022 
   1023         // width is 0
   1024         final LinearLayout.LayoutParams layoutParams1 = new LinearLayout.LayoutParams(0, 300);
   1025         mActivityRule.runOnUiThread(() -> view.setLayoutParams(layoutParams1));
   1026         mInstrumentation.waitForIdleSync();
   1027         assertFalse(view.getGlobalVisibleRect(rect));
   1028 
   1029         // height is -10
   1030         final LinearLayout.LayoutParams layoutParams2 = new LinearLayout.LayoutParams(200, -10);
   1031         mActivityRule.runOnUiThread(() -> view.setLayoutParams(layoutParams2));
   1032         mInstrumentation.waitForIdleSync();
   1033         assertFalse(view.getGlobalVisibleRect(rect));
   1034 
   1035         Display display = mActivity.getWindowManager().getDefaultDisplay();
   1036         int halfWidth = display.getWidth() / 2;
   1037         int halfHeight = display.getHeight() /2;
   1038 
   1039         final LinearLayout.LayoutParams layoutParams3 =
   1040                 new LinearLayout.LayoutParams(halfWidth, halfHeight);
   1041         mActivityRule.runOnUiThread(() -> view.setLayoutParams(layoutParams3));
   1042         mInstrumentation.waitForIdleSync();
   1043         assertTrue(view.getGlobalVisibleRect(rect));
   1044         assertEquals(rcParent.left, rect.left);
   1045         assertEquals(rcParent.top, rect.top);
   1046         assertEquals(rect.left + halfWidth, rect.right);
   1047         assertEquals(rect.top + halfHeight, rect.bottom);
   1048     }
   1049 
   1050     @Test
   1051     public void testComputeHorizontalScroll() throws Throwable {
   1052         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   1053 
   1054         assertEquals(0, view.computeHorizontalScrollOffset());
   1055         assertEquals(view.getWidth(), view.computeHorizontalScrollRange());
   1056         assertEquals(view.getWidth(), view.computeHorizontalScrollExtent());
   1057 
   1058         mActivityRule.runOnUiThread(() -> view.scrollTo(12, 0));
   1059         mInstrumentation.waitForIdleSync();
   1060         assertEquals(12, view.computeHorizontalScrollOffset());
   1061         assertEquals(view.getWidth(), view.computeHorizontalScrollRange());
   1062         assertEquals(view.getWidth(), view.computeHorizontalScrollExtent());
   1063 
   1064         mActivityRule.runOnUiThread(() -> view.scrollBy(12, 0));
   1065         mInstrumentation.waitForIdleSync();
   1066         assertEquals(24, view.computeHorizontalScrollOffset());
   1067         assertEquals(view.getWidth(), view.computeHorizontalScrollRange());
   1068         assertEquals(view.getWidth(), view.computeHorizontalScrollExtent());
   1069 
   1070         int newWidth = 200;
   1071         final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(newWidth, 100);
   1072         mActivityRule.runOnUiThread(() -> view.setLayoutParams(layoutParams));
   1073         mInstrumentation.waitForIdleSync();
   1074         assertEquals(24, view.computeHorizontalScrollOffset());
   1075         assertEquals(newWidth, view.getWidth());
   1076         assertEquals(view.getWidth(), view.computeHorizontalScrollRange());
   1077         assertEquals(view.getWidth(), view.computeHorizontalScrollExtent());
   1078     }
   1079 
   1080     @Test
   1081     public void testComputeVerticalScroll() throws Throwable {
   1082         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   1083 
   1084         assertEquals(0, view.computeVerticalScrollOffset());
   1085         assertEquals(view.getHeight(), view.computeVerticalScrollRange());
   1086         assertEquals(view.getHeight(), view.computeVerticalScrollExtent());
   1087 
   1088         final int scrollToY = 34;
   1089         mActivityRule.runOnUiThread(() -> view.scrollTo(0, scrollToY));
   1090         mInstrumentation.waitForIdleSync();
   1091         assertEquals(scrollToY, view.computeVerticalScrollOffset());
   1092         assertEquals(view.getHeight(), view.computeVerticalScrollRange());
   1093         assertEquals(view.getHeight(), view.computeVerticalScrollExtent());
   1094 
   1095         final int scrollByY = 200;
   1096         mActivityRule.runOnUiThread(() -> view.scrollBy(0, scrollByY));
   1097         mInstrumentation.waitForIdleSync();
   1098         assertEquals(scrollToY + scrollByY, view.computeVerticalScrollOffset());
   1099         assertEquals(view.getHeight(), view.computeVerticalScrollRange());
   1100         assertEquals(view.getHeight(), view.computeVerticalScrollExtent());
   1101 
   1102         int newHeight = 333;
   1103         final LinearLayout.LayoutParams layoutParams =
   1104                 new LinearLayout.LayoutParams(200, newHeight);
   1105         mActivityRule.runOnUiThread(() -> view.setLayoutParams(layoutParams));
   1106         mInstrumentation.waitForIdleSync();
   1107         assertEquals(scrollToY + scrollByY, view.computeVerticalScrollOffset());
   1108         assertEquals(newHeight, view.getHeight());
   1109         assertEquals(view.getHeight(), view.computeVerticalScrollRange());
   1110         assertEquals(view.getHeight(), view.computeVerticalScrollExtent());
   1111     }
   1112 
   1113     @Test
   1114     public void testGetFadingEdgeStrength() throws Throwable {
   1115         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   1116 
   1117         assertEquals(0f, view.getLeftFadingEdgeStrength(), 0.0f);
   1118         assertEquals(0f, view.getRightFadingEdgeStrength(), 0.0f);
   1119         assertEquals(0f, view.getTopFadingEdgeStrength(), 0.0f);
   1120         assertEquals(0f, view.getBottomFadingEdgeStrength(), 0.0f);
   1121 
   1122         mActivityRule.runOnUiThread(() -> view.scrollTo(10, 10));
   1123         mInstrumentation.waitForIdleSync();
   1124         assertEquals(1f, view.getLeftFadingEdgeStrength(), 0.0f);
   1125         assertEquals(0f, view.getRightFadingEdgeStrength(), 0.0f);
   1126         assertEquals(1f, view.getTopFadingEdgeStrength(), 0.0f);
   1127         assertEquals(0f, view.getBottomFadingEdgeStrength(), 0.0f);
   1128 
   1129         mActivityRule.runOnUiThread(() -> view.scrollTo(-10, -10));
   1130         mInstrumentation.waitForIdleSync();
   1131         assertEquals(0f, view.getLeftFadingEdgeStrength(), 0.0f);
   1132         assertEquals(1f, view.getRightFadingEdgeStrength(), 0.0f);
   1133         assertEquals(0f, view.getTopFadingEdgeStrength(), 0.0f);
   1134         assertEquals(1f, view.getBottomFadingEdgeStrength(), 0.0f);
   1135     }
   1136 
   1137     @Test
   1138     public void testGetLeftFadingEdgeStrength() {
   1139         MockView view = new MockView(mActivity);
   1140 
   1141         assertEquals(0.0f, view.getLeftFadingEdgeStrength(), 0.0f);
   1142 
   1143         view.scrollTo(1, 0);
   1144         assertEquals(1.0f, view.getLeftFadingEdgeStrength(), 0.0f);
   1145     }
   1146 
   1147     @Test
   1148     public void testGetRightFadingEdgeStrength() {
   1149         MockView view = new MockView(mActivity);
   1150 
   1151         assertEquals(0.0f, view.getRightFadingEdgeStrength(), 0.0f);
   1152 
   1153         view.scrollTo(-1, 0);
   1154         assertEquals(1.0f, view.getRightFadingEdgeStrength(), 0.0f);
   1155     }
   1156 
   1157     @Test
   1158     public void testGetBottomFadingEdgeStrength() {
   1159         MockView view = new MockView(mActivity);
   1160 
   1161         assertEquals(0.0f, view.getBottomFadingEdgeStrength(), 0.0f);
   1162 
   1163         view.scrollTo(0, -2);
   1164         assertEquals(1.0f, view.getBottomFadingEdgeStrength(), 0.0f);
   1165     }
   1166 
   1167     @Test
   1168     public void testGetTopFadingEdgeStrength() {
   1169         MockView view = new MockView(mActivity);
   1170 
   1171         assertEquals(0.0f, view.getTopFadingEdgeStrength(), 0.0f);
   1172 
   1173         view.scrollTo(0, 2);
   1174         assertEquals(1.0f, view.getTopFadingEdgeStrength(), 0.0f);
   1175     }
   1176 
   1177     @Test
   1178     public void testResolveSize() {
   1179         assertEquals(50, View.resolveSize(50, View.MeasureSpec.UNSPECIFIED));
   1180 
   1181         assertEquals(40, View.resolveSize(50, 40 | View.MeasureSpec.EXACTLY));
   1182 
   1183         assertEquals(30, View.resolveSize(50, 30 | View.MeasureSpec.AT_MOST));
   1184 
   1185         assertEquals(20, View.resolveSize(20, 30 | View.MeasureSpec.AT_MOST));
   1186     }
   1187 
   1188     @Test
   1189     public void testGetDefaultSize() {
   1190         assertEquals(50, View.getDefaultSize(50, View.MeasureSpec.UNSPECIFIED));
   1191 
   1192         assertEquals(40, View.getDefaultSize(50, 40 | View.MeasureSpec.EXACTLY));
   1193 
   1194         assertEquals(30, View.getDefaultSize(50, 30 | View.MeasureSpec.AT_MOST));
   1195 
   1196         assertEquals(30, View.getDefaultSize(20, 30 | View.MeasureSpec.AT_MOST));
   1197     }
   1198 
   1199     @Test
   1200     public void testAccessId() {
   1201         View view = new View(mActivity);
   1202 
   1203         assertEquals(View.NO_ID, view.getId());
   1204 
   1205         view.setId(10);
   1206         assertEquals(10, view.getId());
   1207 
   1208         view.setId(0xFFFFFFFF);
   1209         assertEquals(0xFFFFFFFF, view.getId());
   1210     }
   1211 
   1212     @Test
   1213     public void testAccessLongClickable() {
   1214         View view = new View(mActivity);
   1215 
   1216         assertFalse(view.isLongClickable());
   1217 
   1218         view.setLongClickable(true);
   1219         assertTrue(view.isLongClickable());
   1220 
   1221         view.setLongClickable(false);
   1222         assertFalse(view.isLongClickable());
   1223     }
   1224 
   1225     @Test
   1226     public void testAccessClickable() {
   1227         View view = new View(mActivity);
   1228 
   1229         assertFalse(view.isClickable());
   1230 
   1231         view.setClickable(true);
   1232         assertTrue(view.isClickable());
   1233 
   1234         view.setClickable(false);
   1235         assertFalse(view.isClickable());
   1236     }
   1237 
   1238     @Test
   1239     public void testAccessContextClickable() {
   1240         View view = new View(mActivity);
   1241 
   1242         assertFalse(view.isContextClickable());
   1243 
   1244         view.setContextClickable(true);
   1245         assertTrue(view.isContextClickable());
   1246 
   1247         view.setContextClickable(false);
   1248         assertFalse(view.isContextClickable());
   1249     }
   1250 
   1251     @Test
   1252     public void testGetContextMenuInfo() {
   1253         MockView view = new MockView(mActivity);
   1254 
   1255         assertNull(view.getContextMenuInfo());
   1256     }
   1257 
   1258     @Test
   1259     public void testSetOnCreateContextMenuListener() {
   1260         View view = new View(mActivity);
   1261         assertFalse(view.isLongClickable());
   1262 
   1263         view.setOnCreateContextMenuListener(null);
   1264         assertTrue(view.isLongClickable());
   1265 
   1266         view.setOnCreateContextMenuListener(mock(View.OnCreateContextMenuListener.class));
   1267         assertTrue(view.isLongClickable());
   1268     }
   1269 
   1270     @Test
   1271     public void testCreateContextMenu() throws Throwable {
   1272         mActivityRule.runOnUiThread(() -> {
   1273             View.OnCreateContextMenuListener listener =
   1274                     mock(View.OnCreateContextMenuListener.class);
   1275             MockView view = new MockView(mActivity);
   1276             mActivity.setContentView(view);
   1277             mActivity.registerForContextMenu(view);
   1278             view.setOnCreateContextMenuListener(listener);
   1279             assertFalse(view.hasCalledOnCreateContextMenu());
   1280             verifyZeroInteractions(listener);
   1281 
   1282             view.showContextMenu();
   1283             assertTrue(view.hasCalledOnCreateContextMenu());
   1284             verify(listener, times(1)).onCreateContextMenu(
   1285                     any(), eq(view), any());
   1286         });
   1287     }
   1288 
   1289     @Test(expected=NullPointerException.class)
   1290     public void testCreateContextMenuNull() {
   1291         MockView view = new MockView(mActivity);
   1292         view.createContextMenu(null);
   1293     }
   1294 
   1295     @Test
   1296     public void testAddFocusables() {
   1297         View view = new View(mActivity);
   1298         ArrayList<View> viewList = new ArrayList<>();
   1299 
   1300         // view is not focusable
   1301         assertFalse(view.isFocusable());
   1302         assertEquals(0, viewList.size());
   1303         view.addFocusables(viewList, 0);
   1304         assertEquals(0, viewList.size());
   1305 
   1306         // view is focusable
   1307         view.setFocusable(true);
   1308         view.addFocusables(viewList, 0);
   1309         assertEquals(1, viewList.size());
   1310         assertEquals(view, viewList.get(0));
   1311 
   1312         // null array should be ignored
   1313         view.addFocusables(null, 0);
   1314     }
   1315 
   1316     @Test
   1317     public void testGetFocusables() {
   1318         View view = new View(mActivity);
   1319         ArrayList<View> viewList;
   1320 
   1321         // view is not focusable
   1322         assertFalse(view.isFocusable());
   1323         viewList = view.getFocusables(0);
   1324         assertEquals(0, viewList.size());
   1325 
   1326         // view is focusable
   1327         view.setFocusable(true);
   1328         viewList = view.getFocusables(0);
   1329         assertEquals(1, viewList.size());
   1330         assertEquals(view, viewList.get(0));
   1331 
   1332         viewList = view.getFocusables(-1);
   1333         assertEquals(1, viewList.size());
   1334         assertEquals(view, viewList.get(0));
   1335     }
   1336 
   1337     @Test
   1338     public void testAddFocusablesWithoutTouchMode() {
   1339         View view = new View(mActivity);
   1340         assertFalse("test sanity", view.isInTouchMode());
   1341         focusableInTouchModeTest(view, false);
   1342     }
   1343 
   1344     @Test
   1345     public void testAddFocusablesInTouchMode() {
   1346         View view = spy(new View(mActivity));
   1347         when(view.isInTouchMode()).thenReturn(true);
   1348         focusableInTouchModeTest(view, true);
   1349     }
   1350 
   1351     private void focusableInTouchModeTest(View view, boolean inTouchMode) {
   1352         ArrayList<View> views = new ArrayList<>();
   1353 
   1354         view.setFocusableInTouchMode(false);
   1355         view.setFocusable(true);
   1356 
   1357         view.addFocusables(views, View.FOCUS_FORWARD);
   1358         if (inTouchMode) {
   1359             assertEquals(Collections.emptyList(), views);
   1360         } else {
   1361             assertEquals(Collections.singletonList(view), views);
   1362         }
   1363 
   1364         views.clear();
   1365         view.addFocusables(views, View.FOCUS_FORWARD, View.FOCUSABLES_ALL);
   1366         assertEquals(Collections.singletonList(view), views);
   1367 
   1368         views.clear();
   1369         view.addFocusables(views, View.FOCUS_FORWARD, View.FOCUSABLES_TOUCH_MODE);
   1370         assertEquals(Collections.emptyList(), views);
   1371 
   1372         view.setFocusableInTouchMode(true);
   1373 
   1374         views.clear();
   1375         view.addFocusables(views, View.FOCUS_FORWARD);
   1376         assertEquals(Collections.singletonList(view), views);
   1377 
   1378         views.clear();
   1379         view.addFocusables(views, View.FOCUS_FORWARD, View.FOCUSABLES_ALL);
   1380         assertEquals(Collections.singletonList(view), views);
   1381 
   1382         views.clear();
   1383         view.addFocusables(views, View.FOCUS_FORWARD, View.FOCUSABLES_TOUCH_MODE);
   1384         assertEquals(Collections.singletonList(view), views);
   1385 
   1386         view.setFocusable(false);
   1387 
   1388         views.clear();
   1389         view.addFocusables(views, View.FOCUS_FORWARD);
   1390         assertEquals(Collections.emptyList(), views);
   1391 
   1392         views.clear();
   1393         view.addFocusables(views, View.FOCUS_FORWARD, View.FOCUSABLES_ALL);
   1394         assertEquals(Collections.emptyList(), views);
   1395 
   1396         views.clear();
   1397         view.addFocusables(views, View.FOCUS_FORWARD, View.FOCUSABLES_TOUCH_MODE);
   1398         assertEquals(Collections.emptyList(), views);
   1399     }
   1400 
   1401     @Test
   1402     public void testAddKeyboardNavigationClusters() {
   1403         View view = new View(mActivity);
   1404         ArrayList<View> viewList = new ArrayList<>();
   1405 
   1406         // View is not a keyboard navigation cluster
   1407         assertFalse(view.isKeyboardNavigationCluster());
   1408         view.addKeyboardNavigationClusters(viewList, 0);
   1409         assertEquals(0, viewList.size());
   1410 
   1411         // View is a cluster (but not focusable, so technically empty)
   1412         view.setKeyboardNavigationCluster(true);
   1413         view.addKeyboardNavigationClusters(viewList, 0);
   1414         assertEquals(0, viewList.size());
   1415         viewList.clear();
   1416         // a focusable cluster is not-empty
   1417         view.setFocusableInTouchMode(true);
   1418         view.addKeyboardNavigationClusters(viewList, 0);
   1419         assertEquals(1, viewList.size());
   1420         assertEquals(view, viewList.get(0));
   1421     }
   1422 
   1423     @Test
   1424     public void testKeyboardNavigationClusterSearch() throws Throwable {
   1425         mActivityRule.runOnUiThread(() -> {
   1426             ViewGroup decorView = (ViewGroup) mActivity.getWindow().getDecorView();
   1427             decorView.removeAllViews();
   1428             View v1 = new MockView(mActivity);
   1429             v1.setFocusableInTouchMode(true);
   1430             View v2 = new MockView(mActivity);
   1431             v2.setFocusableInTouchMode(true);
   1432             decorView.addView(v1);
   1433             decorView.addView(v2);
   1434 
   1435             // Searching for clusters.
   1436             v1.setKeyboardNavigationCluster(true);
   1437             v2.setKeyboardNavigationCluster(true);
   1438             assertEquals(v2, decorView.keyboardNavigationClusterSearch(v1, View.FOCUS_FORWARD));
   1439             assertEquals(v1, decorView.keyboardNavigationClusterSearch(null, View.FOCUS_FORWARD));
   1440             assertEquals(v2, decorView.keyboardNavigationClusterSearch(null, View.FOCUS_BACKWARD));
   1441             assertEquals(v2, v1.keyboardNavigationClusterSearch(null, View.FOCUS_FORWARD));
   1442             assertEquals(decorView, v1.keyboardNavigationClusterSearch(null, View.FOCUS_BACKWARD));
   1443             assertEquals(decorView, v2.keyboardNavigationClusterSearch(null, View.FOCUS_FORWARD));
   1444             assertEquals(v1, v2.keyboardNavigationClusterSearch(null, View.FOCUS_BACKWARD));
   1445 
   1446             // Clusters in 3-level hierarchy.
   1447             decorView.removeAllViews();
   1448             LinearLayout middle = new LinearLayout(mActivity);
   1449             middle.addView(v1);
   1450             middle.addView(v2);
   1451             decorView.addView(middle);
   1452             assertEquals(decorView, v2.keyboardNavigationClusterSearch(null, View.FOCUS_FORWARD));
   1453         });
   1454     }
   1455 
   1456     @Test
   1457     public void testGetRootView() {
   1458         MockView view = new MockView(mActivity);
   1459 
   1460         assertNull(view.getParent());
   1461         assertEquals(view, view.getRootView());
   1462 
   1463         view.setParent(mMockParent);
   1464         assertEquals(mMockParent, view.getRootView());
   1465     }
   1466 
   1467     @Test
   1468     public void testGetSolidColor() {
   1469         View view = new View(mActivity);
   1470 
   1471         assertEquals(0, view.getSolidColor());
   1472     }
   1473 
   1474     @Test
   1475     public void testSetMinimumWidth() {
   1476         MockView view = new MockView(mActivity);
   1477         assertEquals(0, view.getSuggestedMinimumWidth());
   1478 
   1479         view.setMinimumWidth(100);
   1480         assertEquals(100, view.getSuggestedMinimumWidth());
   1481 
   1482         view.setMinimumWidth(-100);
   1483         assertEquals(-100, view.getSuggestedMinimumWidth());
   1484     }
   1485 
   1486     @Test
   1487     public void testGetSuggestedMinimumWidth() {
   1488         MockView view = new MockView(mActivity);
   1489         Drawable d = mResources.getDrawable(R.drawable.scenery);
   1490         int drawableMinimumWidth = d.getMinimumWidth();
   1491 
   1492         // drawable is null
   1493         view.setMinimumWidth(100);
   1494         assertNull(view.getBackground());
   1495         assertEquals(100, view.getSuggestedMinimumWidth());
   1496 
   1497         // drawable minimum width is larger than mMinWidth
   1498         view.setBackgroundDrawable(d);
   1499         view.setMinimumWidth(drawableMinimumWidth - 10);
   1500         assertEquals(drawableMinimumWidth, view.getSuggestedMinimumWidth());
   1501 
   1502         // drawable minimum width is smaller than mMinWidth
   1503         view.setMinimumWidth(drawableMinimumWidth + 10);
   1504         assertEquals(drawableMinimumWidth + 10, view.getSuggestedMinimumWidth());
   1505     }
   1506 
   1507     @Test
   1508     public void testSetMinimumHeight() {
   1509         MockView view = new MockView(mActivity);
   1510         assertEquals(0, view.getSuggestedMinimumHeight());
   1511 
   1512         view.setMinimumHeight(100);
   1513         assertEquals(100, view.getSuggestedMinimumHeight());
   1514 
   1515         view.setMinimumHeight(-100);
   1516         assertEquals(-100, view.getSuggestedMinimumHeight());
   1517     }
   1518 
   1519     @Test
   1520     public void testGetSuggestedMinimumHeight() {
   1521         MockView view = new MockView(mActivity);
   1522         Drawable d = mResources.getDrawable(R.drawable.scenery);
   1523         int drawableMinimumHeight = d.getMinimumHeight();
   1524 
   1525         // drawable is null
   1526         view.setMinimumHeight(100);
   1527         assertNull(view.getBackground());
   1528         assertEquals(100, view.getSuggestedMinimumHeight());
   1529 
   1530         // drawable minimum height is larger than mMinHeight
   1531         view.setBackgroundDrawable(d);
   1532         view.setMinimumHeight(drawableMinimumHeight - 10);
   1533         assertEquals(drawableMinimumHeight, view.getSuggestedMinimumHeight());
   1534 
   1535         // drawable minimum height is smaller than mMinHeight
   1536         view.setMinimumHeight(drawableMinimumHeight + 10);
   1537         assertEquals(drawableMinimumHeight + 10, view.getSuggestedMinimumHeight());
   1538     }
   1539 
   1540     @Test
   1541     public void testAccessWillNotCacheDrawing() {
   1542         View view = new View(mActivity);
   1543 
   1544         assertFalse(view.willNotCacheDrawing());
   1545 
   1546         view.setWillNotCacheDrawing(true);
   1547         assertTrue(view.willNotCacheDrawing());
   1548     }
   1549 
   1550     @Test
   1551     public void testAccessDrawingCacheEnabled() {
   1552         View view = new View(mActivity);
   1553 
   1554         assertFalse(view.isDrawingCacheEnabled());
   1555 
   1556         view.setDrawingCacheEnabled(true);
   1557         assertTrue(view.isDrawingCacheEnabled());
   1558     }
   1559 
   1560     @Test
   1561     public void testGetDrawingCache() {
   1562         MockView view = new MockView(mActivity);
   1563 
   1564         // should not call buildDrawingCache when getDrawingCache
   1565         assertNull(view.getDrawingCache());
   1566 
   1567         // should call buildDrawingCache when getDrawingCache
   1568         view = (MockView) mActivity.findViewById(R.id.mock_view);
   1569         view.setDrawingCacheEnabled(true);
   1570         Bitmap bitmap1 = view.getDrawingCache();
   1571         assertNotNull(bitmap1);
   1572         assertEquals(view.getWidth(), bitmap1.getWidth());
   1573         assertEquals(view.getHeight(), bitmap1.getHeight());
   1574 
   1575         view.setWillNotCacheDrawing(true);
   1576         assertNull(view.getDrawingCache());
   1577 
   1578         view.setWillNotCacheDrawing(false);
   1579         // build a new drawingcache
   1580         Bitmap bitmap2 = view.getDrawingCache();
   1581         assertNotSame(bitmap1, bitmap2);
   1582     }
   1583 
   1584     @Test
   1585     public void testBuildAndDestroyDrawingCache() {
   1586         MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   1587 
   1588         assertNull(view.getDrawingCache());
   1589 
   1590         view.buildDrawingCache();
   1591         Bitmap bitmap = view.getDrawingCache();
   1592         assertNotNull(bitmap);
   1593         assertEquals(view.getWidth(), bitmap.getWidth());
   1594         assertEquals(view.getHeight(), bitmap.getHeight());
   1595 
   1596         view.destroyDrawingCache();
   1597         assertNull(view.getDrawingCache());
   1598     }
   1599 
   1600     @Test
   1601     public void testAccessWillNotDraw() {
   1602         View view = new View(mActivity);
   1603 
   1604         assertFalse(view.willNotDraw());
   1605 
   1606         view.setWillNotDraw(true);
   1607         assertTrue(view.willNotDraw());
   1608     }
   1609 
   1610     @Test
   1611     public void testAccessDrawingCacheQuality() {
   1612         View view = new View(mActivity);
   1613 
   1614         assertEquals(0, view.getDrawingCacheQuality());
   1615 
   1616         view.setDrawingCacheQuality(1);
   1617         assertEquals(0, view.getDrawingCacheQuality());
   1618 
   1619         view.setDrawingCacheQuality(0x00100000);
   1620         assertEquals(0x00100000, view.getDrawingCacheQuality());
   1621 
   1622         view.setDrawingCacheQuality(0x00080000);
   1623         assertEquals(0x00080000, view.getDrawingCacheQuality());
   1624 
   1625         view.setDrawingCacheQuality(0xffffffff);
   1626         // 0x00180000 is View.DRAWING_CACHE_QUALITY_MASK
   1627         assertEquals(0x00180000, view.getDrawingCacheQuality());
   1628     }
   1629 
   1630     @Test
   1631     public void testDispatchSetSelected() {
   1632         MockView mockView1 = new MockView(mActivity);
   1633         MockView mockView2 = new MockView(mActivity);
   1634         mockView1.setParent(mMockParent);
   1635         mockView2.setParent(mMockParent);
   1636 
   1637         mMockParent.dispatchSetSelected(true);
   1638         assertTrue(mockView1.isSelected());
   1639         assertTrue(mockView2.isSelected());
   1640 
   1641         mMockParent.dispatchSetSelected(false);
   1642         assertFalse(mockView1.isSelected());
   1643         assertFalse(mockView2.isSelected());
   1644     }
   1645 
   1646     @Test
   1647     public void testAccessSelected() {
   1648         View view = new View(mActivity);
   1649 
   1650         assertFalse(view.isSelected());
   1651 
   1652         view.setSelected(true);
   1653         assertTrue(view.isSelected());
   1654     }
   1655 
   1656     @Test
   1657     public void testDispatchSetPressed() {
   1658         MockView mockView1 = new MockView(mActivity);
   1659         MockView mockView2 = new MockView(mActivity);
   1660         mockView1.setParent(mMockParent);
   1661         mockView2.setParent(mMockParent);
   1662 
   1663         mMockParent.dispatchSetPressed(true);
   1664         assertTrue(mockView1.isPressed());
   1665         assertTrue(mockView2.isPressed());
   1666 
   1667         mMockParent.dispatchSetPressed(false);
   1668         assertFalse(mockView1.isPressed());
   1669         assertFalse(mockView2.isPressed());
   1670     }
   1671 
   1672     @Test
   1673     public void testAccessPressed() {
   1674         View view = new View(mActivity);
   1675 
   1676         assertFalse(view.isPressed());
   1677 
   1678         view.setPressed(true);
   1679         assertTrue(view.isPressed());
   1680     }
   1681 
   1682     @Test
   1683     public void testAccessSoundEffectsEnabled() {
   1684         View view = new View(mActivity);
   1685 
   1686         assertTrue(view.isSoundEffectsEnabled());
   1687 
   1688         view.setSoundEffectsEnabled(false);
   1689         assertFalse(view.isSoundEffectsEnabled());
   1690     }
   1691 
   1692     @Test
   1693     public void testAccessKeepScreenOn() {
   1694         View view = new View(mActivity);
   1695 
   1696         assertFalse(view.getKeepScreenOn());
   1697 
   1698         view.setKeepScreenOn(true);
   1699         assertTrue(view.getKeepScreenOn());
   1700     }
   1701 
   1702     @Test
   1703     public void testAccessDuplicateParentStateEnabled() {
   1704         View view = new View(mActivity);
   1705 
   1706         assertFalse(view.isDuplicateParentStateEnabled());
   1707 
   1708         view.setDuplicateParentStateEnabled(true);
   1709         assertTrue(view.isDuplicateParentStateEnabled());
   1710     }
   1711 
   1712     @Test
   1713     public void testAccessEnabled() {
   1714         View view = new View(mActivity);
   1715 
   1716         assertTrue(view.isEnabled());
   1717 
   1718         view.setEnabled(false);
   1719         assertFalse(view.isEnabled());
   1720     }
   1721 
   1722     @Test
   1723     public void testAccessSaveEnabled() {
   1724         View view = new View(mActivity);
   1725 
   1726         assertTrue(view.isSaveEnabled());
   1727 
   1728         view.setSaveEnabled(false);
   1729         assertFalse(view.isSaveEnabled());
   1730     }
   1731 
   1732     @Test(expected=NullPointerException.class)
   1733     public void testShowContextMenuNullParent() {
   1734         MockView view = new MockView(mActivity);
   1735 
   1736         assertNull(view.getParent());
   1737         view.showContextMenu();
   1738     }
   1739 
   1740     @Test
   1741     public void testShowContextMenu() {
   1742         MockView view = new MockView(mActivity);
   1743         view.setParent(mMockParent);
   1744         assertFalse(mMockParent.hasShowContextMenuForChild());
   1745 
   1746         assertFalse(view.showContextMenu());
   1747         assertTrue(mMockParent.hasShowContextMenuForChild());
   1748     }
   1749 
   1750     @Test(expected=NullPointerException.class)
   1751     public void testShowContextMenuXYNullParent() {
   1752         MockView view = new MockView(mActivity);
   1753 
   1754         assertNull(view.getParent());
   1755         view.showContextMenu(0, 0);
   1756     }
   1757 
   1758     @Test
   1759     public void testShowContextMenuXY() {
   1760         MockViewParent parent = new MockViewParent(mActivity);
   1761         MockView view = new MockView(mActivity);
   1762 
   1763         view.setParent(parent);
   1764         assertFalse(parent.hasShowContextMenuForChildXY());
   1765 
   1766         assertFalse(view.showContextMenu(0, 0));
   1767         assertTrue(parent.hasShowContextMenuForChildXY());
   1768     }
   1769 
   1770     @Test
   1771     public void testFitSystemWindows() {
   1772         final XmlResourceParser parser = mResources.getLayout(R.layout.view_layout);
   1773         final AttributeSet attrs = Xml.asAttributeSet(parser);
   1774         Rect insets = new Rect(10, 20, 30, 50);
   1775 
   1776         MockView view = new MockView(mActivity);
   1777         assertFalse(view.fitSystemWindows(insets));
   1778         assertFalse(view.fitSystemWindows(null));
   1779 
   1780         view = new MockView(mActivity, attrs, android.R.attr.fitsSystemWindows);
   1781         assertFalse(view.fitSystemWindows(insets));
   1782         assertFalse(view.fitSystemWindows(null));
   1783     }
   1784 
   1785     @Test
   1786     public void testPerformClick() {
   1787         View view = new View(mActivity);
   1788         View.OnClickListener listener = mock(View.OnClickListener.class);
   1789 
   1790         assertFalse(view.performClick());
   1791 
   1792         verifyZeroInteractions(listener);
   1793         view.setOnClickListener(listener);
   1794 
   1795         assertTrue(view.performClick());
   1796         verify(listener,times(1)).onClick(view);
   1797 
   1798         view.setOnClickListener(null);
   1799         assertFalse(view.performClick());
   1800     }
   1801 
   1802     @Test
   1803     public void testSetOnClickListener() {
   1804         View view = new View(mActivity);
   1805         assertFalse(view.performClick());
   1806         assertFalse(view.isClickable());
   1807 
   1808         view.setOnClickListener(null);
   1809         assertFalse(view.performClick());
   1810         assertTrue(view.isClickable());
   1811 
   1812         view.setOnClickListener(mock(View.OnClickListener.class));
   1813         assertTrue(view.performClick());
   1814         assertTrue(view.isClickable());
   1815     }
   1816 
   1817     @Test
   1818     public void testSetOnGenericMotionListener() {
   1819         View view = new View(mActivity);
   1820         MotionEvent event =
   1821                 EventUtils.generateMouseEvent(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0);
   1822 
   1823         assertFalse(view.dispatchGenericMotionEvent(event));
   1824         event.recycle();
   1825 
   1826         View.OnGenericMotionListener listener = mock(View.OnGenericMotionListener.class);
   1827         doReturn(true).when(listener).onGenericMotion(any(), any());
   1828         view.setOnGenericMotionListener(listener);
   1829 
   1830         MotionEvent event2 =
   1831                 EventUtils.generateMouseEvent(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0);
   1832 
   1833         assertTrue(view.dispatchGenericMotionEvent(event2));
   1834         event2.recycle();
   1835 
   1836         view.setOnGenericMotionListener(null);
   1837         MotionEvent event3 =
   1838                 EventUtils.generateMouseEvent(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0);
   1839 
   1840         assertFalse(view.dispatchGenericMotionEvent(event3));
   1841         event3.recycle();
   1842     }
   1843 
   1844     @Test(expected=NullPointerException.class)
   1845     public void testPerformLongClickNullParent() {
   1846         MockView view = new MockView(mActivity);
   1847         view.performLongClick();
   1848     }
   1849 
   1850     @Test
   1851     public void testPerformLongClick() {
   1852         MockView view = new MockView(mActivity);
   1853         View.OnLongClickListener listener = mock(View.OnLongClickListener.class);
   1854         doReturn(true).when(listener).onLongClick(any());
   1855 
   1856         view.setParent(mMockParent);
   1857         assertFalse(mMockParent.hasShowContextMenuForChild());
   1858         assertFalse(view.performLongClick());
   1859         assertTrue(mMockParent.hasShowContextMenuForChild());
   1860 
   1861         view.setOnLongClickListener(listener);
   1862         mMockParent.reset();
   1863         assertFalse(mMockParent.hasShowContextMenuForChild());
   1864         verifyZeroInteractions(listener);
   1865         assertTrue(view.performLongClick());
   1866         assertFalse(mMockParent.hasShowContextMenuForChild());
   1867         verify(listener, times(1)).onLongClick(view);
   1868     }
   1869 
   1870     @Test(expected=NullPointerException.class)
   1871     public void testPerformLongClickXYNullParent() {
   1872         MockView view = new MockView(mActivity);
   1873         view.performLongClick(0, 0);
   1874     }
   1875 
   1876     @Test
   1877     public void testPerformLongClickXY() {
   1878         MockViewParent parent = new MockViewParent(mActivity);
   1879         MockView view = new MockView(mActivity);
   1880 
   1881         parent.addView(view);
   1882         assertFalse(parent.hasShowContextMenuForChildXY());
   1883 
   1884         // Verify default context menu behavior.
   1885         assertFalse(view.performLongClick(0, 0));
   1886         assertTrue(parent.hasShowContextMenuForChildXY());
   1887     }
   1888 
   1889     @Test
   1890     public void testPerformLongClickXY_WithListener() {
   1891         OnLongClickListener listener = mock(OnLongClickListener.class);
   1892         when(listener.onLongClick(any(View.class))).thenReturn(true);
   1893 
   1894         MockViewParent parent = new MockViewParent(mActivity);
   1895         MockView view = new MockView(mActivity);
   1896 
   1897         view.setOnLongClickListener(listener);
   1898         verify(listener, never()).onLongClick(any(View.class));
   1899 
   1900         parent.addView(view);
   1901         assertFalse(parent.hasShowContextMenuForChildXY());
   1902 
   1903         // Verify listener is preferred over default context menu.
   1904         assertTrue(view.performLongClick(0, 0));
   1905         assertFalse(parent.hasShowContextMenuForChildXY());
   1906         verify(listener).onLongClick(view);
   1907     }
   1908 
   1909     @Test
   1910     public void testSetOnLongClickListener() {
   1911         MockView view = new MockView(mActivity);
   1912         view.setParent(mMockParent);
   1913         assertFalse(view.performLongClick());
   1914         assertFalse(view.isLongClickable());
   1915 
   1916         view.setOnLongClickListener(null);
   1917         assertFalse(view.performLongClick());
   1918         assertTrue(view.isLongClickable());
   1919 
   1920         View.OnLongClickListener listener = mock(View.OnLongClickListener.class);
   1921         doReturn(true).when(listener).onLongClick(any());
   1922         view.setOnLongClickListener(listener);
   1923         assertTrue(view.performLongClick());
   1924         assertTrue(view.isLongClickable());
   1925     }
   1926 
   1927     @Test
   1928     public void testPerformContextClick() {
   1929         MockView view = new MockView(mActivity);
   1930         view.setParent(mMockParent);
   1931         View.OnContextClickListener listener = mock(View.OnContextClickListener.class);
   1932         doReturn(true).when(listener).onContextClick(any());
   1933 
   1934         view.setOnContextClickListener(listener);
   1935         verifyZeroInteractions(listener);
   1936 
   1937         assertTrue(view.performContextClick());
   1938         verify(listener, times(1)).onContextClick(view);
   1939     }
   1940 
   1941     @Test
   1942     public void testSetOnContextClickListener() {
   1943         MockView view = new MockView(mActivity);
   1944         view.setParent(mMockParent);
   1945 
   1946         assertFalse(view.performContextClick());
   1947         assertFalse(view.isContextClickable());
   1948 
   1949         View.OnContextClickListener listener = mock(View.OnContextClickListener.class);
   1950         doReturn(true).when(listener).onContextClick(any());
   1951         view.setOnContextClickListener(listener);
   1952         assertTrue(view.performContextClick());
   1953         assertTrue(view.isContextClickable());
   1954     }
   1955 
   1956     @Test
   1957     public void testAccessOnFocusChangeListener() {
   1958         View view = new View(mActivity);
   1959         View.OnFocusChangeListener listener = mock(View.OnFocusChangeListener.class);
   1960 
   1961         assertNull(view.getOnFocusChangeListener());
   1962 
   1963         view.setOnFocusChangeListener(listener);
   1964         assertSame(listener, view.getOnFocusChangeListener());
   1965     }
   1966 
   1967     @Test
   1968     public void testAccessNextFocusUpId() {
   1969         View view = new View(mActivity);
   1970 
   1971         assertEquals(View.NO_ID, view.getNextFocusUpId());
   1972 
   1973         view.setNextFocusUpId(1);
   1974         assertEquals(1, view.getNextFocusUpId());
   1975 
   1976         view.setNextFocusUpId(Integer.MAX_VALUE);
   1977         assertEquals(Integer.MAX_VALUE, view.getNextFocusUpId());
   1978 
   1979         view.setNextFocusUpId(Integer.MIN_VALUE);
   1980         assertEquals(Integer.MIN_VALUE, view.getNextFocusUpId());
   1981     }
   1982 
   1983     @Test
   1984     public void testAccessNextFocusDownId() {
   1985         View view = new View(mActivity);
   1986 
   1987         assertEquals(View.NO_ID, view.getNextFocusDownId());
   1988 
   1989         view.setNextFocusDownId(1);
   1990         assertEquals(1, view.getNextFocusDownId());
   1991 
   1992         view.setNextFocusDownId(Integer.MAX_VALUE);
   1993         assertEquals(Integer.MAX_VALUE, view.getNextFocusDownId());
   1994 
   1995         view.setNextFocusDownId(Integer.MIN_VALUE);
   1996         assertEquals(Integer.MIN_VALUE, view.getNextFocusDownId());
   1997     }
   1998 
   1999     @Test
   2000     public void testAccessNextFocusLeftId() {
   2001         View view = new View(mActivity);
   2002 
   2003         assertEquals(View.NO_ID, view.getNextFocusLeftId());
   2004 
   2005         view.setNextFocusLeftId(1);
   2006         assertEquals(1, view.getNextFocusLeftId());
   2007 
   2008         view.setNextFocusLeftId(Integer.MAX_VALUE);
   2009         assertEquals(Integer.MAX_VALUE, view.getNextFocusLeftId());
   2010 
   2011         view.setNextFocusLeftId(Integer.MIN_VALUE);
   2012         assertEquals(Integer.MIN_VALUE, view.getNextFocusLeftId());
   2013     }
   2014 
   2015     @Test
   2016     public void testAccessNextFocusRightId() {
   2017         View view = new View(mActivity);
   2018 
   2019         assertEquals(View.NO_ID, view.getNextFocusRightId());
   2020 
   2021         view.setNextFocusRightId(1);
   2022         assertEquals(1, view.getNextFocusRightId());
   2023 
   2024         view.setNextFocusRightId(Integer.MAX_VALUE);
   2025         assertEquals(Integer.MAX_VALUE, view.getNextFocusRightId());
   2026 
   2027         view.setNextFocusRightId(Integer.MIN_VALUE);
   2028         assertEquals(Integer.MIN_VALUE, view.getNextFocusRightId());
   2029     }
   2030 
   2031     @Test
   2032     public void testAccessMeasuredDimension() {
   2033         MockView view = new MockView(mActivity);
   2034         assertEquals(0, view.getMeasuredWidth());
   2035         assertEquals(0, view.getMeasuredHeight());
   2036 
   2037         view.setMeasuredDimensionWrapper(20, 30);
   2038         assertEquals(20, view.getMeasuredWidth());
   2039         assertEquals(30, view.getMeasuredHeight());
   2040     }
   2041 
   2042     @Test
   2043     public void testMeasure() throws Throwable {
   2044         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   2045 
   2046         float density = view.getContext().getResources().getDisplayMetrics().density;
   2047         int size1 = (int) (100 * density + 0.5);
   2048         int size2 = (int) (75 * density + 0.5);
   2049 
   2050         assertTrue(view.hasCalledOnMeasure());
   2051         assertEquals(size1, view.getMeasuredWidth());
   2052         assertEquals(size2, view.getMeasuredHeight());
   2053 
   2054         view.reset();
   2055         mActivityRule.runOnUiThread(view::requestLayout);
   2056         mInstrumentation.waitForIdleSync();
   2057         assertTrue(view.hasCalledOnMeasure());
   2058         assertEquals(size1, view.getMeasuredWidth());
   2059         assertEquals(size2, view.getMeasuredHeight());
   2060 
   2061         view.reset();
   2062         final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(size2, size1);
   2063         mActivityRule.runOnUiThread(() -> view.setLayoutParams(layoutParams));
   2064         mInstrumentation.waitForIdleSync();
   2065         assertTrue(view.hasCalledOnMeasure());
   2066         assertEquals(size2, view.getMeasuredWidth());
   2067         assertEquals(size1, view.getMeasuredHeight());
   2068     }
   2069 
   2070     @Test(expected=NullPointerException.class)
   2071     public void setSetLayoutParamsNull() {
   2072         View view = new View(mActivity);
   2073         assertNull(view.getLayoutParams());
   2074 
   2075         view.setLayoutParams(null);
   2076     }
   2077 
   2078     @Test
   2079     public void testAccessLayoutParams() {
   2080         View view = new View(mActivity);
   2081         ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(10, 20);
   2082 
   2083         assertFalse(view.isLayoutRequested());
   2084         view.setLayoutParams(params);
   2085         assertSame(params, view.getLayoutParams());
   2086         assertTrue(view.isLayoutRequested());
   2087     }
   2088 
   2089     @Test
   2090     public void testIsShown() {
   2091         MockView view = new MockView(mActivity);
   2092 
   2093         view.setVisibility(View.INVISIBLE);
   2094         assertFalse(view.isShown());
   2095 
   2096         view.setVisibility(View.VISIBLE);
   2097         assertNull(view.getParent());
   2098         assertFalse(view.isShown());
   2099 
   2100         view.setParent(mMockParent);
   2101         // mMockParent is not a instance of ViewRoot
   2102         assertFalse(view.isShown());
   2103     }
   2104 
   2105     @Test
   2106     public void testGetDrawingTime() {
   2107         View view = new View(mActivity);
   2108         // mAttachInfo is null
   2109         assertEquals(0, view.getDrawingTime());
   2110 
   2111         // mAttachInfo is not null
   2112         view = mActivity.findViewById(R.id.fit_windows);
   2113         assertEquals(SystemClock.uptimeMillis(), view.getDrawingTime(), 1000);
   2114     }
   2115 
   2116     @Test
   2117     public void testScheduleDrawable() {
   2118         View view = new View(mActivity);
   2119         Drawable drawable = new StateListDrawable();
   2120         // Does nothing.
   2121         Runnable what = () -> {};
   2122         // mAttachInfo is null
   2123         view.scheduleDrawable(drawable, what, 1000);
   2124 
   2125         view.setBackgroundDrawable(drawable);
   2126         view.scheduleDrawable(drawable, what, 1000);
   2127 
   2128         view.scheduleDrawable(null, null, -1000);
   2129 
   2130         // mAttachInfo is not null
   2131         view = mActivity.findViewById(R.id.fit_windows);
   2132         view.scheduleDrawable(drawable, what, 1000);
   2133 
   2134         view.scheduleDrawable(view.getBackground(), what, 1000);
   2135         view.unscheduleDrawable(view.getBackground(), what);
   2136 
   2137         view.scheduleDrawable(null, null, -1000);
   2138     }
   2139 
   2140     @Test
   2141     public void testUnscheduleDrawable() {
   2142         View view = new View(mActivity);
   2143         Drawable drawable = new StateListDrawable();
   2144         Runnable what = () -> {
   2145             // do nothing
   2146         };
   2147 
   2148         // mAttachInfo is null
   2149         view.unscheduleDrawable(drawable, what);
   2150 
   2151         view.setBackgroundDrawable(drawable);
   2152         view.unscheduleDrawable(drawable);
   2153 
   2154         view.unscheduleDrawable(null, null);
   2155         view.unscheduleDrawable(null);
   2156 
   2157         // mAttachInfo is not null
   2158         view = mActivity.findViewById(R.id.fit_windows);
   2159         view.unscheduleDrawable(drawable);
   2160 
   2161         view.scheduleDrawable(view.getBackground(), what, 1000);
   2162         view.unscheduleDrawable(view.getBackground(), what);
   2163 
   2164         view.unscheduleDrawable(null);
   2165         view.unscheduleDrawable(null, null);
   2166     }
   2167 
   2168     @Test
   2169     public void testGetWindowVisibility() {
   2170         View view = new View(mActivity);
   2171         // mAttachInfo is null
   2172         assertEquals(View.GONE, view.getWindowVisibility());
   2173 
   2174         // mAttachInfo is not null
   2175         view = mActivity.findViewById(R.id.fit_windows);
   2176         assertEquals(View.VISIBLE, view.getWindowVisibility());
   2177     }
   2178 
   2179     @Test
   2180     public void testGetWindowToken() {
   2181         View view = new View(mActivity);
   2182         // mAttachInfo is null
   2183         assertNull(view.getWindowToken());
   2184 
   2185         // mAttachInfo is not null
   2186         view = mActivity.findViewById(R.id.fit_windows);
   2187         assertNotNull(view.getWindowToken());
   2188     }
   2189 
   2190     @Test
   2191     public void testHasWindowFocus() {
   2192         View view = new View(mActivity);
   2193         // mAttachInfo is null
   2194         assertFalse(view.hasWindowFocus());
   2195 
   2196         // mAttachInfo is not null
   2197         final View view2 = mActivity.findViewById(R.id.fit_windows);
   2198         // Wait until the window has been focused.
   2199         PollingCheck.waitFor(TIMEOUT_DELTA, view2::hasWindowFocus);
   2200     }
   2201 
   2202     @Test
   2203     public void testGetHandler() {
   2204         MockView view = new MockView(mActivity);
   2205         // mAttachInfo is null
   2206         assertNull(view.getHandler());
   2207     }
   2208 
   2209     @Test
   2210     public void testRemoveCallbacks() throws InterruptedException {
   2211         final long delay = 500L;
   2212         View view = mActivity.findViewById(R.id.mock_view);
   2213         Runnable runner = mock(Runnable.class);
   2214         assertTrue(view.postDelayed(runner, delay));
   2215         assertTrue(view.removeCallbacks(runner));
   2216         assertTrue(view.removeCallbacks(null));
   2217         assertTrue(view.removeCallbacks(mock(Runnable.class)));
   2218         Thread.sleep(delay * 2);
   2219         verifyZeroInteractions(runner);
   2220         // check that the runner actually works
   2221         runner = mock(Runnable.class);
   2222         assertTrue(view.postDelayed(runner, delay));
   2223         Thread.sleep(delay * 2);
   2224         verify(runner, times(1)).run();
   2225     }
   2226 
   2227     @Test
   2228     public void testCancelLongPress() {
   2229         View view = new View(mActivity);
   2230         // mAttachInfo is null
   2231         view.cancelLongPress();
   2232 
   2233         // mAttachInfo is not null
   2234         view = mActivity.findViewById(R.id.fit_windows);
   2235         view.cancelLongPress();
   2236     }
   2237 
   2238     @Test
   2239     public void testGetViewTreeObserver() {
   2240         View view = new View(mActivity);
   2241         // mAttachInfo is null
   2242         assertNotNull(view.getViewTreeObserver());
   2243 
   2244         // mAttachInfo is not null
   2245         view = mActivity.findViewById(R.id.fit_windows);
   2246         assertNotNull(view.getViewTreeObserver());
   2247     }
   2248 
   2249     @Test
   2250     public void testGetWindowAttachCount() {
   2251         MockView view = new MockView(mActivity);
   2252         // mAttachInfo is null
   2253         assertEquals(0, view.getWindowAttachCount());
   2254     }
   2255 
   2256     @UiThreadTest
   2257     @Test
   2258     public void testOnAttachedToAndDetachedFromWindow() {
   2259         MockView mockView = new MockView(mActivity);
   2260         ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
   2261 
   2262         viewGroup.addView(mockView);
   2263         assertTrue(mockView.hasCalledOnAttachedToWindow());
   2264 
   2265         viewGroup.removeView(mockView);
   2266         assertTrue(mockView.hasCalledOnDetachedFromWindow());
   2267 
   2268         mockView.reset();
   2269         mActivity.setContentView(mockView);
   2270         assertTrue(mockView.hasCalledOnAttachedToWindow());
   2271 
   2272         mActivity.setContentView(R.layout.view_layout);
   2273         assertTrue(mockView.hasCalledOnDetachedFromWindow());
   2274     }
   2275 
   2276     @Test
   2277     public void testGetLocationInWindow() {
   2278         final int[] location = new int[]{-1, -1};
   2279 
   2280         final View layout = mActivity.findViewById(R.id.viewlayout_root);
   2281         int[] layoutLocation = new int[]{-1, -1};
   2282         layout.getLocationInWindow(layoutLocation);
   2283 
   2284         final View mockView = mActivity.findViewById(R.id.mock_view);
   2285         mockView.getLocationInWindow(location);
   2286         assertEquals(layoutLocation[0], location[0]);
   2287         assertEquals(layoutLocation[1], location[1]);
   2288 
   2289         final View scrollView = mActivity.findViewById(R.id.scroll_view);
   2290         scrollView.getLocationInWindow(location);
   2291         assertEquals(layoutLocation[0], location[0]);
   2292         assertEquals(layoutLocation[1] + mockView.getHeight(), location[1]);
   2293     }
   2294 
   2295     @Test(expected=IllegalArgumentException.class)
   2296     public void testGetLocationInWindowNullArray() {
   2297         final View layout = mActivity.findViewById(R.id.viewlayout_root);
   2298         final View mockView = mActivity.findViewById(R.id.mock_view);
   2299 
   2300         mockView.getLocationInWindow(null);
   2301     }
   2302 
   2303     @Test(expected=IllegalArgumentException.class)
   2304     public void testGetLocationInWindowSmallArray() {
   2305         final View layout = mActivity.findViewById(R.id.viewlayout_root);
   2306         final View mockView = mActivity.findViewById(R.id.mock_view);
   2307 
   2308         mockView.getLocationInWindow(new int[] { 0 });
   2309     }
   2310 
   2311     @Test
   2312     public void testGetLocationOnScreen() {
   2313         final int[] location = new int[]{-1, -1};
   2314 
   2315         // mAttachInfo is not null
   2316         final View layout = mActivity.findViewById(R.id.viewlayout_root);
   2317         final int[] layoutLocation = new int[]{-1, -1};
   2318         layout.getLocationOnScreen(layoutLocation);
   2319 
   2320         final View mockView = mActivity.findViewById(R.id.mock_view);
   2321         mockView.getLocationOnScreen(location);
   2322         assertEquals(layoutLocation[0], location[0]);
   2323         assertEquals(layoutLocation[1], location[1]);
   2324 
   2325         final View scrollView = mActivity.findViewById(R.id.scroll_view);
   2326         scrollView.getLocationOnScreen(location);
   2327         assertEquals(layoutLocation[0], location[0]);
   2328         assertEquals(layoutLocation[1] + mockView.getHeight(), location[1]);
   2329     }
   2330 
   2331     @Test(expected=IllegalArgumentException.class)
   2332     public void testGetLocationOnScreenNullArray() {
   2333         final View scrollView = mActivity.findViewById(R.id.scroll_view);
   2334 
   2335         scrollView.getLocationOnScreen(null);
   2336     }
   2337 
   2338     @Test(expected=IllegalArgumentException.class)
   2339     public void testGetLocationOnScreenSmallArray() {
   2340         final View scrollView = mActivity.findViewById(R.id.scroll_view);
   2341 
   2342         scrollView.getLocationOnScreen(new int[] { 0 });
   2343     }
   2344 
   2345     @Test
   2346     public void testAddTouchables() {
   2347         View view = new View(mActivity);
   2348         ArrayList<View> result = new ArrayList<>();
   2349         assertEquals(0, result.size());
   2350 
   2351         view.addTouchables(result);
   2352         assertEquals(0, result.size());
   2353 
   2354         view.setClickable(true);
   2355         view.addTouchables(result);
   2356         assertEquals(1, result.size());
   2357         assertSame(view, result.get(0));
   2358 
   2359         try {
   2360             view.addTouchables(null);
   2361             fail("should throw NullPointerException");
   2362         } catch (NullPointerException e) {
   2363         }
   2364 
   2365         result.clear();
   2366         view.setEnabled(false);
   2367         assertTrue(view.isClickable());
   2368         view.addTouchables(result);
   2369         assertEquals(0, result.size());
   2370     }
   2371 
   2372     @Test
   2373     public void testGetTouchables() {
   2374         View view = new View(mActivity);
   2375         ArrayList<View> result;
   2376 
   2377         result = view.getTouchables();
   2378         assertEquals(0, result.size());
   2379 
   2380         view.setClickable(true);
   2381         result = view.getTouchables();
   2382         assertEquals(1, result.size());
   2383         assertSame(view, result.get(0));
   2384 
   2385         result.clear();
   2386         view.setEnabled(false);
   2387         assertTrue(view.isClickable());
   2388         result = view.getTouchables();
   2389         assertEquals(0, result.size());
   2390     }
   2391 
   2392     @Test
   2393     public void testInflate() {
   2394         View view = View.inflate(mActivity, R.layout.view_layout, null);
   2395         assertNotNull(view);
   2396         assertTrue(view instanceof LinearLayout);
   2397 
   2398         MockView mockView = (MockView) view.findViewById(R.id.mock_view);
   2399         assertNotNull(mockView);
   2400         assertTrue(mockView.hasCalledOnFinishInflate());
   2401     }
   2402 
   2403     @Test
   2404     public void testIsInTouchMode() {
   2405         View view = new View(mActivity);
   2406         // mAttachInfo is null
   2407         assertFalse(view.isInTouchMode());
   2408 
   2409         // mAttachInfo is not null
   2410         view = mActivity.findViewById(R.id.fit_windows);
   2411         assertFalse(view.isInTouchMode());
   2412     }
   2413 
   2414     @Test
   2415     public void testIsInEditMode() {
   2416         View view = new View(mActivity);
   2417         assertFalse(view.isInEditMode());
   2418     }
   2419 
   2420     @Test
   2421     public void testPostInvalidate1() {
   2422         View view = new View(mActivity);
   2423         // mAttachInfo is null
   2424         view.postInvalidate();
   2425 
   2426         // mAttachInfo is not null
   2427         view = mActivity.findViewById(R.id.fit_windows);
   2428         view.postInvalidate();
   2429     }
   2430 
   2431     @Test
   2432     public void testPostInvalidate2() {
   2433         View view = new View(mActivity);
   2434         // mAttachInfo is null
   2435         view.postInvalidate(0, 1, 2, 3);
   2436 
   2437         // mAttachInfo is not null
   2438         view = mActivity.findViewById(R.id.fit_windows);
   2439         view.postInvalidate(10, 20, 30, 40);
   2440         view.postInvalidate(0, -20, -30, -40);
   2441     }
   2442 
   2443     @Test
   2444     public void testPostInvalidateDelayed() {
   2445         View view = new View(mActivity);
   2446         // mAttachInfo is null
   2447         view.postInvalidateDelayed(1000);
   2448         view.postInvalidateDelayed(500, 0, 0, 100, 200);
   2449 
   2450         // mAttachInfo is not null
   2451         view = mActivity.findViewById(R.id.fit_windows);
   2452         view.postInvalidateDelayed(1000);
   2453         view.postInvalidateDelayed(500, 0, 0, 100, 200);
   2454         view.postInvalidateDelayed(-1);
   2455     }
   2456 
   2457     @Test
   2458     public void testPost() {
   2459         View view = new View(mActivity);
   2460         Runnable action = mock(Runnable.class);
   2461 
   2462         // mAttachInfo is null
   2463         assertTrue(view.post(action));
   2464         assertTrue(view.post(null));
   2465 
   2466         // mAttachInfo is not null
   2467         view = mActivity.findViewById(R.id.fit_windows);
   2468         assertTrue(view.post(action));
   2469         assertTrue(view.post(null));
   2470     }
   2471 
   2472     @Test
   2473     public void testPostDelayed() {
   2474         View view = new View(mActivity);
   2475         Runnable action = mock(Runnable.class);
   2476 
   2477         // mAttachInfo is null
   2478         assertTrue(view.postDelayed(action, 1000));
   2479         assertTrue(view.postDelayed(null, -1));
   2480 
   2481         // mAttachInfo is not null
   2482         view = mActivity.findViewById(R.id.fit_windows);
   2483         assertTrue(view.postDelayed(action, 1000));
   2484         assertTrue(view.postDelayed(null, 0));
   2485     }
   2486 
   2487     @UiThreadTest
   2488     @Test
   2489     public void testPlaySoundEffect() {
   2490         MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   2491         // sound effect enabled
   2492         view.playSoundEffect(SoundEffectConstants.CLICK);
   2493 
   2494         // sound effect disabled
   2495         view.setSoundEffectsEnabled(false);
   2496         view.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
   2497 
   2498         // no way to assert the soundConstant be really played.
   2499     }
   2500 
   2501     @Test
   2502     public void testOnKeyShortcut() throws Throwable {
   2503         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   2504         mActivityRule.runOnUiThread(() -> {
   2505             view.setFocusable(true);
   2506             view.requestFocus();
   2507         });
   2508         mInstrumentation.waitForIdleSync();
   2509         assertTrue(view.isFocused());
   2510 
   2511         KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU);
   2512         mInstrumentation.sendKeySync(event);
   2513         event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0);
   2514         mInstrumentation.sendKeySync(event);
   2515         assertTrue(view.hasCalledOnKeyShortcut());
   2516     }
   2517 
   2518     @Test
   2519     public void testOnKeyMultiple() throws Throwable {
   2520         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   2521         mActivityRule.runOnUiThread(() -> view.setFocusable(true));
   2522 
   2523         assertFalse(view.hasCalledOnKeyMultiple());
   2524         view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_ENTER));
   2525         assertTrue(view.hasCalledOnKeyMultiple());
   2526     }
   2527 
   2528     @UiThreadTest
   2529     @Test
   2530     public void testDispatchKeyShortcutEvent() {
   2531         MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   2532         view.setFocusable(true);
   2533 
   2534         KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0);
   2535         view.dispatchKeyShortcutEvent(event);
   2536         assertTrue(view.hasCalledOnKeyShortcut());
   2537     }
   2538 
   2539     @UiThreadTest
   2540     @Test(expected=NullPointerException.class)
   2541     public void testDispatchKeyShortcutEventNull() {
   2542         MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   2543         view.setFocusable(true);
   2544 
   2545         view.dispatchKeyShortcutEvent(null);
   2546     }
   2547 
   2548     @Test
   2549     public void testOnTrackballEvent() throws Throwable {
   2550         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   2551         mActivityRule.runOnUiThread(() -> {
   2552             view.setEnabled(true);
   2553             view.setFocusable(true);
   2554             view.requestFocus();
   2555         });
   2556         mInstrumentation.waitForIdleSync();
   2557 
   2558         long downTime = SystemClock.uptimeMillis();
   2559         long eventTime = downTime;
   2560         MotionEvent event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE,
   2561                 1, 2, 0);
   2562         mInstrumentation.sendTrackballEventSync(event);
   2563         mInstrumentation.waitForIdleSync();
   2564         assertTrue(view.hasCalledOnTrackballEvent());
   2565     }
   2566 
   2567     @UiThreadTest
   2568     @Test
   2569     public void testDispatchTrackballMoveEvent() {
   2570         ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
   2571         MockView mockView1 = new MockView(mActivity);
   2572         MockView mockView2 = new MockView(mActivity);
   2573         viewGroup.addView(mockView1);
   2574         viewGroup.addView(mockView2);
   2575         mockView1.setFocusable(true);
   2576         mockView2.setFocusable(true);
   2577         mockView2.requestFocus();
   2578 
   2579         long downTime = SystemClock.uptimeMillis();
   2580         long eventTime = downTime;
   2581         MotionEvent event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE,
   2582                 1, 2, 0);
   2583         mockView1.dispatchTrackballEvent(event);
   2584         // issue 1695243
   2585         // It passes a trackball motion event down to itself even if it is not the focused view.
   2586         assertTrue(mockView1.hasCalledOnTrackballEvent());
   2587         assertFalse(mockView2.hasCalledOnTrackballEvent());
   2588 
   2589         mockView1.reset();
   2590         mockView2.reset();
   2591         downTime = SystemClock.uptimeMillis();
   2592         eventTime = downTime;
   2593         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, 1, 2, 0);
   2594         mockView2.dispatchTrackballEvent(event);
   2595         assertFalse(mockView1.hasCalledOnTrackballEvent());
   2596         assertTrue(mockView2.hasCalledOnTrackballEvent());
   2597     }
   2598 
   2599     @Test
   2600     public void testDispatchUnhandledMove() throws Throwable {
   2601         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   2602         mActivityRule.runOnUiThread(() -> {
   2603             view.setFocusable(true);
   2604             view.requestFocus();
   2605         });
   2606         mInstrumentation.waitForIdleSync();
   2607 
   2608         KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
   2609         mInstrumentation.sendKeySync(event);
   2610 
   2611         assertTrue(view.hasCalledDispatchUnhandledMove());
   2612     }
   2613 
   2614     @Test
   2615     public void testUnhandledKeys() throws Throwable {
   2616         MockUnhandledKeyListener listener = new MockUnhandledKeyListener();
   2617         ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
   2618         // Attaching a fallback handler
   2619         TextView mockView1 = new TextView(mActivity);
   2620         mockView1.addOnUnhandledKeyEventListener(listener);
   2621 
   2622         // Before the view is attached, it shouldn't respond to anything
   2623         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
   2624         assertFalse(listener.fired());
   2625 
   2626         // Once attached, it should start receiving fallback events
   2627         mActivityRule.runOnUiThread(() -> viewGroup.addView(mockView1));
   2628         mInstrumentation.waitForIdleSync();
   2629         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
   2630         assertTrue(listener.fired());
   2631         listener.reset();
   2632 
   2633         // If multiple on one view, last added should receive event first
   2634         MockUnhandledKeyListener listener2 = new MockUnhandledKeyListener();
   2635         listener2.mReturnVal = true;
   2636         mActivityRule.runOnUiThread(() -> mockView1.addOnUnhandledKeyEventListener(listener2));
   2637         mInstrumentation.waitForIdleSync();
   2638         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
   2639         assertTrue(listener2.fired());
   2640         assertFalse(listener.fired());
   2641         listener2.reset();
   2642 
   2643         // If removed, it should not receive fallbacks anymore
   2644         mActivityRule.runOnUiThread(() -> {
   2645             mockView1.removeOnUnhandledKeyEventListener(listener);
   2646             mockView1.removeOnUnhandledKeyEventListener(listener2);
   2647         });
   2648         mInstrumentation.waitForIdleSync();
   2649         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
   2650         assertFalse(listener.fired());
   2651 
   2652         mActivityRule.runOnUiThread(() -> mActivity.setContentView(R.layout.key_fallback_layout));
   2653         mInstrumentation.waitForIdleSync();
   2654         View higherInNormal = mActivity.findViewById(R.id.higher_in_normal);
   2655         View higherGroup = mActivity.findViewById(R.id.higher_group);
   2656         View lowerInHigher = mActivity.findViewById(R.id.lower_in_higher);
   2657         View lastButton = mActivity.findViewById(R.id.last_button);
   2658         View lastInHigher = mActivity.findViewById(R.id.last_in_higher);
   2659         View lastInNormal = mActivity.findViewById(R.id.last_in_normal);
   2660 
   2661         View[] allViews = new View[]{higherInNormal, higherGroup, lowerInHigher, lastButton,
   2662                 lastInHigher, lastInNormal};
   2663 
   2664         // Test ordering by depth
   2665         listener.mReturnVal = true;
   2666         mActivityRule.runOnUiThread(() -> {
   2667             for (View v : allViews) {
   2668                 v.addOnUnhandledKeyEventListener(listener);
   2669             }
   2670         });
   2671         mInstrumentation.waitForIdleSync();
   2672 
   2673         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
   2674         assertEquals(lastInHigher, listener.mLastView);
   2675         listener.reset();
   2676 
   2677         mActivityRule.runOnUiThread(
   2678                 () -> lastInHigher.removeOnUnhandledKeyEventListener(listener));
   2679         mInstrumentation.waitForIdleSync();
   2680         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
   2681         assertEquals(lowerInHigher, listener.mLastView);
   2682         listener.reset();
   2683 
   2684         mActivityRule.runOnUiThread(
   2685                 () -> lowerInHigher.removeOnUnhandledKeyEventListener(listener));
   2686         mInstrumentation.waitForIdleSync();
   2687         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
   2688         assertEquals(higherGroup, listener.mLastView);
   2689         listener.reset();
   2690 
   2691         mActivityRule.runOnUiThread(() -> higherGroup.removeOnUnhandledKeyEventListener(listener));
   2692         mInstrumentation.waitForIdleSync();
   2693         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
   2694         assertEquals(lastButton, listener.mLastView);
   2695         listener.reset();
   2696 
   2697         mActivityRule.runOnUiThread(() -> lastButton.removeOnUnhandledKeyEventListener(listener));
   2698         mInstrumentation.waitForIdleSync();
   2699         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
   2700         assertEquals(higherInNormal, listener.mLastView);
   2701         listener.reset();
   2702 
   2703         mActivityRule.runOnUiThread(
   2704                 () -> higherInNormal.removeOnUnhandledKeyEventListener(listener));
   2705         mInstrumentation.waitForIdleSync();
   2706         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
   2707         assertEquals(lastInNormal, listener.mLastView);
   2708         listener.reset();
   2709 
   2710         // Test "capture"
   2711         mActivityRule.runOnUiThread(() -> lastInNormal.requestFocus());
   2712         mInstrumentation.waitForIdleSync();
   2713         lastInNormal.setOnKeyListener((v, keyCode, event)
   2714                 -> (keyCode == KeyEvent.KEYCODE_B && event.getAction() == KeyEvent.ACTION_UP));
   2715         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
   2716         assertTrue(listener.fired()); // checks that both up and down were received
   2717         listener.reset();
   2718     }
   2719 
   2720     @Test
   2721     public void testWindowVisibilityChanged() throws Throwable {
   2722         final MockView mockView = new MockView(mActivity);
   2723         final ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
   2724 
   2725         mActivityRule.runOnUiThread(() -> viewGroup.addView(mockView));
   2726         mInstrumentation.waitForIdleSync();
   2727         assertTrue(mockView.hasCalledOnWindowVisibilityChanged());
   2728 
   2729         mockView.reset();
   2730         mActivityRule.runOnUiThread(() -> mActivity.setVisible(false));
   2731         mInstrumentation.waitForIdleSync();
   2732         assertTrue(mockView.hasCalledDispatchWindowVisibilityChanged());
   2733         assertTrue(mockView.hasCalledOnWindowVisibilityChanged());
   2734 
   2735         mockView.reset();
   2736         mActivityRule.runOnUiThread(() -> mActivity.setVisible(true));
   2737         mInstrumentation.waitForIdleSync();
   2738         assertTrue(mockView.hasCalledDispatchWindowVisibilityChanged());
   2739         assertTrue(mockView.hasCalledOnWindowVisibilityChanged());
   2740 
   2741         mockView.reset();
   2742         mActivityRule.runOnUiThread(() -> viewGroup.removeView(mockView));
   2743         mInstrumentation.waitForIdleSync();
   2744         assertTrue(mockView.hasCalledOnWindowVisibilityChanged());
   2745     }
   2746 
   2747     @Test
   2748     public void testGetLocalVisibleRect() throws Throwable {
   2749         final View view = mActivity.findViewById(R.id.mock_view);
   2750         Rect rect = new Rect();
   2751 
   2752         float density = view.getContext().getResources().getDisplayMetrics().density;
   2753         int size1 = (int) (100 * density + 0.5);
   2754         int size2 = (int) (75 * density + 0.5);
   2755 
   2756         assertTrue(view.getLocalVisibleRect(rect));
   2757         assertEquals(0, rect.left);
   2758         assertEquals(0, rect.top);
   2759         assertEquals(size1, rect.right);
   2760         assertEquals(size2, rect.bottom);
   2761 
   2762         final LinearLayout.LayoutParams layoutParams1 = new LinearLayout.LayoutParams(0, 300);
   2763         mActivityRule.runOnUiThread(() -> view.setLayoutParams(layoutParams1));
   2764         mInstrumentation.waitForIdleSync();
   2765         assertFalse(view.getLocalVisibleRect(rect));
   2766 
   2767         final LinearLayout.LayoutParams layoutParams2 = new LinearLayout.LayoutParams(200, -10);
   2768         mActivityRule.runOnUiThread(() -> view.setLayoutParams(layoutParams2));
   2769         mInstrumentation.waitForIdleSync();
   2770         assertFalse(view.getLocalVisibleRect(rect));
   2771 
   2772         Display display = mActivity.getWindowManager().getDefaultDisplay();
   2773         int halfWidth = display.getWidth() / 2;
   2774         int halfHeight = display.getHeight() /2;
   2775 
   2776         final LinearLayout.LayoutParams layoutParams3 =
   2777                 new LinearLayout.LayoutParams(halfWidth, halfHeight);
   2778         mActivityRule.runOnUiThread(() -> {
   2779             view.setLayoutParams(layoutParams3);
   2780             view.scrollTo(20, -30);
   2781         });
   2782         mInstrumentation.waitForIdleSync();
   2783         assertTrue(view.getLocalVisibleRect(rect));
   2784         assertEquals(20, rect.left);
   2785         assertEquals(-30, rect.top);
   2786         assertEquals(halfWidth + 20, rect.right);
   2787         assertEquals(halfHeight - 30, rect.bottom);
   2788 
   2789         try {
   2790             view.getLocalVisibleRect(null);
   2791             fail("should throw NullPointerException");
   2792         } catch (NullPointerException e) {
   2793         }
   2794     }
   2795 
   2796     @Test
   2797     public void testMergeDrawableStates() {
   2798         MockView view = new MockView(mActivity);
   2799 
   2800         int[] states = view.mergeDrawableStatesWrapper(new int[] { 0, 1, 2, 0, 0 },
   2801                 new int[] { 3 });
   2802         assertNotNull(states);
   2803         assertEquals(5, states.length);
   2804         assertEquals(0, states[0]);
   2805         assertEquals(1, states[1]);
   2806         assertEquals(2, states[2]);
   2807         assertEquals(3, states[3]);
   2808         assertEquals(0, states[4]);
   2809 
   2810         try {
   2811             view.mergeDrawableStatesWrapper(new int[] { 1, 2 }, new int[] { 3 });
   2812             fail("should throw IndexOutOfBoundsException");
   2813         } catch (IndexOutOfBoundsException e) {
   2814         }
   2815 
   2816         try {
   2817             view.mergeDrawableStatesWrapper(null, new int[] { 0 });
   2818             fail("should throw NullPointerException");
   2819         } catch (NullPointerException e) {
   2820         }
   2821 
   2822         try {
   2823             view.mergeDrawableStatesWrapper(new int [] { 0 }, null);
   2824             fail("should throw NullPointerException");
   2825         } catch (NullPointerException e) {
   2826         }
   2827     }
   2828 
   2829     @Test
   2830     public void testSaveAndRestoreHierarchyState() {
   2831         int viewId = R.id.mock_view;
   2832         MockView view = (MockView) mActivity.findViewById(viewId);
   2833         SparseArray<Parcelable> container = new SparseArray<>();
   2834         view.saveHierarchyState(container);
   2835         assertTrue(view.hasCalledDispatchSaveInstanceState());
   2836         assertTrue(view.hasCalledOnSaveInstanceState());
   2837         assertEquals(viewId, container.keyAt(0));
   2838 
   2839         view.reset();
   2840         container.put(R.id.mock_view, BaseSavedState.EMPTY_STATE);
   2841         view.restoreHierarchyState(container);
   2842         assertTrue(view.hasCalledDispatchRestoreInstanceState());
   2843         assertTrue(view.hasCalledOnRestoreInstanceState());
   2844         container.clear();
   2845         view.saveHierarchyState(container);
   2846         assertTrue(view.hasCalledDispatchSaveInstanceState());
   2847         assertTrue(view.hasCalledOnSaveInstanceState());
   2848         assertEquals(viewId, container.keyAt(0));
   2849 
   2850         container.clear();
   2851         container.put(viewId, new android.graphics.Rect());
   2852         try {
   2853             view.restoreHierarchyState(container);
   2854             fail("Parcelable state must be an AbsSaveState, should throw IllegalArgumentException");
   2855         } catch (IllegalArgumentException e) {
   2856             // expected
   2857         }
   2858 
   2859         try {
   2860             view.restoreHierarchyState(null);
   2861             fail("Cannot pass null to restoreHierarchyState(), should throw NullPointerException");
   2862         } catch (NullPointerException e) {
   2863             // expected
   2864         }
   2865 
   2866         try {
   2867             view.saveHierarchyState(null);
   2868             fail("Cannot pass null to saveHierarchyState(), should throw NullPointerException");
   2869         } catch (NullPointerException e) {
   2870             // expected
   2871         }
   2872     }
   2873 
   2874     @Test
   2875     public void testOnKeyDownOrUp() throws Throwable {
   2876         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   2877         mActivityRule.runOnUiThread(() -> {
   2878             view.setFocusable(true);
   2879             view.requestFocus();
   2880         });
   2881         mInstrumentation.waitForIdleSync();
   2882         assertTrue(view.isFocused());
   2883 
   2884         KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0);
   2885         mInstrumentation.sendKeySync(event);
   2886         assertTrue(view.hasCalledOnKeyDown());
   2887 
   2888         event = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0);
   2889         mInstrumentation.sendKeySync(event);
   2890         assertTrue(view.hasCalledOnKeyUp());
   2891 
   2892         view.reset();
   2893         assertTrue(view.isEnabled());
   2894         assertFalse(view.isClickable());
   2895         assertFalse(view.isPressed());
   2896         event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER);
   2897         mInstrumentation.sendKeySync(event);
   2898         assertFalse(view.isPressed());
   2899         assertTrue(view.hasCalledOnKeyDown());
   2900 
   2901         mActivityRule.runOnUiThread(() -> {
   2902             view.setEnabled(true);
   2903             view.setClickable(true);
   2904         });
   2905         view.reset();
   2906         View.OnClickListener listener = mock(View.OnClickListener.class);
   2907         view.setOnClickListener(listener);
   2908 
   2909         assertFalse(view.isPressed());
   2910         event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER);
   2911         mInstrumentation.sendKeySync(event);
   2912         assertTrue(view.isPressed());
   2913         assertTrue(view.hasCalledOnKeyDown());
   2914         event = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER);
   2915         mInstrumentation.sendKeySync(event);
   2916         assertFalse(view.isPressed());
   2917         assertTrue(view.hasCalledOnKeyUp());
   2918         verify(listener, times(1)).onClick(view);
   2919 
   2920         view.setPressed(false);
   2921         reset(listener);
   2922         view.reset();
   2923 
   2924         assertFalse(view.isPressed());
   2925         event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER);
   2926         mInstrumentation.sendKeySync(event);
   2927         assertTrue(view.isPressed());
   2928         assertTrue(view.hasCalledOnKeyDown());
   2929         event = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER);
   2930         mInstrumentation.sendKeySync(event);
   2931         assertFalse(view.isPressed());
   2932         assertTrue(view.hasCalledOnKeyUp());
   2933         verify(listener, times(1)).onClick(view);
   2934     }
   2935 
   2936     private void checkBounds(final ViewGroup viewGroup, final View view,
   2937             final CountDownLatch countDownLatch, final int left, final int top,
   2938             final int width, final int height) {
   2939         viewGroup.getViewTreeObserver().addOnPreDrawListener(
   2940                 new ViewTreeObserver.OnPreDrawListener() {
   2941             @Override
   2942             public boolean onPreDraw() {
   2943                 assertEquals(left, view.getLeft());
   2944                 assertEquals(top, view.getTop());
   2945                 assertEquals(width, view.getWidth());
   2946                 assertEquals(height, view.getHeight());
   2947                 countDownLatch.countDown();
   2948                 viewGroup.getViewTreeObserver().removeOnPreDrawListener(this);
   2949                 return true;
   2950             }
   2951         });
   2952     }
   2953 
   2954     @Test
   2955     public void testAddRemoveAffectsWrapContentLayout() throws Throwable {
   2956         final int childWidth = 100;
   2957         final int childHeight = 200;
   2958         final int parentHeight = 400;
   2959         final LinearLayout parent = new LinearLayout(mActivity);
   2960         ViewGroup.LayoutParams parentParams = new ViewGroup.LayoutParams(
   2961                 ViewGroup.LayoutParams.WRAP_CONTENT, parentHeight);
   2962         parent.setLayoutParams(parentParams);
   2963         final MockView child = new MockView(mActivity);
   2964         child.setBackgroundColor(Color.GREEN);
   2965         ViewGroup.LayoutParams childParams = new ViewGroup.LayoutParams(childWidth, childHeight);
   2966         child.setLayoutParams(childParams);
   2967         final ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
   2968 
   2969         // Idea:
   2970         // Add the wrap_content parent view to the hierarchy (removing other views as they
   2971         // are not needed), test that parent is 0xparentHeight
   2972         // Add the child view to the parent, test that parent has same width as child
   2973         // Remove the child view from the parent, test that parent is 0xparentHeight
   2974         final CountDownLatch countDownLatch1 = new CountDownLatch(1);
   2975         mActivityRule.runOnUiThread(() -> {
   2976             viewGroup.removeAllViews();
   2977             viewGroup.addView(parent);
   2978             checkBounds(viewGroup, parent, countDownLatch1, 0, 0, 0, parentHeight);
   2979         });
   2980         countDownLatch1.await(500, TimeUnit.MILLISECONDS);
   2981 
   2982         final CountDownLatch countDownLatch2 = new CountDownLatch(1);
   2983         mActivityRule.runOnUiThread(() -> {
   2984             parent.addView(child);
   2985             checkBounds(viewGroup, parent, countDownLatch2, 0, 0, childWidth, parentHeight);
   2986         });
   2987         countDownLatch2.await(500, TimeUnit.MILLISECONDS);
   2988 
   2989         final CountDownLatch countDownLatch3 = new CountDownLatch(1);
   2990         mActivityRule.runOnUiThread(() -> {
   2991             parent.removeView(child);
   2992             checkBounds(viewGroup, parent, countDownLatch3, 0, 0, 0, parentHeight);
   2993         });
   2994         countDownLatch3.await(500, TimeUnit.MILLISECONDS);
   2995     }
   2996 
   2997     @UiThreadTest
   2998     @Test
   2999     public void testDispatchKeyEvent() {
   3000         MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   3001         MockView mockView1 = new MockView(mActivity);
   3002         MockView mockView2 = new MockView(mActivity);
   3003         ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
   3004         viewGroup.addView(mockView1);
   3005         viewGroup.addView(mockView2);
   3006         view.setFocusable(true);
   3007         mockView1.setFocusable(true);
   3008         mockView2.setFocusable(true);
   3009 
   3010         assertFalse(view.hasCalledOnKeyDown());
   3011         assertFalse(mockView1.hasCalledOnKeyDown());
   3012         assertFalse(mockView2.hasCalledOnKeyDown());
   3013         KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0);
   3014         assertFalse(view.dispatchKeyEvent(event));
   3015         assertTrue(view.hasCalledOnKeyDown());
   3016         assertFalse(mockView1.hasCalledOnKeyDown());
   3017         assertFalse(mockView2.hasCalledOnKeyDown());
   3018 
   3019         view.reset();
   3020         mockView1.reset();
   3021         mockView2.reset();
   3022         assertFalse(view.hasCalledOnKeyDown());
   3023         assertFalse(mockView1.hasCalledOnKeyDown());
   3024         assertFalse(mockView2.hasCalledOnKeyDown());
   3025         event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0);
   3026         assertFalse(mockView1.dispatchKeyEvent(event));
   3027         assertFalse(view.hasCalledOnKeyDown());
   3028         // issue 1695243
   3029         // When the view has NOT focus, it dispatches to itself, which disobey the javadoc.
   3030         assertTrue(mockView1.hasCalledOnKeyDown());
   3031         assertFalse(mockView2.hasCalledOnKeyDown());
   3032 
   3033         assertFalse(view.hasCalledOnKeyUp());
   3034         event = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0);
   3035         assertFalse(view.dispatchKeyEvent(event));
   3036         assertTrue(view.hasCalledOnKeyUp());
   3037 
   3038         assertFalse(view.hasCalledOnKeyMultiple());
   3039         event = new KeyEvent(1, 2, KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_0, 2);
   3040         assertFalse(view.dispatchKeyEvent(event));
   3041         assertTrue(view.hasCalledOnKeyMultiple());
   3042 
   3043         try {
   3044             view.dispatchKeyEvent(null);
   3045             fail("should throw NullPointerException");
   3046         } catch (NullPointerException e) {
   3047             // expected
   3048         }
   3049 
   3050         view.reset();
   3051         event = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0);
   3052         View.OnKeyListener listener = mock(View.OnKeyListener.class);
   3053         doReturn(true).when(listener).onKey(any(), anyInt(), any());
   3054         view.setOnKeyListener(listener);
   3055         verifyZeroInteractions(listener);
   3056         assertTrue(view.dispatchKeyEvent(event));
   3057         ArgumentCaptor<KeyEvent> keyEventCaptor = ArgumentCaptor.forClass(KeyEvent.class);
   3058         verify(listener, times(1)).onKey(eq(view), eq(KeyEvent.KEYCODE_0),
   3059                 keyEventCaptor.capture());
   3060         assertEquals(KeyEvent.ACTION_UP, keyEventCaptor.getValue().getAction());
   3061         assertEquals(KeyEvent.KEYCODE_0, keyEventCaptor.getValue().getKeyCode());
   3062         assertFalse(view.hasCalledOnKeyUp());
   3063     }
   3064 
   3065     @UiThreadTest
   3066     @Test
   3067     public void testDispatchTouchEvent() {
   3068         ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
   3069         MockView mockView1 = new MockView(mActivity);
   3070         MockView mockView2 = new MockView(mActivity);
   3071         viewGroup.addView(mockView1);
   3072         viewGroup.addView(mockView2);
   3073 
   3074         int[] xy = new int[2];
   3075         mockView1.getLocationOnScreen(xy);
   3076 
   3077         final int viewWidth = mockView1.getWidth();
   3078         final int viewHeight = mockView1.getHeight();
   3079         final float x = xy[0] + viewWidth / 2.0f;
   3080         final float y = xy[1] + viewHeight / 2.0f;
   3081 
   3082         long downTime = SystemClock.uptimeMillis();
   3083         long eventTime = SystemClock.uptimeMillis();
   3084         MotionEvent event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE,
   3085                 x, y, 0);
   3086 
   3087         assertFalse(mockView1.hasCalledOnTouchEvent());
   3088         assertFalse(mockView1.dispatchTouchEvent(event));
   3089         assertTrue(mockView1.hasCalledOnTouchEvent());
   3090 
   3091         assertFalse(mockView2.hasCalledOnTouchEvent());
   3092         assertFalse(mockView2.dispatchTouchEvent(event));
   3093         // issue 1695243
   3094         // it passes the touch screen motion event down to itself even if it is not the target view.
   3095         assertTrue(mockView2.hasCalledOnTouchEvent());
   3096 
   3097         mockView1.reset();
   3098         View.OnTouchListener listener = mock(View.OnTouchListener.class);
   3099         doReturn(true).when(listener).onTouch(any(), any());
   3100         mockView1.setOnTouchListener(listener);
   3101         verifyZeroInteractions(listener);
   3102         assertTrue(mockView1.dispatchTouchEvent(event));
   3103         verify(listener, times(1)).onTouch(mockView1, event);
   3104         assertFalse(mockView1.hasCalledOnTouchEvent());
   3105     }
   3106 
   3107     /**
   3108      * Ensure two MotionEvents are equal, for the purposes of this test only.
   3109      * Only compare actions, source, and times.
   3110      * Do not compare coordinates, because the injected event has coordinates relative to
   3111      * the screen, while the event received by view will be adjusted relative to the parent.
   3112      *
   3113      * Due to event batching, if two or more input events are injected / occur between two
   3114      * consecutive vsync's, they might end up getting combined into a single MotionEvent.
   3115      * It is caller's responsibility to ensure that the events were injected with a gap that's
   3116      * larger than time between two vsyncs, in order for this function to behave predictably.
   3117      *
   3118      * Recycle both MotionEvents.
   3119      */
   3120     private static void compareAndRecycleMotionEvents(MotionEvent event1, MotionEvent event2) {
   3121         if (event1 == null && event2 == null) {
   3122             return;
   3123         }
   3124 
   3125         if (event1 == null) {
   3126             event2.recycle();
   3127             fail("Expected non-null event in first position");
   3128         }
   3129         if (event2 == null) {
   3130             event1.recycle();
   3131             fail("Expected non-null event in second position");
   3132         }
   3133 
   3134         assertEquals(event1.getAction(), event2.getAction());
   3135         assertEquals(event1.getPointerCount(), event2.getPointerCount());
   3136         assertEquals(event1.getSource(), event2.getSource());
   3137         assertEquals(event1.getDownTime(), event2.getDownTime());
   3138         // If resampling occurs, the "real" (injected) events will become historical data,
   3139         // and resampled events will be inserted into MotionEvent and returned by the standard api.
   3140         // Since the injected event should contain no history, but the event received by
   3141         // the view might, we could distinguish them. But for simplicity, only require that
   3142         // the events are close in time if historical data is present.
   3143         if (event1.getHistorySize() == 0 && event2.getHistorySize() == 0) {
   3144             assertEquals(event1.getEventTime(), event2.getEventTime());
   3145         } else {
   3146             assertEquals(event1.getEventTime(), event2.getEventTime(), 20 /*delta*/);
   3147         }
   3148 
   3149         event1.recycle();
   3150         event2.recycle();
   3151     }
   3152 
   3153     @Test
   3154     public void testOnTouchListener() {
   3155         BlockingQueue<MotionEvent> events = new LinkedBlockingQueue<>();
   3156         class TestTouchListener implements View.OnTouchListener {
   3157             @Override
   3158             public boolean onTouch(View v, MotionEvent event) {
   3159                 events.add(MotionEvent.obtain(event));
   3160                 return true;
   3161             }
   3162         }
   3163 
   3164         // Inject some touch events
   3165         TestTouchListener listener = new TestTouchListener();
   3166         View view = mActivity.findViewById(R.id.mock_view);
   3167         view.setOnTouchListener(listener);
   3168 
   3169         int[] xy = new int[2];
   3170         view.getLocationOnScreen(xy);
   3171 
   3172         final int viewWidth = view.getWidth();
   3173         final int viewHeight = view.getHeight();
   3174         final float x = xy[0] + viewWidth / 2.0f;
   3175         final float y = xy[1] + viewHeight / 2.0f;
   3176 
   3177         final long downTime = SystemClock.uptimeMillis();
   3178         MotionEvent downEvent =
   3179                 MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, x, y, 0);
   3180         downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
   3181         mInstrumentation.getUiAutomation().injectInputEvent(downEvent, true);
   3182         final long eventTime = SystemClock.uptimeMillis();
   3183         MotionEvent upEvent =
   3184                 MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
   3185         upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
   3186         mInstrumentation.getUiAutomation().injectInputEvent(upEvent, true);
   3187 
   3188         compareAndRecycleMotionEvents(downEvent, events.poll());
   3189         compareAndRecycleMotionEvents(upEvent, events.poll());
   3190         assertTrue(events.isEmpty());
   3191     }
   3192 
   3193     @Test
   3194     public void testInvalidate1() throws Throwable {
   3195         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   3196         assertTrue(view.hasCalledOnDraw());
   3197 
   3198         view.reset();
   3199         mActivityRule.runOnUiThread(view::invalidate);
   3200         mInstrumentation.waitForIdleSync();
   3201         PollingCheck.waitFor(view::hasCalledOnDraw);
   3202 
   3203         view.reset();
   3204         mActivityRule.runOnUiThread(() -> {
   3205             view.setVisibility(View.INVISIBLE);
   3206             view.invalidate();
   3207         });
   3208         mInstrumentation.waitForIdleSync();
   3209         assertFalse(view.hasCalledOnDraw());
   3210     }
   3211 
   3212     @Test
   3213     public void testInvalidate2() throws Throwable {
   3214         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   3215         assertTrue(view.hasCalledOnDraw());
   3216 
   3217         try {
   3218             view.invalidate(null);
   3219             fail("should throw NullPointerException");
   3220         } catch (NullPointerException e) {
   3221         }
   3222 
   3223         view.reset();
   3224         final Rect dirty = new Rect(view.getLeft() + 1, view.getTop() + 1,
   3225                 view.getLeft() + view.getWidth() / 2, view.getTop() + view.getHeight() / 2);
   3226         mActivityRule.runOnUiThread(() -> view.invalidate(dirty));
   3227         mInstrumentation.waitForIdleSync();
   3228         PollingCheck.waitFor(view::hasCalledOnDraw);
   3229 
   3230         view.reset();
   3231         mActivityRule.runOnUiThread(() -> {
   3232             view.setVisibility(View.INVISIBLE);
   3233             view.invalidate(dirty);
   3234         });
   3235         mInstrumentation.waitForIdleSync();
   3236         assertFalse(view.hasCalledOnDraw());
   3237     }
   3238 
   3239     @Test
   3240     public void testInvalidate3() throws Throwable {
   3241         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   3242         assertTrue(view.hasCalledOnDraw());
   3243 
   3244         view.reset();
   3245         final Rect dirty = new Rect(view.getLeft() + 1, view.getTop() + 1,
   3246                 view.getLeft() + view.getWidth() / 2, view.getTop() + view.getHeight() / 2);
   3247         mActivityRule.runOnUiThread(
   3248                 () -> view.invalidate(dirty.left, dirty.top, dirty.right, dirty.bottom));
   3249         mInstrumentation.waitForIdleSync();
   3250         PollingCheck.waitFor(view::hasCalledOnDraw);
   3251 
   3252         view.reset();
   3253         mActivityRule.runOnUiThread(() -> {
   3254             view.setVisibility(View.INVISIBLE);
   3255             view.invalidate(dirty.left, dirty.top, dirty.right, dirty.bottom);
   3256         });
   3257         mInstrumentation.waitForIdleSync();
   3258         assertFalse(view.hasCalledOnDraw());
   3259     }
   3260 
   3261     @Test
   3262     public void testInvalidateDrawable() throws Throwable {
   3263         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   3264         final Drawable d1 = mResources.getDrawable(R.drawable.scenery);
   3265         final Drawable d2 = mResources.getDrawable(R.drawable.pass);
   3266 
   3267         view.reset();
   3268         mActivityRule.runOnUiThread(() -> {
   3269             view.setBackgroundDrawable(d1);
   3270             view.invalidateDrawable(d1);
   3271         });
   3272         mInstrumentation.waitForIdleSync();
   3273         PollingCheck.waitFor(view::hasCalledOnDraw);
   3274 
   3275         view.reset();
   3276         mActivityRule.runOnUiThread(() -> view.invalidateDrawable(d2));
   3277         mInstrumentation.waitForIdleSync();
   3278         assertFalse(view.hasCalledOnDraw());
   3279 
   3280         MockView viewTestNull = new MockView(mActivity);
   3281         try {
   3282             viewTestNull.invalidateDrawable(null);
   3283             fail("should throw NullPointerException");
   3284         } catch (NullPointerException e) {
   3285         }
   3286     }
   3287 
   3288     @UiThreadTest
   3289     @Test
   3290     public void testOnFocusChanged() {
   3291         MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   3292 
   3293         mActivity.findViewById(R.id.fit_windows).setFocusable(true);
   3294         view.setFocusable(true);
   3295         assertFalse(view.hasCalledOnFocusChanged());
   3296 
   3297         view.requestFocus();
   3298         assertTrue(view.hasCalledOnFocusChanged());
   3299 
   3300         view.reset();
   3301         view.clearFocus();
   3302         assertTrue(view.hasCalledOnFocusChanged());
   3303     }
   3304 
   3305     @UiThreadTest
   3306     @Test
   3307     public void testRestoreDefaultFocus() {
   3308         MockView view = new MockView(mActivity);
   3309         view.restoreDefaultFocus();
   3310         assertTrue(view.hasCalledRequestFocus());
   3311     }
   3312 
   3313     @Test
   3314     public void testDrawableState() {
   3315         MockView view = new MockView(mActivity);
   3316         view.setParent(mMockParent);
   3317 
   3318         assertFalse(view.hasCalledOnCreateDrawableState());
   3319         assertTrue(Arrays.equals(MockView.getEnabledStateSet(), view.getDrawableState()));
   3320         assertTrue(view.hasCalledOnCreateDrawableState());
   3321 
   3322         view.reset();
   3323         assertFalse(view.hasCalledOnCreateDrawableState());
   3324         assertTrue(Arrays.equals(MockView.getEnabledStateSet(), view.getDrawableState()));
   3325         assertFalse(view.hasCalledOnCreateDrawableState());
   3326 
   3327         view.reset();
   3328         assertFalse(view.hasCalledDrawableStateChanged());
   3329         view.setPressed(true);
   3330         assertTrue(view.hasCalledDrawableStateChanged());
   3331         assertTrue(Arrays.equals(MockView.getPressedEnabledStateSet(), view.getDrawableState()));
   3332         assertTrue(view.hasCalledOnCreateDrawableState());
   3333 
   3334         view.reset();
   3335         mMockParent.reset();
   3336         assertFalse(view.hasCalledDrawableStateChanged());
   3337         assertFalse(mMockParent.hasChildDrawableStateChanged());
   3338         view.refreshDrawableState();
   3339         assertTrue(view.hasCalledDrawableStateChanged());
   3340         assertTrue(mMockParent.hasChildDrawableStateChanged());
   3341         assertTrue(Arrays.equals(MockView.getPressedEnabledStateSet(), view.getDrawableState()));
   3342         assertTrue(view.hasCalledOnCreateDrawableState());
   3343     }
   3344 
   3345     @Test
   3346     public void testWindowFocusChanged() {
   3347         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   3348 
   3349         // Wait until the window has been focused.
   3350         PollingCheck.waitFor(TIMEOUT_DELTA, view::hasWindowFocus);
   3351 
   3352         PollingCheck.waitFor(view::hasCalledOnWindowFocusChanged);
   3353 
   3354         assertTrue(view.hasCalledOnWindowFocusChanged());
   3355         assertTrue(view.hasCalledDispatchWindowFocusChanged());
   3356 
   3357         view.reset();
   3358         assertFalse(view.hasCalledOnWindowFocusChanged());
   3359         assertFalse(view.hasCalledDispatchWindowFocusChanged());
   3360 
   3361         CtsActivity activity = mCtsActivityRule.launchActivity(null);
   3362 
   3363         // Wait until the window lost focus.
   3364         PollingCheck.waitFor(TIMEOUT_DELTA, () -> !view.hasWindowFocus());
   3365 
   3366         assertTrue(view.hasCalledOnWindowFocusChanged());
   3367         assertTrue(view.hasCalledDispatchWindowFocusChanged());
   3368 
   3369         activity.finish();
   3370     }
   3371 
   3372     @Test
   3373     public void testDraw() throws Throwable {
   3374         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   3375         mActivityRule.runOnUiThread(view::requestLayout);
   3376         mInstrumentation.waitForIdleSync();
   3377 
   3378         assertTrue(view.hasCalledOnDraw());
   3379         assertTrue(view.hasCalledDispatchDraw());
   3380     }
   3381 
   3382     @Test
   3383     public void testRequestFocusFromTouch() {
   3384         View view = new View(mActivity);
   3385         view.setFocusable(true);
   3386         assertFalse(view.isFocused());
   3387 
   3388         view.requestFocusFromTouch();
   3389         assertTrue(view.isFocused());
   3390 
   3391         view.requestFocusFromTouch();
   3392         assertTrue(view.isFocused());
   3393     }
   3394 
   3395     @Test
   3396     public void testRequestRectangleOnScreen1() {
   3397         MockView view = new MockView(mActivity);
   3398         Rect rectangle = new Rect(10, 10, 20, 30);
   3399         MockViewGroupParent parent = new MockViewGroupParent(mActivity);
   3400 
   3401         // parent is null
   3402         assertFalse(view.requestRectangleOnScreen(rectangle, true));
   3403         assertFalse(view.requestRectangleOnScreen(rectangle, false));
   3404         assertFalse(view.requestRectangleOnScreen(null, true));
   3405 
   3406         view.setParent(parent);
   3407         view.scrollTo(1, 2);
   3408         assertFalse(parent.hasRequestChildRectangleOnScreen());
   3409 
   3410         assertFalse(view.requestRectangleOnScreen(rectangle, true));
   3411         assertTrue(parent.hasRequestChildRectangleOnScreen());
   3412 
   3413         parent.reset();
   3414         view.scrollTo(11, 22);
   3415         assertFalse(parent.hasRequestChildRectangleOnScreen());
   3416 
   3417         assertFalse(view.requestRectangleOnScreen(rectangle, true));
   3418         assertTrue(parent.hasRequestChildRectangleOnScreen());
   3419 
   3420         try {
   3421             view.requestRectangleOnScreen(null, true);
   3422             fail("should throw NullPointerException");
   3423         } catch (NullPointerException e) {
   3424         }
   3425     }
   3426 
   3427     @Test
   3428     public void testRequestRectangleOnScreen2() {
   3429         MockView view = new MockView(mActivity);
   3430         Rect rectangle = new Rect();
   3431         MockViewGroupParent parent = new MockViewGroupParent(mActivity);
   3432 
   3433         MockViewGroupParent grandparent = new MockViewGroupParent(mActivity);
   3434 
   3435         // parent is null
   3436         assertFalse(view.requestRectangleOnScreen(rectangle));
   3437         assertFalse(view.requestRectangleOnScreen(null));
   3438         assertEquals(0, rectangle.left);
   3439         assertEquals(0, rectangle.top);
   3440         assertEquals(0, rectangle.right);
   3441         assertEquals(0, rectangle.bottom);
   3442 
   3443         parent.addView(view);
   3444         parent.scrollTo(1, 2);
   3445         grandparent.addView(parent);
   3446 
   3447         assertFalse(parent.hasRequestChildRectangleOnScreen());
   3448         assertFalse(grandparent.hasRequestChildRectangleOnScreen());
   3449 
   3450         assertFalse(view.requestRectangleOnScreen(rectangle));
   3451 
   3452         assertTrue(parent.hasRequestChildRectangleOnScreen());
   3453         assertTrue(grandparent.hasRequestChildRectangleOnScreen());
   3454 
   3455         // it is grand parent's responsibility to check parent's scroll offset
   3456         final Rect requestedRect = grandparent.getLastRequestedChildRectOnScreen();
   3457         assertEquals(0, requestedRect.left);
   3458         assertEquals(0, requestedRect.top);
   3459         assertEquals(0, requestedRect.right);
   3460         assertEquals(0, requestedRect.bottom);
   3461 
   3462         try {
   3463             view.requestRectangleOnScreen(null);
   3464             fail("should throw NullPointerException");
   3465         } catch (NullPointerException e) {
   3466         }
   3467     }
   3468 
   3469     @Test
   3470     public void testRequestRectangleOnScreen3() {
   3471         requestRectangleOnScreenTest(false);
   3472     }
   3473 
   3474     @Test
   3475     public void testRequestRectangleOnScreen4() {
   3476         requestRectangleOnScreenTest(true);
   3477     }
   3478 
   3479     @Test
   3480     public void testRequestRectangleOnScreen5() {
   3481         MockView child = new MockView(mActivity);
   3482 
   3483         MockViewGroupParent parent = new MockViewGroupParent(mActivity);
   3484         MockViewGroupParent grandParent = new MockViewGroupParent(mActivity);
   3485         parent.addView(child);
   3486         grandParent.addView(parent);
   3487 
   3488         child.layout(5, 6, 7, 9);
   3489         child.requestRectangleOnScreen(new Rect(10, 10, 12, 13));
   3490         assertEquals(new Rect(10, 10, 12, 13), parent.getLastRequestedChildRectOnScreen());
   3491         assertEquals(new Rect(15, 16, 17, 19), grandParent.getLastRequestedChildRectOnScreen());
   3492 
   3493         child.scrollBy(1, 2);
   3494         child.requestRectangleOnScreen(new Rect(10, 10, 12, 13));
   3495         assertEquals(new Rect(10, 10, 12, 13), parent.getLastRequestedChildRectOnScreen());
   3496         assertEquals(new Rect(14, 14, 16, 17), grandParent.getLastRequestedChildRectOnScreen());
   3497     }
   3498 
   3499     private void requestRectangleOnScreenTest(boolean scrollParent) {
   3500         MockView child = new MockView(mActivity);
   3501 
   3502         MockViewGroupParent parent = new MockViewGroupParent(mActivity);
   3503         MockViewGroupParent grandParent = new MockViewGroupParent(mActivity);
   3504         parent.addView(child);
   3505         grandParent.addView(parent);
   3506 
   3507         child.requestRectangleOnScreen(new Rect(10, 10, 12, 13));
   3508         assertEquals(new Rect(10, 10, 12, 13), parent.getLastRequestedChildRectOnScreen());
   3509         assertEquals(new Rect(10, 10, 12, 13), grandParent.getLastRequestedChildRectOnScreen());
   3510 
   3511         child.scrollBy(1, 2);
   3512         if (scrollParent) {
   3513             // should not affect anything
   3514             parent.scrollBy(25, 30);
   3515             parent.layout(3, 5, 7, 9);
   3516         }
   3517         child.requestRectangleOnScreen(new Rect(10, 10, 12, 13));
   3518         assertEquals(new Rect(10, 10, 12, 13), parent.getLastRequestedChildRectOnScreen());
   3519         assertEquals(new Rect(9, 8, 11, 11), grandParent.getLastRequestedChildRectOnScreen());
   3520     }
   3521 
   3522     @Test
   3523     public void testRequestRectangleOnScreenWithScale() {
   3524         // scale should not affect the rectangle
   3525         MockView child = new MockView(mActivity);
   3526         child.setScaleX(2);
   3527         child.setScaleX(3);
   3528         MockViewGroupParent parent = new MockViewGroupParent(mActivity);
   3529         MockViewGroupParent grandParent = new MockViewGroupParent(mActivity);
   3530         parent.addView(child);
   3531         grandParent.addView(parent);
   3532         child.requestRectangleOnScreen(new Rect(10, 10, 12, 13));
   3533         assertEquals(new Rect(10, 10, 12, 13), parent.getLastRequestedChildRectOnScreen());
   3534         assertEquals(new Rect(10, 10, 12, 13), grandParent.getLastRequestedChildRectOnScreen());
   3535     }
   3536 
   3537     /**
   3538      * For the duration of the tap timeout we are in a 'prepressed' state
   3539      * to differentiate between taps and touch scrolls.
   3540      * Wait at least this long before testing if the view is pressed
   3541      * by calling this function.
   3542      */
   3543     private void waitPrepressedTimeout() {
   3544         try {
   3545             Thread.sleep(ViewConfiguration.getTapTimeout() + 10);
   3546         } catch (InterruptedException e) {
   3547             Log.e(LOG_TAG, "waitPrepressedTimeout() interrupted! Test may fail!", e);
   3548         }
   3549         mInstrumentation.waitForIdleSync();
   3550     }
   3551 
   3552     @Test
   3553     public void testOnTouchEventTap() {
   3554         final MockView view = mActivity.findViewById(R.id.mock_view);
   3555 
   3556         assertTrue(view.isEnabled());
   3557         assertFalse(view.isClickable());
   3558         assertFalse(view.isLongClickable());
   3559 
   3560         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, view);
   3561         assertTrue(view.hasCalledOnTouchEvent());
   3562     }
   3563 
   3564 
   3565     @Test
   3566     public void testOnTouchEventScroll() throws Throwable {
   3567         final MockView view = mActivity.findViewById(R.id.mock_view);
   3568 
   3569         mActivityRule.runOnUiThread(() -> {
   3570             view.setEnabled(true);
   3571             view.setClickable(true);
   3572             view.setLongClickable(true);
   3573         });
   3574         mInstrumentation.waitForIdleSync();
   3575         assertTrue(view.isEnabled());
   3576         assertTrue(view.isClickable());
   3577         assertTrue(view.isLongClickable());
   3578 
   3579         // MotionEvent.ACTION_DOWN
   3580         int[] xy = new int[2];
   3581         view.getLocationOnScreen(xy);
   3582 
   3583         final int viewWidth = view.getWidth();
   3584         final int viewHeight = view.getHeight();
   3585         float x = xy[0] + viewWidth / 2.0f;
   3586         float y = xy[1] + viewHeight / 2.0f;
   3587 
   3588         long downTime = SystemClock.uptimeMillis();
   3589         MotionEvent event = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN,
   3590                 x, y, 0);
   3591         assertFalse(view.isPressed());
   3592         mInstrumentation.sendPointerSync(event);
   3593         waitPrepressedTimeout();
   3594         compareAndRecycleMotionEvents(event, view.pollTouchEvent());
   3595         assertTrue(view.isPressed());
   3596 
   3597         // MotionEvent.ACTION_MOVE
   3598         // move out of the bound.
   3599         view.reset();
   3600         long eventTime = SystemClock.uptimeMillis();
   3601         final int slop = ViewConfiguration.get(mActivity).getScaledTouchSlop();
   3602         x = xy[0] + viewWidth + slop;
   3603         y = xy[1] + viewHeight + slop;
   3604         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
   3605         mInstrumentation.sendPointerSync(event);
   3606         compareAndRecycleMotionEvents(event, view.pollTouchEvent());
   3607         assertFalse(view.isPressed());
   3608 
   3609         // move into view
   3610         view.reset();
   3611         eventTime = SystemClock.uptimeMillis();
   3612         x = xy[0] + viewWidth - 1;
   3613         y = xy[1] + viewHeight - 1;
   3614         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
   3615         SystemClock.sleep(20); // prevent event batching
   3616         mInstrumentation.sendPointerSync(event);
   3617         waitPrepressedTimeout();
   3618         compareAndRecycleMotionEvents(event, view.pollTouchEvent());
   3619         assertFalse(view.isPressed());
   3620 
   3621         // MotionEvent.ACTION_UP
   3622         View.OnClickListener listener = mock(View.OnClickListener.class);
   3623         view.setOnClickListener(listener);
   3624         view.reset();
   3625         eventTime = SystemClock.uptimeMillis();
   3626         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
   3627         mInstrumentation.sendPointerSync(event);
   3628         compareAndRecycleMotionEvents(event, view.pollTouchEvent());
   3629         verifyZeroInteractions(listener);
   3630 
   3631         view.reset();
   3632         x = xy[0] + viewWidth / 2.0f;
   3633         y = xy[1] + viewHeight / 2.0f;
   3634         downTime = SystemClock.uptimeMillis();
   3635         event = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, x, y, 0);
   3636         mInstrumentation.sendPointerSync(event);
   3637         compareAndRecycleMotionEvents(event, view.pollTouchEvent());
   3638 
   3639         // MotionEvent.ACTION_CANCEL
   3640         view.reset();
   3641         reset(listener);
   3642         eventTime = SystemClock.uptimeMillis();
   3643         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_CANCEL, x, y, 0);
   3644         mInstrumentation.sendPointerSync(event);
   3645         compareAndRecycleMotionEvents(event, view.pollTouchEvent());
   3646         assertFalse(view.isPressed());
   3647         verifyZeroInteractions(listener);
   3648     }
   3649 
   3650     @Test
   3651     public void testBringToFront() {
   3652         MockView view = new MockView(mActivity);
   3653         view.setParent(mMockParent);
   3654 
   3655         assertFalse(mMockParent.hasBroughtChildToFront());
   3656         view.bringToFront();
   3657         assertTrue(mMockParent.hasBroughtChildToFront());
   3658     }
   3659 
   3660     @Test
   3661     public void testGetApplicationWindowToken() {
   3662         View view = new View(mActivity);
   3663         // mAttachInfo is null
   3664         assertNull(view.getApplicationWindowToken());
   3665 
   3666         // mAttachInfo is not null
   3667         view = mActivity.findViewById(R.id.fit_windows);
   3668         assertNotNull(view.getApplicationWindowToken());
   3669     }
   3670 
   3671     @Test
   3672     public void testGetBottomPaddingOffset() {
   3673         MockView view = new MockView(mActivity);
   3674         assertEquals(0, view.getBottomPaddingOffset());
   3675     }
   3676 
   3677     @Test
   3678     public void testGetLeftPaddingOffset() {
   3679         MockView view = new MockView(mActivity);
   3680         assertEquals(0, view.getLeftPaddingOffset());
   3681     }
   3682 
   3683     @Test
   3684     public void testGetRightPaddingOffset() {
   3685         MockView view = new MockView(mActivity);
   3686         assertEquals(0, view.getRightPaddingOffset());
   3687     }
   3688 
   3689     @Test
   3690     public void testGetTopPaddingOffset() {
   3691         MockView view = new MockView(mActivity);
   3692         assertEquals(0, view.getTopPaddingOffset());
   3693     }
   3694 
   3695     @Test
   3696     public void testIsPaddingOffsetRequired() {
   3697         MockView view = new MockView(mActivity);
   3698         assertFalse(view.isPaddingOffsetRequired());
   3699     }
   3700 
   3701     @UiThreadTest
   3702     @Test
   3703     public void testPadding() {
   3704         MockView view = (MockView) mActivity.findViewById(R.id.mock_view_padding_full);
   3705         Drawable background = view.getBackground();
   3706         Rect backgroundPadding = new Rect();
   3707         background.getPadding(backgroundPadding);
   3708 
   3709         // There is some background with a non null padding
   3710         assertNotNull(background);
   3711         assertTrue(backgroundPadding.left != 0);
   3712         assertTrue(backgroundPadding.right != 0);
   3713         assertTrue(backgroundPadding.top != 0);
   3714         assertTrue(backgroundPadding.bottom != 0);
   3715 
   3716         // The XML defines android:padding="0dp" and that should be the resulting padding
   3717         assertEquals(0, view.getPaddingLeft());
   3718         assertEquals(0, view.getPaddingTop());
   3719         assertEquals(0, view.getPaddingRight());
   3720         assertEquals(0, view.getPaddingBottom());
   3721 
   3722         // LEFT case
   3723         view = (MockView) mActivity.findViewById(R.id.mock_view_padding_left);
   3724         background = view.getBackground();
   3725         backgroundPadding = new Rect();
   3726         background.getPadding(backgroundPadding);
   3727 
   3728         // There is some background with a non null padding
   3729         assertNotNull(background);
   3730         assertTrue(backgroundPadding.left != 0);
   3731         assertTrue(backgroundPadding.right != 0);
   3732         assertTrue(backgroundPadding.top != 0);
   3733         assertTrue(backgroundPadding.bottom != 0);
   3734 
   3735         // The XML defines android:paddingLeft="0dp" and that should be the resulting padding
   3736         assertEquals(0, view.getPaddingLeft());
   3737         assertEquals(backgroundPadding.top, view.getPaddingTop());
   3738         assertEquals(backgroundPadding.right, view.getPaddingRight());
   3739         assertEquals(backgroundPadding.bottom, view.getPaddingBottom());
   3740 
   3741         // RIGHT case
   3742         view = (MockView) mActivity.findViewById(R.id.mock_view_padding_right);
   3743         background = view.getBackground();
   3744         backgroundPadding = new Rect();
   3745         background.getPadding(backgroundPadding);
   3746 
   3747         // There is some background with a non null padding
   3748         assertNotNull(background);
   3749         assertTrue(backgroundPadding.left != 0);
   3750         assertTrue(backgroundPadding.right != 0);
   3751         assertTrue(backgroundPadding.top != 0);
   3752         assertTrue(backgroundPadding.bottom != 0);
   3753 
   3754         // The XML defines android:paddingRight="0dp" and that should be the resulting padding
   3755         assertEquals(backgroundPadding.left, view.getPaddingLeft());
   3756         assertEquals(backgroundPadding.top, view.getPaddingTop());
   3757         assertEquals(0, view.getPaddingRight());
   3758         assertEquals(backgroundPadding.bottom, view.getPaddingBottom());
   3759 
   3760         // TOP case
   3761         view = (MockView) mActivity.findViewById(R.id.mock_view_padding_top);
   3762         background = view.getBackground();
   3763         backgroundPadding = new Rect();
   3764         background.getPadding(backgroundPadding);
   3765 
   3766         // There is some background with a non null padding
   3767         assertNotNull(background);
   3768         assertTrue(backgroundPadding.left != 0);
   3769         assertTrue(backgroundPadding.right != 0);
   3770         assertTrue(backgroundPadding.top != 0);
   3771         assertTrue(backgroundPadding.bottom != 0);
   3772 
   3773         // The XML defines android:paddingTop="0dp" and that should be the resulting padding
   3774         assertEquals(backgroundPadding.left, view.getPaddingLeft());
   3775         assertEquals(0, view.getPaddingTop());
   3776         assertEquals(backgroundPadding.right, view.getPaddingRight());
   3777         assertEquals(backgroundPadding.bottom, view.getPaddingBottom());
   3778 
   3779         // BOTTOM case
   3780         view = (MockView) mActivity.findViewById(R.id.mock_view_padding_bottom);
   3781         background = view.getBackground();
   3782         backgroundPadding = new Rect();
   3783         background.getPadding(backgroundPadding);
   3784 
   3785         // There is some background with a non null padding
   3786         assertNotNull(background);
   3787         assertTrue(backgroundPadding.left != 0);
   3788         assertTrue(backgroundPadding.right != 0);
   3789         assertTrue(backgroundPadding.top != 0);
   3790         assertTrue(backgroundPadding.bottom != 0);
   3791 
   3792         // The XML defines android:paddingBottom="0dp" and that should be the resulting padding
   3793         assertEquals(backgroundPadding.left, view.getPaddingLeft());
   3794         assertEquals(backgroundPadding.top, view.getPaddingTop());
   3795         assertEquals(backgroundPadding.right, view.getPaddingRight());
   3796         assertEquals(0, view.getPaddingBottom());
   3797 
   3798         // Case for interleaved background/padding changes
   3799         view = (MockView) mActivity.findViewById(R.id.mock_view_padding_runtime_updated);
   3800         background = view.getBackground();
   3801         backgroundPadding = new Rect();
   3802         background.getPadding(backgroundPadding);
   3803 
   3804         // There is some background with a null padding
   3805         assertNotNull(background);
   3806         assertTrue(backgroundPadding.left == 0);
   3807         assertTrue(backgroundPadding.right == 0);
   3808         assertTrue(backgroundPadding.top == 0);
   3809         assertTrue(backgroundPadding.bottom == 0);
   3810 
   3811         final int paddingLeft = view.getPaddingLeft();
   3812         final int paddingRight = view.getPaddingRight();
   3813         final int paddingTop = view.getPaddingTop();
   3814         final int paddingBottom = view.getPaddingBottom();
   3815         assertEquals(8, paddingLeft);
   3816         assertEquals(0, paddingTop);
   3817         assertEquals(8, paddingRight);
   3818         assertEquals(0, paddingBottom);
   3819 
   3820         // Manipulate background and padding
   3821         background.setState(view.getDrawableState());
   3822         background.jumpToCurrentState();
   3823         view.setBackground(background);
   3824         view.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
   3825 
   3826         assertEquals(8, view.getPaddingLeft());
   3827         assertEquals(0, view.getPaddingTop());
   3828         assertEquals(8, view.getPaddingRight());
   3829         assertEquals(0, view.getPaddingBottom());
   3830     }
   3831 
   3832     @Test
   3833     public void testGetWindowVisibleDisplayFrame() {
   3834         Rect outRect = new Rect();
   3835         View view = new View(mActivity);
   3836         // mAttachInfo is null
   3837         DisplayManager dm = (DisplayManager) mActivity.getApplicationContext().getSystemService(
   3838                 Context.DISPLAY_SERVICE);
   3839         Display d = dm.getDisplay(Display.DEFAULT_DISPLAY);
   3840         view.getWindowVisibleDisplayFrame(outRect);
   3841         assertEquals(0, outRect.left);
   3842         assertEquals(0, outRect.top);
   3843         assertEquals(d.getWidth(), outRect.right);
   3844         assertEquals(d.getHeight(), outRect.bottom);
   3845 
   3846         // mAttachInfo is not null
   3847         outRect = new Rect();
   3848         view = mActivity.findViewById(R.id.fit_windows);
   3849         // it's implementation detail
   3850         view.getWindowVisibleDisplayFrame(outRect);
   3851     }
   3852 
   3853     @Test
   3854     public void testSetScrollContainer() throws Throwable {
   3855         final MockView mockView = (MockView) mActivity.findViewById(R.id.mock_view);
   3856         final MockView scrollView = (MockView) mActivity.findViewById(R.id.scroll_view);
   3857         Bitmap bitmap = Bitmap.createBitmap(200, 300, Bitmap.Config.RGB_565);
   3858         final BitmapDrawable d = new BitmapDrawable(bitmap);
   3859         final InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(
   3860                 Context.INPUT_METHOD_SERVICE);
   3861         final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(300, 500);
   3862         mActivityRule.runOnUiThread(() -> {
   3863             mockView.setBackgroundDrawable(d);
   3864             mockView.setHorizontalFadingEdgeEnabled(true);
   3865             mockView.setVerticalFadingEdgeEnabled(true);
   3866             mockView.setLayoutParams(layoutParams);
   3867             scrollView.setLayoutParams(layoutParams);
   3868 
   3869             mockView.setFocusable(true);
   3870             mockView.requestFocus();
   3871             mockView.setScrollContainer(true);
   3872             scrollView.setScrollContainer(false);
   3873             imm.showSoftInput(mockView, 0);
   3874         });
   3875         mInstrumentation.waitForIdleSync();
   3876 
   3877         // FIXME: why the size of view doesn't change?
   3878 
   3879         mActivityRule.runOnUiThread(
   3880                 () -> imm.hideSoftInputFromInputMethod(mockView.getWindowToken(), 0));
   3881         mInstrumentation.waitForIdleSync();
   3882     }
   3883 
   3884     @Test
   3885     public void testTouchMode() throws Throwable {
   3886         final MockView mockView = (MockView) mActivity.findViewById(R.id.mock_view);
   3887         final View fitWindowsView = mActivity.findViewById(R.id.fit_windows);
   3888         mActivityRule.runOnUiThread(() -> {
   3889             mockView.setFocusableInTouchMode(true);
   3890             fitWindowsView.setFocusable(true);
   3891             fitWindowsView.requestFocus();
   3892         });
   3893         mInstrumentation.waitForIdleSync();
   3894         assertTrue(mockView.isFocusableInTouchMode());
   3895         assertFalse(fitWindowsView.isFocusableInTouchMode());
   3896         assertTrue(mockView.isFocusable());
   3897         assertTrue(fitWindowsView.isFocusable());
   3898         assertFalse(mockView.isFocused());
   3899         assertTrue(fitWindowsView.isFocused());
   3900         assertFalse(mockView.isInTouchMode());
   3901         assertFalse(fitWindowsView.isInTouchMode());
   3902 
   3903         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mockView);
   3904         assertFalse(fitWindowsView.isFocused());
   3905         assertFalse(mockView.isFocused());
   3906         mActivityRule.runOnUiThread(mockView::requestFocus);
   3907         mInstrumentation.waitForIdleSync();
   3908         assertTrue(mockView.isFocused());
   3909         mActivityRule.runOnUiThread(fitWindowsView::requestFocus);
   3910         mInstrumentation.waitForIdleSync();
   3911         assertFalse(fitWindowsView.isFocused());
   3912         assertTrue(mockView.isInTouchMode());
   3913         assertTrue(fitWindowsView.isInTouchMode());
   3914 
   3915         KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0);
   3916         mInstrumentation.sendKeySync(keyEvent);
   3917         assertTrue(mockView.isFocused());
   3918         assertFalse(fitWindowsView.isFocused());
   3919         mActivityRule.runOnUiThread(fitWindowsView::requestFocus);
   3920         mInstrumentation.waitForIdleSync();
   3921         assertFalse(mockView.isFocused());
   3922         assertTrue(fitWindowsView.isFocused());
   3923         assertFalse(mockView.isInTouchMode());
   3924         assertFalse(fitWindowsView.isInTouchMode());
   3925 
   3926         // Mouse events should not trigger touch mode.
   3927         final MotionEvent event =
   3928                 CtsMouseUtil.obtainMouseEvent(MotionEvent.ACTION_SCROLL, mockView, 0, 0);
   3929         mInstrumentation.sendPointerSync(event);
   3930         assertFalse(fitWindowsView.isInTouchMode());
   3931 
   3932         event.setAction(MotionEvent.ACTION_DOWN);
   3933         mInstrumentation.sendPointerSync(event);
   3934         assertFalse(fitWindowsView.isInTouchMode());
   3935 
   3936         // Stylus events should not trigger touch mode.
   3937         event.setSource(InputDevice.SOURCE_STYLUS);
   3938         mInstrumentation.sendPointerSync(event);
   3939         assertFalse(fitWindowsView.isInTouchMode());
   3940 
   3941         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mockView);
   3942         assertTrue(fitWindowsView.isInTouchMode());
   3943 
   3944         event.setSource(InputDevice.SOURCE_MOUSE);
   3945         event.setAction(MotionEvent.ACTION_DOWN);
   3946         mInstrumentation.sendPointerSync(event);
   3947         assertFalse(fitWindowsView.isInTouchMode());
   3948     }
   3949 
   3950     @UiThreadTest
   3951     @Test
   3952     public void testScrollbarStyle() {
   3953         MockView view = (MockView) mActivity.findViewById(R.id.scroll_view);
   3954         Bitmap bitmap = Bitmap.createBitmap(200, 300, Bitmap.Config.RGB_565);
   3955         BitmapDrawable d = new BitmapDrawable(bitmap);
   3956         view.setBackgroundDrawable(d);
   3957         view.setHorizontalFadingEdgeEnabled(true);
   3958         view.setVerticalFadingEdgeEnabled(true);
   3959 
   3960         assertTrue(view.isHorizontalScrollBarEnabled());
   3961         assertTrue(view.isVerticalScrollBarEnabled());
   3962         int verticalScrollBarWidth = view.getVerticalScrollbarWidth();
   3963         int horizontalScrollBarHeight = view.getHorizontalScrollbarHeight();
   3964         assertTrue(verticalScrollBarWidth > 0);
   3965         assertTrue(horizontalScrollBarHeight > 0);
   3966         assertEquals(0, view.getPaddingRight());
   3967         assertEquals(0, view.getPaddingBottom());
   3968 
   3969         view.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
   3970         assertEquals(View.SCROLLBARS_INSIDE_INSET, view.getScrollBarStyle());
   3971         assertEquals(verticalScrollBarWidth, view.getPaddingRight());
   3972         assertEquals(horizontalScrollBarHeight, view.getPaddingBottom());
   3973 
   3974         view.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
   3975         assertEquals(View.SCROLLBARS_OUTSIDE_OVERLAY, view.getScrollBarStyle());
   3976         assertEquals(0, view.getPaddingRight());
   3977         assertEquals(0, view.getPaddingBottom());
   3978 
   3979         view.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);
   3980         assertEquals(View.SCROLLBARS_OUTSIDE_INSET, view.getScrollBarStyle());
   3981         assertEquals(verticalScrollBarWidth, view.getPaddingRight());
   3982         assertEquals(horizontalScrollBarHeight, view.getPaddingBottom());
   3983 
   3984         // TODO: how to get the position of the Scrollbar to assert it is inside or outside.
   3985     }
   3986 
   3987     @UiThreadTest
   3988     @Test
   3989     public void testScrollFading() {
   3990         MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   3991         Bitmap bitmap = Bitmap.createBitmap(200, 300, Bitmap.Config.RGB_565);
   3992         BitmapDrawable d = new BitmapDrawable(bitmap);
   3993         view.setBackgroundDrawable(d);
   3994 
   3995         assertFalse(view.isHorizontalFadingEdgeEnabled());
   3996         assertFalse(view.isVerticalFadingEdgeEnabled());
   3997         assertEquals(0, view.getHorizontalFadingEdgeLength());
   3998         assertEquals(0, view.getVerticalFadingEdgeLength());
   3999 
   4000         view.setHorizontalFadingEdgeEnabled(true);
   4001         view.setVerticalFadingEdgeEnabled(true);
   4002         assertTrue(view.isHorizontalFadingEdgeEnabled());
   4003         assertTrue(view.isVerticalFadingEdgeEnabled());
   4004         assertTrue(view.getHorizontalFadingEdgeLength() > 0);
   4005         assertTrue(view.getVerticalFadingEdgeLength() > 0);
   4006 
   4007         final int fadingLength = 20;
   4008         view.setFadingEdgeLength(fadingLength);
   4009         assertEquals(fadingLength, view.getHorizontalFadingEdgeLength());
   4010         assertEquals(fadingLength, view.getVerticalFadingEdgeLength());
   4011     }
   4012 
   4013     @UiThreadTest
   4014     @Test
   4015     public void testScrolling() {
   4016         MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   4017         view.reset();
   4018         assertEquals(0, view.getScrollX());
   4019         assertEquals(0, view.getScrollY());
   4020         assertFalse(view.hasCalledOnScrollChanged());
   4021 
   4022         view.scrollTo(0, 0);
   4023         assertEquals(0, view.getScrollX());
   4024         assertEquals(0, view.getScrollY());
   4025         assertFalse(view.hasCalledOnScrollChanged());
   4026 
   4027         view.scrollBy(0, 0);
   4028         assertEquals(0, view.getScrollX());
   4029         assertEquals(0, view.getScrollY());
   4030         assertFalse(view.hasCalledOnScrollChanged());
   4031 
   4032         view.scrollTo(10, 100);
   4033         assertEquals(10, view.getScrollX());
   4034         assertEquals(100, view.getScrollY());
   4035         assertTrue(view.hasCalledOnScrollChanged());
   4036 
   4037         view.reset();
   4038         assertFalse(view.hasCalledOnScrollChanged());
   4039         view.scrollBy(-10, -100);
   4040         assertEquals(0, view.getScrollX());
   4041         assertEquals(0, view.getScrollY());
   4042         assertTrue(view.hasCalledOnScrollChanged());
   4043 
   4044         view.reset();
   4045         assertFalse(view.hasCalledOnScrollChanged());
   4046         view.scrollTo(-1, -2);
   4047         assertEquals(-1, view.getScrollX());
   4048         assertEquals(-2, view.getScrollY());
   4049         assertTrue(view.hasCalledOnScrollChanged());
   4050     }
   4051 
   4052     @Test
   4053     public void testInitializeScrollbarsAndFadingEdge() {
   4054         MockView view = (MockView) mActivity.findViewById(R.id.scroll_view);
   4055 
   4056         assertTrue(view.isHorizontalScrollBarEnabled());
   4057         assertTrue(view.isVerticalScrollBarEnabled());
   4058         assertFalse(view.isHorizontalFadingEdgeEnabled());
   4059         assertFalse(view.isVerticalFadingEdgeEnabled());
   4060 
   4061         view = (MockView) mActivity.findViewById(R.id.scroll_view_2);
   4062         final int fadingEdgeLength = 20;
   4063 
   4064         assertTrue(view.isHorizontalScrollBarEnabled());
   4065         assertTrue(view.isVerticalScrollBarEnabled());
   4066         assertTrue(view.isHorizontalFadingEdgeEnabled());
   4067         assertTrue(view.isVerticalFadingEdgeEnabled());
   4068         assertEquals(fadingEdgeLength, view.getHorizontalFadingEdgeLength());
   4069         assertEquals(fadingEdgeLength, view.getVerticalFadingEdgeLength());
   4070     }
   4071 
   4072     @UiThreadTest
   4073     @Test
   4074     public void testScrollIndicators() {
   4075         MockView view = (MockView) mActivity.findViewById(R.id.scroll_view);
   4076 
   4077         assertEquals("Set indicators match those specified in XML",
   4078                 View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM,
   4079                 view.getScrollIndicators());
   4080 
   4081         view.setScrollIndicators(0);
   4082         assertEquals("Cleared indicators", 0, view.getScrollIndicators());
   4083 
   4084         view.setScrollIndicators(View.SCROLL_INDICATOR_START | View.SCROLL_INDICATOR_RIGHT);
   4085         assertEquals("Set start and right indicators",
   4086                 View.SCROLL_INDICATOR_START | View.SCROLL_INDICATOR_RIGHT,
   4087                 view.getScrollIndicators());
   4088 
   4089     }
   4090 
   4091     @Test
   4092     public void testScrollbarSize() {
   4093         final int configScrollbarSize = ViewConfiguration.get(mActivity).getScaledScrollBarSize();
   4094         final int customScrollbarSize = configScrollbarSize * 2;
   4095 
   4096         // No explicit scrollbarSize or custom drawables, ViewConfiguration applies.
   4097         final MockView view = (MockView) mActivity.findViewById(R.id.scroll_view);
   4098         assertEquals(configScrollbarSize, view.getScrollBarSize());
   4099         assertEquals(configScrollbarSize, view.getVerticalScrollbarWidth());
   4100         assertEquals(configScrollbarSize, view.getHorizontalScrollbarHeight());
   4101 
   4102         // No custom drawables, explicit scrollbarSize takes precedence.
   4103         final MockView view2 = (MockView) mActivity.findViewById(R.id.scroll_view_2);
   4104         view2.setScrollBarSize(customScrollbarSize);
   4105         assertEquals(customScrollbarSize, view2.getScrollBarSize());
   4106         assertEquals(customScrollbarSize, view2.getVerticalScrollbarWidth());
   4107         assertEquals(customScrollbarSize, view2.getHorizontalScrollbarHeight());
   4108 
   4109         // Custom drawables with no intrinsic size, ViewConfiguration applies.
   4110         final MockView view3 = (MockView) mActivity.findViewById(R.id.scroll_view_3);
   4111         assertEquals(configScrollbarSize, view3.getVerticalScrollbarWidth());
   4112         assertEquals(configScrollbarSize, view3.getHorizontalScrollbarHeight());
   4113         // Explicit scrollbarSize takes precedence.
   4114         view3.setScrollBarSize(customScrollbarSize);
   4115         assertEquals(view3.getScrollBarSize(), view3.getVerticalScrollbarWidth());
   4116         assertEquals(view3.getScrollBarSize(), view3.getHorizontalScrollbarHeight());
   4117 
   4118         // Custom thumb drawables with intrinsic sizes define the scrollbars' dimensions.
   4119         final MockView view4 = (MockView) mActivity.findViewById(R.id.scroll_view_4);
   4120         final Resources res = mActivity.getResources();
   4121         final int thumbWidth = res.getDimensionPixelSize(R.dimen.scrollbar_thumb_width);
   4122         final int thumbHeight = res.getDimensionPixelSize(R.dimen.scrollbar_thumb_height);
   4123         assertEquals(thumbWidth, view4.getVerticalScrollbarWidth());
   4124         assertEquals(thumbHeight, view4.getHorizontalScrollbarHeight());
   4125         // Explicit scrollbarSize has no effect.
   4126         view4.setScrollBarSize(customScrollbarSize);
   4127         assertEquals(thumbWidth, view4.getVerticalScrollbarWidth());
   4128         assertEquals(thumbHeight, view4.getHorizontalScrollbarHeight());
   4129 
   4130         // Custom thumb and track drawables with intrinsic sizes. Track size take precedence.
   4131         final MockView view5 = (MockView) mActivity.findViewById(R.id.scroll_view_5);
   4132         final int trackWidth = res.getDimensionPixelSize(R.dimen.scrollbar_track_width);
   4133         final int trackHeight = res.getDimensionPixelSize(R.dimen.scrollbar_track_height);
   4134         assertEquals(trackWidth, view5.getVerticalScrollbarWidth());
   4135         assertEquals(trackHeight, view5.getHorizontalScrollbarHeight());
   4136         // Explicit scrollbarSize has no effect.
   4137         view5.setScrollBarSize(customScrollbarSize);
   4138         assertEquals(trackWidth, view5.getVerticalScrollbarWidth());
   4139         assertEquals(trackHeight, view5.getHorizontalScrollbarHeight());
   4140 
   4141         // Custom thumb and track, track with no intrinsic size, ViewConfiguration applies
   4142         // regardless of the thumb drawable dimensions.
   4143         final MockView view6 = (MockView) mActivity.findViewById(R.id.scroll_view_6);
   4144         assertEquals(configScrollbarSize, view6.getVerticalScrollbarWidth());
   4145         assertEquals(configScrollbarSize, view6.getHorizontalScrollbarHeight());
   4146         // Explicit scrollbarSize takes precedence.
   4147         view6.setScrollBarSize(customScrollbarSize);
   4148         assertEquals(customScrollbarSize, view6.getVerticalScrollbarWidth());
   4149         assertEquals(customScrollbarSize, view6.getHorizontalScrollbarHeight());
   4150     }
   4151 
   4152     @Test
   4153     public void testOnStartAndFinishTemporaryDetach() throws Throwable {
   4154         final AtomicBoolean exitedDispatchStartTemporaryDetach = new AtomicBoolean(false);
   4155         final AtomicBoolean exitedDispatchFinishTemporaryDetach = new AtomicBoolean(false);
   4156 
   4157         final View view = new View(mActivity) {
   4158             private boolean mEnteredDispatchStartTemporaryDetach = false;
   4159             private boolean mExitedDispatchStartTemporaryDetach = false;
   4160             private boolean mEnteredDispatchFinishTemporaryDetach = false;
   4161             private boolean mExitedDispatchFinishTemporaryDetach = false;
   4162 
   4163             private boolean mCalledOnStartTemporaryDetach = false;
   4164             private boolean mCalledOnFinishTemporaryDetach = false;
   4165 
   4166             @Override
   4167             public void dispatchStartTemporaryDetach() {
   4168                 assertFalse(mEnteredDispatchStartTemporaryDetach);
   4169                 assertFalse(mExitedDispatchStartTemporaryDetach);
   4170                 assertFalse(mEnteredDispatchFinishTemporaryDetach);
   4171                 assertFalse(mExitedDispatchFinishTemporaryDetach);
   4172                 assertFalse(mCalledOnStartTemporaryDetach);
   4173                 assertFalse(mCalledOnFinishTemporaryDetach);
   4174                 mEnteredDispatchStartTemporaryDetach = true;
   4175 
   4176                 assertFalse(isTemporarilyDetached());
   4177 
   4178                 super.dispatchStartTemporaryDetach();
   4179 
   4180                 assertTrue(isTemporarilyDetached());
   4181 
   4182                 assertTrue(mEnteredDispatchStartTemporaryDetach);
   4183                 assertFalse(mExitedDispatchStartTemporaryDetach);
   4184                 assertFalse(mEnteredDispatchFinishTemporaryDetach);
   4185                 assertFalse(mExitedDispatchFinishTemporaryDetach);
   4186                 assertTrue(mCalledOnStartTemporaryDetach);
   4187                 assertFalse(mCalledOnFinishTemporaryDetach);
   4188                 mExitedDispatchStartTemporaryDetach = true;
   4189                 exitedDispatchStartTemporaryDetach.set(true);
   4190             }
   4191 
   4192             @Override
   4193             public void dispatchFinishTemporaryDetach() {
   4194                 assertTrue(mEnteredDispatchStartTemporaryDetach);
   4195                 assertTrue(mExitedDispatchStartTemporaryDetach);
   4196                 assertFalse(mEnteredDispatchFinishTemporaryDetach);
   4197                 assertFalse(mExitedDispatchFinishTemporaryDetach);
   4198                 assertTrue(mCalledOnStartTemporaryDetach);
   4199                 assertFalse(mCalledOnFinishTemporaryDetach);
   4200                 mEnteredDispatchFinishTemporaryDetach = true;
   4201 
   4202                 assertTrue(isTemporarilyDetached());
   4203 
   4204                 super.dispatchFinishTemporaryDetach();
   4205 
   4206                 assertFalse(isTemporarilyDetached());
   4207 
   4208                 assertTrue(mEnteredDispatchStartTemporaryDetach);
   4209                 assertTrue(mExitedDispatchStartTemporaryDetach);
   4210                 assertTrue(mEnteredDispatchFinishTemporaryDetach);
   4211                 assertFalse(mExitedDispatchFinishTemporaryDetach);
   4212                 assertTrue(mCalledOnStartTemporaryDetach);
   4213                 assertTrue(mCalledOnFinishTemporaryDetach);
   4214                 mExitedDispatchFinishTemporaryDetach = true;
   4215                 exitedDispatchFinishTemporaryDetach.set(true);
   4216             }
   4217 
   4218             @Override
   4219             public void onStartTemporaryDetach() {
   4220                 assertTrue(mEnteredDispatchStartTemporaryDetach);
   4221                 assertFalse(mExitedDispatchStartTemporaryDetach);
   4222                 assertFalse(mEnteredDispatchFinishTemporaryDetach);
   4223                 assertFalse(mExitedDispatchFinishTemporaryDetach);
   4224                 assertFalse(mCalledOnStartTemporaryDetach);
   4225                 assertFalse(mCalledOnFinishTemporaryDetach);
   4226 
   4227                 assertTrue(isTemporarilyDetached());
   4228 
   4229                 mCalledOnStartTemporaryDetach = true;
   4230             }
   4231 
   4232             @Override
   4233             public void onFinishTemporaryDetach() {
   4234                 assertTrue(mEnteredDispatchStartTemporaryDetach);
   4235                 assertTrue(mExitedDispatchStartTemporaryDetach);
   4236                 assertTrue(mEnteredDispatchFinishTemporaryDetach);
   4237                 assertFalse(mExitedDispatchFinishTemporaryDetach);
   4238                 assertTrue(mCalledOnStartTemporaryDetach);
   4239                 assertFalse(mCalledOnFinishTemporaryDetach);
   4240 
   4241                 assertFalse(isTemporarilyDetached());
   4242 
   4243                 mCalledOnFinishTemporaryDetach = true;
   4244             }
   4245         };
   4246 
   4247         assertFalse(view.isTemporarilyDetached());
   4248 
   4249         mActivityRule.runOnUiThread(view::dispatchStartTemporaryDetach);
   4250         mInstrumentation.waitForIdleSync();
   4251 
   4252         assertTrue(view.isTemporarilyDetached());
   4253         assertTrue(exitedDispatchStartTemporaryDetach.get());
   4254         assertFalse(exitedDispatchFinishTemporaryDetach.get());
   4255 
   4256         mActivityRule.runOnUiThread(view::dispatchFinishTemporaryDetach);
   4257         mInstrumentation.waitForIdleSync();
   4258 
   4259         assertFalse(view.isTemporarilyDetached());
   4260         assertTrue(exitedDispatchStartTemporaryDetach.get());
   4261         assertTrue(exitedDispatchFinishTemporaryDetach.get());
   4262     }
   4263 
   4264     @Test
   4265     public void testKeyPreIme() throws Throwable {
   4266         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   4267 
   4268         mActivityRule.runOnUiThread(() -> {
   4269             view.setFocusable(true);
   4270             view.requestFocus();
   4271         });
   4272         mInstrumentation.waitForIdleSync();
   4273 
   4274         mInstrumentation.sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
   4275         assertTrue(view.hasCalledDispatchKeyEventPreIme());
   4276         assertTrue(view.hasCalledOnKeyPreIme());
   4277     }
   4278 
   4279     @Test
   4280     public void testHapticFeedback() {
   4281         Vibrator vib = (Vibrator) mActivity.getSystemService(Context.VIBRATOR_SERVICE);
   4282         boolean hasVibrator = vib.hasVibrator();
   4283 
   4284         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   4285         final int LONG_PRESS = HapticFeedbackConstants.LONG_PRESS;
   4286         final int FLAG_IGNORE_VIEW_SETTING = HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING;
   4287         final int FLAG_IGNORE_GLOBAL_SETTING = HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING;
   4288         final int ALWAYS = FLAG_IGNORE_VIEW_SETTING | FLAG_IGNORE_GLOBAL_SETTING;
   4289 
   4290         view.setHapticFeedbackEnabled(false);
   4291         assertFalse(view.isHapticFeedbackEnabled());
   4292         assertFalse(view.performHapticFeedback(LONG_PRESS));
   4293         assertFalse(view.performHapticFeedback(LONG_PRESS, FLAG_IGNORE_GLOBAL_SETTING));
   4294         assertEquals(hasVibrator, view.performHapticFeedback(LONG_PRESS, ALWAYS));
   4295 
   4296         view.setHapticFeedbackEnabled(true);
   4297         assertTrue(view.isHapticFeedbackEnabled());
   4298         assertEquals(hasVibrator, view.performHapticFeedback(LONG_PRESS,
   4299                 FLAG_IGNORE_GLOBAL_SETTING));
   4300     }
   4301 
   4302     @Test
   4303     public void testInputConnection() throws Throwable {
   4304         final InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(
   4305                 Context.INPUT_METHOD_SERVICE);
   4306         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
   4307         final ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
   4308         final MockEditText editText = new MockEditText(mActivity);
   4309 
   4310         mActivityRule.runOnUiThread(() -> {
   4311             // Give a fixed size since, on most devices, the edittext is off-screen
   4312             // and therefore doesn't get laid-out properly.
   4313             viewGroup.addView(editText, 100, 30);
   4314             editText.requestFocus();
   4315         });
   4316         mInstrumentation.waitForIdleSync();
   4317         assertTrue(editText.isFocused());
   4318 
   4319         mActivityRule.runOnUiThread(() -> imm.showSoftInput(editText, 0));
   4320         mInstrumentation.waitForIdleSync();
   4321 
   4322         PollingCheck.waitFor(TIMEOUT_DELTA, editText::hasCalledOnCreateInputConnection);
   4323 
   4324         assertTrue(editText.hasCalledOnCheckIsTextEditor());
   4325 
   4326         mActivityRule.runOnUiThread(() -> {
   4327             assertTrue(imm.isActive(editText));
   4328             assertFalse(editText.hasCalledCheckInputConnectionProxy());
   4329             imm.isActive(view);
   4330             assertTrue(editText.hasCalledCheckInputConnectionProxy());
   4331         });
   4332     }
   4333 
   4334     @Test
   4335     public void testFilterTouchesWhenObscured() throws Throwable {
   4336         View.OnTouchListener touchListener = mock(View.OnTouchListener.class);
   4337         doReturn(true).when(touchListener).onTouch(any(), any());
   4338         View view = new View(mActivity);
   4339         view.setOnTouchListener(touchListener);
   4340 
   4341         MotionEvent.PointerProperties[] props = new MotionEvent.PointerProperties[] {
   4342                 new MotionEvent.PointerProperties()
   4343         };
   4344         MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[] {
   4345                 new MotionEvent.PointerCoords()
   4346         };
   4347         MotionEvent obscuredTouch = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN,
   4348                 1, props, coords, 0, 0, 0, 0, -1, 0, InputDevice.SOURCE_TOUCHSCREEN,
   4349                 MotionEvent.FLAG_WINDOW_IS_OBSCURED);
   4350         MotionEvent unobscuredTouch = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN,
   4351                 1, props, coords, 0, 0, 0, 0, -1, 0, InputDevice.SOURCE_TOUCHSCREEN,
   4352                 0);
   4353 
   4354         // Initially filter touches is false so all touches are dispatched.
   4355         assertFalse(view.getFilterTouchesWhenObscured());
   4356 
   4357         view.dispatchTouchEvent(unobscuredTouch);
   4358         verify(touchListener, times(1)).onTouch(view, unobscuredTouch);
   4359         reset(touchListener);
   4360         view.dispatchTouchEvent(obscuredTouch);
   4361         verify(touchListener, times(1)).onTouch(view, obscuredTouch);
   4362         reset(touchListener);
   4363 
   4364         // Set filter touches to true so only unobscured touches are dispatched.
   4365         view.setFilterTouchesWhenObscured(true);
   4366         assertTrue(view.getFilterTouchesWhenObscured());
   4367 
   4368         view.dispatchTouchEvent(unobscuredTouch);
   4369         verify(touchListener, times(1)).onTouch(view, unobscuredTouch);
   4370         reset(touchListener);
   4371         view.dispatchTouchEvent(obscuredTouch);
   4372         verifyZeroInteractions(touchListener);
   4373         reset(touchListener);
   4374 
   4375         // Set filter touches to false so all touches are dispatched.
   4376         view.setFilterTouchesWhenObscured(false);
   4377         assertFalse(view.getFilterTouchesWhenObscured());
   4378 
   4379         view.dispatchTouchEvent(unobscuredTouch);
   4380         verify(touchListener, times(1)).onTouch(view, unobscuredTouch);
   4381         reset(touchListener);
   4382         view.dispatchTouchEvent(obscuredTouch);
   4383         verify(touchListener, times(1)).onTouch(view, obscuredTouch);
   4384         reset(touchListener);
   4385     }
   4386 
   4387     @Test
   4388     public void testBackgroundTint() {
   4389         View inflatedView = mActivity.findViewById(R.id.background_tint);
   4390 
   4391         assertEquals("Background tint inflated correctly",
   4392                 Color.WHITE, inflatedView.getBackgroundTintList().getDefaultColor());
   4393         assertEquals("Background tint mode inflated correctly",
   4394                 PorterDuff.Mode.SRC_OVER, inflatedView.getBackgroundTintMode());
   4395 
   4396         MockDrawable bg = new MockDrawable();
   4397         View view = new View(mActivity);
   4398 
   4399         view.setBackground(bg);
   4400         assertFalse("No background tint applied by default", bg.hasCalledSetTint());
   4401 
   4402         view.setBackgroundTintList(ColorStateList.valueOf(Color.WHITE));
   4403         assertTrue("Background tint applied when setBackgroundTints() called after setBackground()",
   4404                 bg.hasCalledSetTint());
   4405 
   4406         bg.reset();
   4407         view.setBackground(null);
   4408         view.setBackground(bg);
   4409         assertTrue("Background tint applied when setBackgroundTints() called before setBackground()",
   4410                 bg.hasCalledSetTint());
   4411     }
   4412 
   4413     @Test
   4414     public void testStartActionModeWithParent() {
   4415         View view = new View(mActivity);
   4416         MockViewGroup parent = new MockViewGroup(mActivity);
   4417         parent.addView(view);
   4418 
   4419         ActionMode mode = view.startActionMode(null);
   4420 
   4421         assertNotNull(mode);
   4422         assertEquals(NO_OP_ACTION_MODE, mode);
   4423         assertTrue(parent.isStartActionModeForChildCalled);
   4424         assertEquals(ActionMode.TYPE_PRIMARY, parent.startActionModeForChildType);
   4425     }
   4426 
   4427     @Test
   4428     public void testStartActionModeWithoutParent() {
   4429         View view = new View(mActivity);
   4430 
   4431         ActionMode mode = view.startActionMode(null);
   4432 
   4433         assertNull(mode);
   4434     }
   4435 
   4436     @Test
   4437     public void testStartActionModeTypedWithParent() {
   4438         View view = new View(mActivity);
   4439         MockViewGroup parent = new MockViewGroup(mActivity);
   4440         parent.addView(view);
   4441 
   4442         ActionMode mode = view.startActionMode(null, ActionMode.TYPE_FLOATING);
   4443 
   4444         assertNotNull(mode);
   4445         assertEquals(NO_OP_ACTION_MODE, mode);
   4446         assertTrue(parent.isStartActionModeForChildCalled);
   4447         assertEquals(ActionMode.TYPE_FLOATING, parent.startActionModeForChildType);
   4448     }
   4449 
   4450     @Test
   4451     public void testStartActionModeTypedWithoutParent() {
   4452         View view = new View(mActivity);
   4453 
   4454         ActionMode mode = view.startActionMode(null, ActionMode.TYPE_FLOATING);
   4455 
   4456         assertNull(mode);
   4457     }
   4458 
   4459     @Test
   4460     public void testVisibilityAggregated() throws Throwable {
   4461         final View grandparent = mActivity.findViewById(R.id.viewlayout_root);
   4462         final View parent = mActivity.findViewById(R.id.aggregate_visibility_parent);
   4463         final MockView mv = (MockView) mActivity.findViewById(R.id.mock_view_aggregate_visibility);
   4464 
   4465         assertEquals(parent, mv.getParent());
   4466         assertEquals(grandparent, parent.getParent());
   4467 
   4468         assertTrue(mv.hasCalledOnVisibilityAggregated());
   4469         assertTrue(mv.getLastAggregatedVisibility());
   4470 
   4471         final Runnable reset = () -> {
   4472             grandparent.setVisibility(View.VISIBLE);
   4473             parent.setVisibility(View.VISIBLE);
   4474             mv.setVisibility(View.VISIBLE);
   4475             mv.reset();
   4476         };
   4477 
   4478         mActivityRule.runOnUiThread(reset);
   4479 
   4480         setVisibilityOnUiThread(parent, View.GONE);
   4481 
   4482         assertTrue(mv.hasCalledOnVisibilityAggregated());
   4483         assertFalse(mv.getLastAggregatedVisibility());
   4484 
   4485         mActivityRule.runOnUiThread(reset);
   4486 
   4487         setVisibilityOnUiThread(grandparent, View.GONE);
   4488 
   4489         assertTrue(mv.hasCalledOnVisibilityAggregated());
   4490         assertFalse(mv.getLastAggregatedVisibility());
   4491 
   4492         mActivityRule.runOnUiThread(reset);
   4493         mActivityRule.runOnUiThread(() -> {
   4494             grandparent.setVisibility(View.GONE);
   4495             parent.setVisibility(View.GONE);
   4496             mv.setVisibility(View.VISIBLE);
   4497 
   4498             grandparent.setVisibility(View.VISIBLE);
   4499         });
   4500 
   4501         assertTrue(mv.hasCalledOnVisibilityAggregated());
   4502         assertFalse(mv.getLastAggregatedVisibility());
   4503 
   4504         mActivityRule.runOnUiThread(reset);
   4505         mActivityRule.runOnUiThread(() -> {
   4506             grandparent.setVisibility(View.GONE);
   4507             parent.setVisibility(View.INVISIBLE);
   4508 
   4509             grandparent.setVisibility(View.VISIBLE);
   4510         });
   4511 
   4512         assertTrue(mv.hasCalledOnVisibilityAggregated());
   4513         assertFalse(mv.getLastAggregatedVisibility());
   4514 
   4515         mActivityRule.runOnUiThread(() -> parent.setVisibility(View.VISIBLE));
   4516 
   4517         assertTrue(mv.getLastAggregatedVisibility());
   4518     }
   4519 
   4520     @Test
   4521     public void testOverlappingRendering() {
   4522         View overlappingUnsetView = mActivity.findViewById(R.id.overlapping_rendering_unset);
   4523         View overlappingFalseView = mActivity.findViewById(R.id.overlapping_rendering_false);
   4524         View overlappingTrueView = mActivity.findViewById(R.id.overlapping_rendering_true);
   4525 
   4526         assertTrue(overlappingUnsetView.hasOverlappingRendering());
   4527         assertTrue(overlappingUnsetView.getHasOverlappingRendering());
   4528         overlappingUnsetView.forceHasOverlappingRendering(false);
   4529         assertTrue(overlappingUnsetView.hasOverlappingRendering());
   4530         assertFalse(overlappingUnsetView.getHasOverlappingRendering());
   4531         overlappingUnsetView.forceHasOverlappingRendering(true);
   4532         assertTrue(overlappingUnsetView.hasOverlappingRendering());
   4533         assertTrue(overlappingUnsetView.getHasOverlappingRendering());
   4534 
   4535         assertTrue(overlappingTrueView.hasOverlappingRendering());
   4536         assertTrue(overlappingTrueView.getHasOverlappingRendering());
   4537 
   4538         assertTrue(overlappingFalseView.hasOverlappingRendering());
   4539         assertFalse(overlappingFalseView.getHasOverlappingRendering());
   4540 
   4541         View overridingView = new MockOverlappingRenderingSubclass(mActivity, false);
   4542         assertFalse(overridingView.hasOverlappingRendering());
   4543 
   4544         overridingView = new MockOverlappingRenderingSubclass(mActivity, true);
   4545         assertTrue(overridingView.hasOverlappingRendering());
   4546         overridingView.forceHasOverlappingRendering(false);
   4547         assertFalse(overridingView.getHasOverlappingRendering());
   4548         assertTrue(overridingView.hasOverlappingRendering());
   4549         overridingView.forceHasOverlappingRendering(true);
   4550         assertTrue(overridingView.getHasOverlappingRendering());
   4551         assertTrue(overridingView.hasOverlappingRendering());
   4552     }
   4553 
   4554     private boolean startDragAndDrop(View view, View.DragShadowBuilder shadowBuilder) {
   4555         final Point size = new Point();
   4556         mActivity.getWindowManager().getDefaultDisplay().getSize(size);
   4557         final MotionEvent event = MotionEvent.obtain(
   4558                 SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
   4559                 MotionEvent.ACTION_DOWN, size.x / 2, size.y / 2, 1);
   4560         event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
   4561         mInstrumentation.getUiAutomation().injectInputEvent(event, true);
   4562 
   4563         return view.startDragAndDrop(ClipData.newPlainText("", ""), shadowBuilder, view, 0);
   4564     }
   4565 
   4566     private static View.DragShadowBuilder createDragShadowBuidler() {
   4567         View.DragShadowBuilder shadowBuilder = mock(View.DragShadowBuilder.class);
   4568         doAnswer(a -> {
   4569             final Point outPoint = (Point) a.getArguments()[0];
   4570             outPoint.x = 1;
   4571             outPoint.y = 1;
   4572             return null;
   4573         }).when(shadowBuilder).onProvideShadowMetrics(any(), any());
   4574         return shadowBuilder;
   4575     }
   4576 
   4577     @Test
   4578     public void testUpdateDragShadow() {
   4579         View view = mActivity.findViewById(R.id.fit_windows);
   4580         assertTrue(view.isAttachedToWindow());
   4581 
   4582         final View.DragShadowBuilder shadowBuilder = createDragShadowBuidler();
   4583         try {
   4584             assertTrue("Could not start drag and drop", startDragAndDrop(view, shadowBuilder));
   4585             reset(shadowBuilder);
   4586             view.updateDragShadow(shadowBuilder);
   4587             // TODO: Verify with the canvas from the drag surface instead.
   4588             verify(shadowBuilder).onDrawShadow(any(Canvas.class));
   4589         } finally {
   4590             // Ensure to cancel drag and drop operation so that it does not affect other tests.
   4591             view.cancelDragAndDrop();
   4592         }
   4593     }
   4594 
   4595     @Test
   4596     public void testUpdateDragShadow_detachedView() {
   4597         View view = new View(mActivity);
   4598         assertFalse(view.isAttachedToWindow());
   4599 
   4600         View.DragShadowBuilder shadowBuilder = createDragShadowBuidler();
   4601         try {
   4602             assertFalse("Drag and drop for detached view must fail",
   4603                     startDragAndDrop(view, shadowBuilder));
   4604             reset(shadowBuilder);
   4605 
   4606             view.updateDragShadow(shadowBuilder);
   4607             verify(shadowBuilder, never()).onDrawShadow(any(Canvas.class));
   4608         } finally {
   4609             // Ensure to cancel drag and drop operation so that it does not affect other tests.
   4610             view.cancelDragAndDrop();
   4611         }
   4612     }
   4613 
   4614     @Test
   4615     public void testUpdateDragShadow_noActiveDrag() {
   4616         View view = mActivity.findViewById(R.id.fit_windows);
   4617         assertTrue(view.isAttachedToWindow());
   4618 
   4619         View.DragShadowBuilder shadowBuilder = createDragShadowBuidler();
   4620         view.updateDragShadow(shadowBuilder);
   4621         verify(shadowBuilder, never()).onDrawShadow(any(Canvas.class));
   4622     }
   4623 
   4624     private void setVisibilityOnUiThread(final View view, final int visibility) throws Throwable {
   4625         mActivityRule.runOnUiThread(() -> view.setVisibility(visibility));
   4626     }
   4627 
   4628     private static class MockOverlappingRenderingSubclass extends View {
   4629         boolean mOverlap;
   4630 
   4631         public MockOverlappingRenderingSubclass(Context context, boolean overlap) {
   4632             super(context);
   4633             mOverlap = overlap;
   4634         }
   4635 
   4636         @Override
   4637         public boolean hasOverlappingRendering() {
   4638             return mOverlap;
   4639         }
   4640     }
   4641 
   4642     private static class MockViewGroup extends ViewGroup {
   4643         boolean isStartActionModeForChildCalled = false;
   4644         int startActionModeForChildType = ActionMode.TYPE_PRIMARY;
   4645 
   4646         public MockViewGroup(Context context) {
   4647             super(context);
   4648         }
   4649 
   4650         @Override
   4651         public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
   4652             isStartActionModeForChildCalled = true;
   4653             startActionModeForChildType = ActionMode.TYPE_PRIMARY;
   4654             return NO_OP_ACTION_MODE;
   4655         }
   4656 
   4657         @Override
   4658         public ActionMode startActionModeForChild(
   4659                 View originalView, ActionMode.Callback callback, int type) {
   4660             isStartActionModeForChildCalled = true;
   4661             startActionModeForChildType = type;
   4662             return NO_OP_ACTION_MODE;
   4663         }
   4664 
   4665         @Override
   4666         protected void onLayout(boolean changed, int l, int t, int r, int b) {
   4667             // no-op
   4668         }
   4669     }
   4670 
   4671     private static final ActionMode NO_OP_ACTION_MODE =
   4672             new ActionMode() {
   4673                 @Override
   4674                 public void setTitle(CharSequence title) {}
   4675 
   4676                 @Override
   4677                 public void setTitle(int resId) {}
   4678 
   4679                 @Override
   4680                 public void setSubtitle(CharSequence subtitle) {}
   4681 
   4682                 @Override
   4683                 public void setSubtitle(int resId) {}
   4684 
   4685                 @Override
   4686                 public void setCustomView(View view) {}
   4687 
   4688                 @Override
   4689                 public void invalidate() {}
   4690 
   4691                 @Override
   4692                 public void finish() {}
   4693 
   4694                 @Override
   4695                 public Menu getMenu() {
   4696                     return null;
   4697                 }
   4698 
   4699                 @Override
   4700                 public CharSequence getTitle() {
   4701                     return null;
   4702                 }
   4703 
   4704                 @Override
   4705                 public CharSequence getSubtitle() {
   4706                     return null;
   4707                 }
   4708 
   4709                 @Override
   4710                 public View getCustomView() {
   4711                     return null;
   4712                 }
   4713 
   4714                 @Override
   4715                 public MenuInflater getMenuInflater() {
   4716                     return null;
   4717                 }
   4718             };
   4719 
   4720     @Test
   4721     public void testTranslationSetter() {
   4722         View view = new View(mActivity);
   4723         float offset = 10.0f;
   4724         view.setTranslationX(offset);
   4725         view.setTranslationY(offset);
   4726         view.setTranslationZ(offset);
   4727         view.setElevation(offset);
   4728 
   4729         assertEquals("Incorrect translationX", offset, view.getTranslationX(), 0.0f);
   4730         assertEquals("Incorrect translationY", offset, view.getTranslationY(), 0.0f);
   4731         assertEquals("Incorrect translationZ", offset, view.getTranslationZ(), 0.0f);
   4732         assertEquals("Incorrect elevation", offset, view.getElevation(), 0.0f);
   4733     }
   4734 
   4735     @Test
   4736     public void testXYZ() {
   4737         View view = new View(mActivity);
   4738         float offset = 10.0f;
   4739         float start = 15.0f;
   4740         view.setTranslationX(offset);
   4741         view.setLeft((int) start);
   4742         view.setTranslationY(offset);
   4743         view.setTop((int) start);
   4744         view.setTranslationZ(offset);
   4745         view.setElevation(start);
   4746 
   4747         assertEquals("Incorrect X value", offset + start, view.getX(), 0.0f);
   4748         assertEquals("Incorrect Y value", offset + start, view.getY(), 0.0f);
   4749         assertEquals("Incorrect Z value", offset + start, view.getZ(), 0.0f);
   4750     }
   4751 
   4752     @Test
   4753     public void testOnHoverEvent() {
   4754         MotionEvent event;
   4755 
   4756         View view = new View(mActivity);
   4757         long downTime = SystemClock.uptimeMillis();
   4758 
   4759         // Preconditions.
   4760         assertFalse(view.isHovered());
   4761         assertFalse(view.isClickable());
   4762         assertTrue(view.isEnabled());
   4763 
   4764         // Simulate an ENTER/EXIT pair on a non-clickable view.
   4765         event = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
   4766         view.onHoverEvent(event);
   4767         assertFalse(view.isHovered());
   4768         event.recycle();
   4769 
   4770         event = MotionEvent.obtain(downTime, downTime + 10, MotionEvent.ACTION_HOVER_EXIT, 0, 0, 0);
   4771         view.onHoverEvent(event);
   4772         assertFalse(view.isHovered());
   4773         event.recycle();
   4774 
   4775         // Simulate an ENTER/EXIT pair on a clickable view.
   4776         view.setClickable(true);
   4777 
   4778         event = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
   4779         view.onHoverEvent(event);
   4780         assertTrue(view.isHovered());
   4781         event.recycle();
   4782 
   4783         event = MotionEvent.obtain(downTime, downTime + 10, MotionEvent.ACTION_HOVER_EXIT, 0, 0, 0);
   4784         view.onHoverEvent(event);
   4785         assertFalse(view.isHovered());
   4786         event.recycle();
   4787 
   4788         // Simulate an ENTER, then disable the view and simulate EXIT.
   4789         event = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
   4790         view.onHoverEvent(event);
   4791         assertTrue(view.isHovered());
   4792         event.recycle();
   4793 
   4794         view.setEnabled(false);
   4795 
   4796         event = MotionEvent.obtain(downTime, downTime + 10, MotionEvent.ACTION_HOVER_EXIT, 0, 0, 0);
   4797         view.onHoverEvent(event);
   4798         assertFalse(view.isHovered());
   4799         event.recycle();
   4800     }
   4801 
   4802     @Test(expected = IllegalArgumentException.class)
   4803     public void testScaleXNaN() {
   4804         View view = new View(mContext);
   4805         view.setScaleX(Float.NaN);
   4806     }
   4807 
   4808     @Test(expected = IllegalArgumentException.class)
   4809     public void testScaleXPositiveInfinity() {
   4810         View view = new View(mContext);
   4811         view.setScaleX(Float.POSITIVE_INFINITY);
   4812     }
   4813 
   4814     @Test(expected = IllegalArgumentException.class)
   4815     public void testScaleXNegativeInfinity() {
   4816         View view = new View(mContext);
   4817         view.setScaleX(Float.NEGATIVE_INFINITY);
   4818     }
   4819 
   4820     @Test(expected = IllegalArgumentException.class)
   4821     public void testScaleYNaN() {
   4822         View view = new View(mContext);
   4823         view.setScaleY(Float.NaN);
   4824     }
   4825 
   4826     @Test(expected = IllegalArgumentException.class)
   4827     public void testScaleYPositiveInfinity() {
   4828         View view = new View(mContext);
   4829         view.setScaleY(Float.POSITIVE_INFINITY);
   4830     }
   4831 
   4832     @Test(expected = IllegalArgumentException.class)
   4833     public void testScaleYNegativeInfinity() {
   4834         View view = new View(mContext);
   4835         view.setScaleY(Float.NEGATIVE_INFINITY);
   4836     }
   4837 
   4838     @Test
   4839     public void testTransitionAlpha() {
   4840         View view = new View(mContext);
   4841         view.setAlpha(1f);
   4842         view.setTransitionAlpha(0.5f);
   4843 
   4844         assertEquals(1f, view.getAlpha(), 0.0001f);
   4845         assertEquals(0.5f, view.getTransitionAlpha(), 0.0001f);
   4846     }
   4847 
   4848     @Test
   4849     public void testSetGetOutlineShadowColor() {
   4850         ViewGroup group = (ViewGroup) LayoutInflater.from(mContext).inflate(
   4851                 R.layout.view_outlineshadowcolor, null);
   4852         View defaultShadow = group.findViewById(R.id.default_shadow);
   4853         assertEquals(Color.BLACK, defaultShadow.getOutlineSpotShadowColor());
   4854         assertEquals(Color.BLACK, defaultShadow.getOutlineAmbientShadowColor());
   4855         defaultShadow.setOutlineSpotShadowColor(Color.YELLOW);
   4856         defaultShadow.setOutlineAmbientShadowColor(Color.GREEN);
   4857         assertEquals(Color.YELLOW, defaultShadow.getOutlineSpotShadowColor());
   4858         assertEquals(Color.GREEN, defaultShadow.getOutlineAmbientShadowColor());
   4859 
   4860         View redAmbientShadow = group.findViewById(R.id.red_shadow);
   4861         assertEquals(Color.RED, redAmbientShadow.getOutlineAmbientShadowColor());
   4862         assertEquals(Color.BLACK, redAmbientShadow.getOutlineSpotShadowColor());
   4863 
   4864         View blueSpotShadow = group.findViewById(R.id.blue_shadow);
   4865         assertEquals(Color.BLUE, blueSpotShadow.getOutlineSpotShadowColor());
   4866         assertEquals(Color.BLACK, blueSpotShadow.getOutlineAmbientShadowColor());
   4867 
   4868         View greenShadow = group.findViewById(R.id.green_shadow);
   4869         assertEquals(Color.GREEN, greenShadow.getOutlineSpotShadowColor());
   4870         assertEquals(Color.GREEN, greenShadow.getOutlineAmbientShadowColor());
   4871     }
   4872 
   4873     @Test
   4874     public void testTransformMatrixToGlobal() {
   4875         final View view = mActivity.findViewById(R.id.transform_matrix_view);
   4876         final Matrix initialMatrix = view.getMatrix();
   4877         assertNotNull(initialMatrix);
   4878 
   4879         final Matrix newMatrix = new Matrix(initialMatrix);
   4880         float[] initialValues = new float[9];
   4881         newMatrix.getValues(initialValues);
   4882 
   4883         view.transformMatrixToGlobal(newMatrix);
   4884         float[] newValues = new float[9];
   4885         newMatrix.getValues(newValues);
   4886         int[] location = new int[2];
   4887         view.getLocationInWindow(location);
   4888         boolean hasChanged = false;
   4889         for (int i = 0; i < 9; ++i) {
   4890             if (initialValues[i] != newValues[i]) {
   4891                 hasChanged = true;
   4892             }
   4893         }
   4894         assertTrue("Matrix should be changed", hasChanged);
   4895         assertEquals("Matrix should reflect position in window",
   4896                 location[1], newValues[5], 0.001);
   4897     }
   4898 
   4899     @Test
   4900     public void testTransformMatrixToLocal() {
   4901         final View view1 = mActivity.findViewById(R.id.transform_matrix_view);
   4902         final View view2 = mActivity.findViewById(R.id.transform_matrix_view_2);
   4903         final Matrix initialMatrix = view1.getMatrix();
   4904         assertNotNull(initialMatrix);
   4905 
   4906         final Matrix globalMatrix = new Matrix(initialMatrix);
   4907 
   4908         view1.transformMatrixToGlobal(globalMatrix);
   4909         float[] globalValues = new float[9];
   4910         globalMatrix.getValues(globalValues);
   4911 
   4912         view2.transformMatrixToLocal(globalMatrix);
   4913         float[] localValues = new float[9];
   4914         globalMatrix.getValues(localValues);
   4915 
   4916         boolean hasChanged = false;
   4917         for (int i = 0; i < 9; ++i) {
   4918             if (globalValues[i] != localValues[i]) {
   4919                 hasChanged = true;
   4920             }
   4921         }
   4922         assertTrue("Matrix should be changed", hasChanged);
   4923         assertEquals("The first view should be 10px above the second view",
   4924                 -10, localValues[5], 0.001);
   4925     }
   4926 
   4927     @Test
   4928     public void testPivot() {
   4929         View view = new View(mContext);
   4930         int widthSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
   4931         int heightSpec = View.MeasureSpec.makeMeasureSpec(200, View.MeasureSpec.EXACTLY);
   4932         view.measure(widthSpec, heightSpec);
   4933         assertEquals(100, view.getMeasuredWidth());
   4934         assertEquals(200, view.getMeasuredHeight());
   4935         view.layout(0, 0, 100, 200);
   4936         assertEquals(100, view.getWidth());
   4937         assertEquals(200, view.getHeight());
   4938 
   4939         // Assert default pivot behavior
   4940         assertEquals(50, view.getPivotX(), 0.0f);
   4941         assertEquals(100, view.getPivotY(), 0.0f);
   4942         assertFalse(view.isPivotSet());
   4943 
   4944         // Assert it changes as expected
   4945         view.setPivotX(15);
   4946         assertEquals(15, view.getPivotX(), 0.0f);
   4947         assertEquals(100, view.getPivotY(), 0.0f);
   4948         assertTrue(view.isPivotSet());
   4949         view.setPivotY(0);
   4950         assertEquals(0, view.getPivotY(), 0.0f);
   4951         assertTrue(view.isPivotSet());
   4952 
   4953         // Asset resetting back to default
   4954         view.resetPivot();
   4955         assertEquals(50, view.getPivotX(), 0.0f);
   4956         assertEquals(100, view.getPivotY(), 0.0f);
   4957         assertFalse(view.isPivotSet());
   4958     }
   4959 
   4960     @Test
   4961     public void testSetLeftTopRightBottom() {
   4962         View view = new View(mContext);
   4963         view.setLeftTopRightBottom(1, 2, 3, 4);
   4964 
   4965         assertEquals(1, view.getLeft());
   4966         assertEquals(2, view.getTop());
   4967         assertEquals(3, view.getRight());
   4968         assertEquals(4, view.getBottom());
   4969     }
   4970 
   4971     @Test
   4972     public void testGetUniqueDrawingId() {
   4973         View view1 = new View(mContext);
   4974         View view2 = new View(mContext);
   4975         Set<Long> idSet = new HashSet<>(50);
   4976 
   4977         assertNotEquals(view1.getUniqueDrawingId(), view2.getUniqueDrawingId());
   4978 
   4979         for (int i = 0; i < 50; i++) {
   4980             assertTrue(idSet.add(new View(mContext).getUniqueDrawingId()));
   4981         }
   4982     }
   4983 
   4984     @Test
   4985     public void testSetVerticalScrollbarTrack() {
   4986         View view = new View(mContext);
   4987 
   4988         ColorDrawable colorDrawable = new ColorDrawable(Color.CYAN);
   4989         view.setVerticalScrollbarTrackDrawable(colorDrawable);
   4990 
   4991         Drawable verticalTrackDrawable = view.getVerticalScrollbarTrackDrawable();
   4992         assertTrue(verticalTrackDrawable instanceof ColorDrawable);
   4993         assertEquals(Color.CYAN, ((ColorDrawable) verticalTrackDrawable).getColor());
   4994     }
   4995 
   4996     @Test
   4997     public void testSetVerticalScrollbarThumb() {
   4998 
   4999         View view = new View(mContext);
   5000 
   5001         ColorDrawable colorDrawable = new ColorDrawable(Color.CYAN);
   5002         view.setVerticalScrollbarThumbDrawable(colorDrawable);
   5003 
   5004         Drawable verticalThumbDrawable = view.getVerticalScrollbarThumbDrawable();
   5005         assertTrue(verticalThumbDrawable instanceof ColorDrawable);
   5006         assertEquals(Color.CYAN, ((ColorDrawable) verticalThumbDrawable).getColor());
   5007     }
   5008 
   5009     @Test
   5010     public void testSetHorizontalScrollbarTrack() {
   5011 
   5012         View view = new View(mContext);
   5013 
   5014         ColorDrawable colorDrawable = new ColorDrawable(Color.CYAN);
   5015         view.setHorizontalScrollbarTrackDrawable(colorDrawable);
   5016 
   5017         Drawable horizontalTrackDrawable = view.getHorizontalScrollbarTrackDrawable();
   5018         assertTrue(horizontalTrackDrawable instanceof ColorDrawable);
   5019         assertEquals(Color.CYAN, ((ColorDrawable) horizontalTrackDrawable).getColor());
   5020     }
   5021 
   5022     @Test
   5023     public void testSetHorizontalScrollbarThumb() {
   5024 
   5025         View view = new View(mContext);
   5026 
   5027         ColorDrawable colorDrawable = new ColorDrawable(Color.CYAN);
   5028         view.setHorizontalScrollbarThumbDrawable(colorDrawable);
   5029 
   5030         Drawable horizontalThumbDrawable = view.getHorizontalScrollbarThumbDrawable();
   5031         assertTrue(horizontalThumbDrawable instanceof ColorDrawable);
   5032         assertEquals(Color.CYAN, ((ColorDrawable) horizontalThumbDrawable).getColor());
   5033     }
   5034 
   5035     @Test
   5036     public void testSetTransitionVisibility() {
   5037         MockView view = new MockView(mContext);
   5038         view.setVisibility(View.GONE);
   5039         view.setParent(mMockParent);
   5040         mMockParent.reset();
   5041 
   5042         // setTransitionVisibility shouldn't trigger requestLayout() on the parent
   5043         view.setTransitionVisibility(View.VISIBLE);
   5044 
   5045         assertEquals(View.VISIBLE, view.getVisibility());
   5046         assertFalse(mMockParent.hasRequestLayout());
   5047 
   5048         // Reset state
   5049         view.setVisibility(View.GONE);
   5050         mMockParent.reset();
   5051 
   5052         // setVisibility should trigger requestLayout() on the parent
   5053         view.setVisibility(View.VISIBLE);
   5054 
   5055         assertEquals(View.VISIBLE, view.getVisibility());
   5056         assertTrue(mMockParent.hasRequestLayout());
   5057     }
   5058 
   5059     private static class MockDrawable extends Drawable {
   5060         private boolean mCalledSetTint = false;
   5061 
   5062         @Override
   5063         public void draw(Canvas canvas) {}
   5064 
   5065         @Override
   5066         public void setAlpha(int alpha) {}
   5067 
   5068         @Override
   5069         public void setColorFilter(ColorFilter cf) {}
   5070 
   5071         @Override
   5072         public int getOpacity() {
   5073             return 0;
   5074         }
   5075 
   5076         @Override
   5077         public void setTintList(ColorStateList tint) {
   5078             super.setTintList(tint);
   5079             mCalledSetTint = true;
   5080         }
   5081 
   5082         public boolean hasCalledSetTint() {
   5083             return mCalledSetTint;
   5084         }
   5085 
   5086         public void reset() {
   5087             mCalledSetTint = false;
   5088         }
   5089     }
   5090 
   5091     private static class MockEditText extends EditText {
   5092         private boolean mCalledCheckInputConnectionProxy = false;
   5093         private boolean mCalledOnCreateInputConnection = false;
   5094         private boolean mCalledOnCheckIsTextEditor = false;
   5095 
   5096         public MockEditText(Context context) {
   5097             super(context);
   5098         }
   5099 
   5100         @Override
   5101         public boolean checkInputConnectionProxy(View view) {
   5102             mCalledCheckInputConnectionProxy = true;
   5103             return super.checkInputConnectionProxy(view);
   5104         }
   5105 
   5106         public boolean hasCalledCheckInputConnectionProxy() {
   5107             return mCalledCheckInputConnectionProxy;
   5108         }
   5109 
   5110         @Override
   5111         public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
   5112             mCalledOnCreateInputConnection = true;
   5113             return super.onCreateInputConnection(outAttrs);
   5114         }
   5115 
   5116         public boolean hasCalledOnCreateInputConnection() {
   5117             return mCalledOnCreateInputConnection;
   5118         }
   5119 
   5120         @Override
   5121         public boolean onCheckIsTextEditor() {
   5122             mCalledOnCheckIsTextEditor = true;
   5123             return super.onCheckIsTextEditor();
   5124         }
   5125 
   5126         public boolean hasCalledOnCheckIsTextEditor() {
   5127             return mCalledOnCheckIsTextEditor;
   5128         }
   5129 
   5130         public void reset() {
   5131             mCalledCheckInputConnectionProxy = false;
   5132             mCalledOnCreateInputConnection = false;
   5133             mCalledOnCheckIsTextEditor = false;
   5134         }
   5135     }
   5136 
   5137     private final static class MockViewParent extends ViewGroup {
   5138         private boolean mHasRequestLayout = false;
   5139         private boolean mHasCreateContextMenu = false;
   5140         private boolean mHasShowContextMenuForChild = false;
   5141         private boolean mHasShowContextMenuForChildXY = false;
   5142         private boolean mHasChildDrawableStateChanged = false;
   5143         private boolean mHasBroughtChildToFront = false;
   5144 
   5145         private final static int[] DEFAULT_PARENT_STATE_SET = new int[] { 789 };
   5146 
   5147         @Override
   5148         public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
   5149                 boolean immediate) {
   5150             return false;
   5151         }
   5152 
   5153         public MockViewParent(Context context) {
   5154             super(context);
   5155         }
   5156 
   5157         @Override
   5158         public void bringChildToFront(View child) {
   5159             mHasBroughtChildToFront = true;
   5160         }
   5161 
   5162         public boolean hasBroughtChildToFront() {
   5163             return mHasBroughtChildToFront;
   5164         }
   5165 
   5166         @Override
   5167         public void childDrawableStateChanged(View child) {
   5168             mHasChildDrawableStateChanged = true;
   5169         }
   5170 
   5171         public boolean hasChildDrawableStateChanged() {
   5172             return mHasChildDrawableStateChanged;
   5173         }
   5174 
   5175         @Override
   5176         public void dispatchSetPressed(boolean pressed) {
   5177             super.dispatchSetPressed(pressed);
   5178         }
   5179 
   5180         @Override
   5181         public void dispatchSetSelected(boolean selected) {
   5182             super.dispatchSetSelected(selected);
   5183         }
   5184 
   5185         @Override
   5186         public void clearChildFocus(View child) {
   5187 
   5188         }
   5189 
   5190         @Override
   5191         public void createContextMenu(ContextMenu menu) {
   5192             mHasCreateContextMenu = true;
   5193         }
   5194 
   5195         public boolean hasCreateContextMenu() {
   5196             return mHasCreateContextMenu;
   5197         }
   5198 
   5199         @Override
   5200         public View focusSearch(View v, int direction) {
   5201             return v;
   5202         }
   5203 
   5204         @Override
   5205         public void focusableViewAvailable(View v) {
   5206 
   5207         }
   5208 
   5209         @Override
   5210         public boolean getChildVisibleRect(View child, Rect r, Point offset) {
   5211             return false;
   5212         }
   5213 
   5214         @Override
   5215         protected void onLayout(boolean changed, int l, int t, int r, int b) {
   5216 
   5217         }
   5218 
   5219         @Override
   5220         public ViewParent invalidateChildInParent(int[] location, Rect r) {
   5221             return null;
   5222         }
   5223 
   5224         @Override
   5225         public boolean isLayoutRequested() {
   5226             return false;
   5227         }
   5228 
   5229         @Override
   5230         public void recomputeViewAttributes(View child) {
   5231 
   5232         }
   5233 
   5234         @Override
   5235         public void requestChildFocus(View child, View focused) {
   5236 
   5237         }
   5238 
   5239         @Override
   5240         public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
   5241 
   5242         }
   5243 
   5244         @Override
   5245         public void requestLayout() {
   5246             mHasRequestLayout = true;
   5247         }
   5248 
   5249         public boolean hasRequestLayout() {
   5250             return mHasRequestLayout;
   5251         }
   5252 
   5253         @Override
   5254         public void requestTransparentRegion(View child) {
   5255 
   5256         }
   5257 
   5258         @Override
   5259         public boolean showContextMenuForChild(View originalView) {
   5260             mHasShowContextMenuForChild = true;
   5261             return false;
   5262         }
   5263 
   5264         @Override
   5265         public boolean showContextMenuForChild(View originalView, float x, float y) {
   5266             mHasShowContextMenuForChildXY = true;
   5267             return false;
   5268         }
   5269 
   5270         @Override
   5271         public ActionMode startActionModeForChild(View originalView,
   5272                 ActionMode.Callback callback) {
   5273             return null;
   5274         }
   5275 
   5276         @Override
   5277         public ActionMode startActionModeForChild(View originalView,
   5278                 ActionMode.Callback callback, int type) {
   5279             return null;
   5280         }
   5281 
   5282         public boolean hasShowContextMenuForChild() {
   5283             return mHasShowContextMenuForChild;
   5284         }
   5285 
   5286         public boolean hasShowContextMenuForChildXY() {
   5287             return mHasShowContextMenuForChildXY;
   5288         }
   5289 
   5290         @Override
   5291         protected int[] onCreateDrawableState(int extraSpace) {
   5292             return DEFAULT_PARENT_STATE_SET;
   5293         }
   5294 
   5295         @Override
   5296         public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
   5297             return false;
   5298         }
   5299 
   5300         public void reset() {
   5301             mHasRequestLayout = false;
   5302             mHasCreateContextMenu = false;
   5303             mHasShowContextMenuForChild = false;
   5304             mHasShowContextMenuForChildXY = false;
   5305             mHasChildDrawableStateChanged = false;
   5306             mHasBroughtChildToFront = false;
   5307         }
   5308 
   5309         @Override
   5310         public void childHasTransientStateChanged(View child, boolean hasTransientState) {
   5311 
   5312         }
   5313 
   5314         @Override
   5315         public ViewParent getParentForAccessibility() {
   5316             return null;
   5317         }
   5318 
   5319         @Override
   5320         public void notifySubtreeAccessibilityStateChanged(View child,
   5321             View source, int changeType) {
   5322 
   5323         }
   5324 
   5325         @Override
   5326         public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
   5327             return false;
   5328         }
   5329 
   5330         @Override
   5331         public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
   5332         }
   5333 
   5334         @Override
   5335         public void onStopNestedScroll(View target) {
   5336         }
   5337 
   5338         @Override
   5339         public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
   5340                                    int dxUnconsumed, int dyUnconsumed) {
   5341         }
   5342 
   5343         @Override
   5344         public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
   5345         }
   5346 
   5347         @Override
   5348         public boolean onNestedFling(View target, float velocityX, float velocityY,
   5349                 boolean consumed) {
   5350             return false;
   5351         }
   5352 
   5353         @Override
   5354         public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
   5355             return false;
   5356         }
   5357 
   5358         @Override
   5359         public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) {
   5360             return false;
   5361         }
   5362     }
   5363 
   5364     private static class MockViewGroupParent extends ViewGroup implements ViewParent {
   5365         private boolean mHasRequestChildRectangleOnScreen = false;
   5366         private Rect mLastRequestedChildRectOnScreen = new Rect(
   5367                 Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
   5368 
   5369         public MockViewGroupParent(Context context) {
   5370             super(context);
   5371         }
   5372 
   5373         @Override
   5374         protected void onLayout(boolean changed, int l, int t, int r, int b) {
   5375 
   5376         }
   5377 
   5378         @Override
   5379         public boolean requestChildRectangleOnScreen(View child,
   5380                 Rect rectangle, boolean immediate) {
   5381             mHasRequestChildRectangleOnScreen = true;
   5382             mLastRequestedChildRectOnScreen.set(rectangle);
   5383             return super.requestChildRectangleOnScreen(child, rectangle, immediate);
   5384         }
   5385 
   5386         public Rect getLastRequestedChildRectOnScreen() {
   5387             return mLastRequestedChildRectOnScreen;
   5388         }
   5389 
   5390         public boolean hasRequestChildRectangleOnScreen() {
   5391             return mHasRequestChildRectangleOnScreen;
   5392         }
   5393 
   5394         @Override
   5395         protected void detachViewFromParent(View child) {
   5396             super.detachViewFromParent(child);
   5397         }
   5398 
   5399         public void reset() {
   5400             mHasRequestChildRectangleOnScreen = false;
   5401         }
   5402     }
   5403 
   5404     private static final class ViewData {
   5405         public int childCount;
   5406         public String tag;
   5407         public View firstChild;
   5408     }
   5409 
   5410     private static class MockUnhandledKeyListener implements OnUnhandledKeyEventListener {
   5411         public View mLastView = null;
   5412         public boolean mGotUp = false;
   5413         public boolean mReturnVal = false;
   5414 
   5415         @Override
   5416         public boolean onUnhandledKeyEvent(View v, KeyEvent event) {
   5417             if (event.getAction() == KeyEvent.ACTION_DOWN) {
   5418                 mLastView = v;
   5419             } else if (event.getAction() == KeyEvent.ACTION_UP) {
   5420                 mGotUp = true;
   5421             }
   5422             return mReturnVal;
   5423         }
   5424         public void reset() {
   5425             mLastView = null;
   5426             mGotUp = false;
   5427         }
   5428         public boolean fired() {
   5429             return mLastView != null && mGotUp;
   5430         }
   5431     }
   5432 
   5433     public static class ScrollTestView extends View {
   5434         public ScrollTestView(Context context) {
   5435             super(context);
   5436         }
   5437 
   5438         @Override
   5439         public boolean awakenScrollBars() {
   5440             return super.awakenScrollBars();
   5441         }
   5442 
   5443         @Override
   5444         public int computeHorizontalScrollRange() {
   5445             return super.computeHorizontalScrollRange();
   5446         }
   5447 
   5448         @Override
   5449         public int computeHorizontalScrollExtent() {
   5450             return super.computeHorizontalScrollExtent();
   5451         }
   5452 
   5453         @Override
   5454         public int computeVerticalScrollRange() {
   5455             return super.computeVerticalScrollRange();
   5456         }
   5457 
   5458         @Override
   5459         public int computeVerticalScrollExtent() {
   5460             return super.computeVerticalScrollExtent();
   5461         }
   5462 
   5463         @Override
   5464         protected int getHorizontalScrollbarHeight() {
   5465             return super.getHorizontalScrollbarHeight();
   5466         }
   5467     }
   5468 
   5469     private static final Class<?> ASYNC_INFLATE_VIEWS[] = {
   5470         android.app.FragmentBreadCrumbs.class,
   5471 // DISABLED because it doesn't have a AppWidgetHostView(Context, AttributeSet)
   5472 // constructor, so it's not inflate-able
   5473 //        android.appwidget.AppWidgetHostView.class,
   5474         android.gesture.GestureOverlayView.class,
   5475         android.inputmethodservice.ExtractEditText.class,
   5476         android.inputmethodservice.KeyboardView.class,
   5477 //        android.media.tv.TvView.class,
   5478 //        android.opengl.GLSurfaceView.class,
   5479 //        android.view.SurfaceView.class,
   5480         android.view.TextureView.class,
   5481         android.view.ViewStub.class,
   5482 //        android.webkit.WebView.class,
   5483         android.widget.AbsoluteLayout.class,
   5484         android.widget.AdapterViewFlipper.class,
   5485         android.widget.AnalogClock.class,
   5486         android.widget.AutoCompleteTextView.class,
   5487         android.widget.Button.class,
   5488         android.widget.CalendarView.class,
   5489         android.widget.CheckBox.class,
   5490         android.widget.CheckedTextView.class,
   5491         android.widget.Chronometer.class,
   5492         android.widget.DatePicker.class,
   5493         android.widget.DialerFilter.class,
   5494         android.widget.DigitalClock.class,
   5495         android.widget.EditText.class,
   5496         android.widget.ExpandableListView.class,
   5497         android.widget.FrameLayout.class,
   5498         android.widget.Gallery.class,
   5499         android.widget.GridView.class,
   5500         android.widget.HorizontalScrollView.class,
   5501         android.widget.ImageButton.class,
   5502         android.widget.ImageSwitcher.class,
   5503         android.widget.ImageView.class,
   5504         android.widget.LinearLayout.class,
   5505         android.widget.ListView.class,
   5506         android.widget.MediaController.class,
   5507         android.widget.MultiAutoCompleteTextView.class,
   5508         android.widget.NumberPicker.class,
   5509         android.widget.ProgressBar.class,
   5510         android.widget.QuickContactBadge.class,
   5511         android.widget.RadioButton.class,
   5512         android.widget.RadioGroup.class,
   5513         android.widget.RatingBar.class,
   5514         android.widget.RelativeLayout.class,
   5515         android.widget.ScrollView.class,
   5516         android.widget.SeekBar.class,
   5517 // DISABLED because it has required attributes
   5518 //        android.widget.SlidingDrawer.class,
   5519         android.widget.Spinner.class,
   5520         android.widget.StackView.class,
   5521         android.widget.Switch.class,
   5522         android.widget.TabHost.class,
   5523         android.widget.TabWidget.class,
   5524         android.widget.TableLayout.class,
   5525         android.widget.TableRow.class,
   5526         android.widget.TextClock.class,
   5527         android.widget.TextSwitcher.class,
   5528         android.widget.TextView.class,
   5529         android.widget.TimePicker.class,
   5530         android.widget.ToggleButton.class,
   5531         android.widget.TwoLineListItem.class,
   5532 //        android.widget.VideoView.class,
   5533         android.widget.ViewAnimator.class,
   5534         android.widget.ViewFlipper.class,
   5535         android.widget.ViewSwitcher.class,
   5536         android.widget.ZoomButton.class,
   5537         android.widget.ZoomControls.class,
   5538     };
   5539 }
   5540