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
setup()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
testConstructor()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)
testConstructorNullContext1()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)
testConstructorNullContext2()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
testConstructor2()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
testGetContext()233     public void testGetContext() {
234         View view = new View(mActivity);
235         assertSame(mActivity, view.getContext());
236     }
237 
238     @Test
testGetResources()239     public void testGetResources() {
240         View view = new View(mActivity);
241         assertSame(mResources, view.getResources());
242     }
243 
244     @Test
testGetAnimation()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
testSetAnimation()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
testClearAnimation()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)
testStartAnimationNull()289     public void testStartAnimationNull() {
290         View view = new View(mActivity);
291         view.startAnimation(null);
292     }
293 
294     @Test
testStartAnimation()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
testOnAnimation()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
testGetParent()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
testAccessScrollIndicators()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
testSetScrollIndicators()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
testFindViewById()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
testRequireViewById()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)
testRequireViewByIdNoId()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)
testRequireViewByIdInvalid()386     public void testRequireViewByIdInvalid() {
387         View parent = mActivity.findViewById(R.id.viewlayout_root);
388         parent.requireViewById(0);
389     }
390 
391     @Test(expected = IllegalArgumentException.class)
testRequireViewByIdNotFound()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
testAccessTouchDelegate()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
onHoverEvent_verticalCanScroll_awakenScrollBarsCalled()428     public void onHoverEvent_verticalCanScroll_awakenScrollBarsCalled() {
429         onHoverEvent_awakensScrollBars(true, true, true);
430     }
431 
432     @Test
onHoverEvent_verticalCantScroll_awakenScrollBarsNotCalled()433     public void onHoverEvent_verticalCantScroll_awakenScrollBarsNotCalled() {
434         onHoverEvent_awakensScrollBars(true, false, false);
435     }
436 
437     @Test
onHoverEvent_horizontalCanScroll_awakenScrollBarsCalled()438     public void onHoverEvent_horizontalCanScroll_awakenScrollBarsCalled() {
439         onHoverEvent_awakensScrollBars(false, true, true);
440     }
441 
442     @Test
onHoverEvent_horizontalCantScroll_awakenScrollBarsNotCalled()443     public void onHoverEvent_horizontalCantScroll_awakenScrollBarsNotCalled() {
444         onHoverEvent_awakensScrollBars(false, false, false);
445     }
446 
onHoverEvent_awakensScrollBars(boolean vertical, boolean canScroll, boolean awakenScrollBarsCalled)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
testMouseEventCallsGetPointerIcon()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
testAccessPointerIcon()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
testPointerIconOverlap()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
onResolvePointerIcon_verticalCanScroll_pointerIsArrow()582     public void onResolvePointerIcon_verticalCanScroll_pointerIsArrow() {
583         onResolvePointerIcon_scrollabilityAffectsPointerIcon(true, true, true);
584     }
585 
586     @Test
onResolvePointerIcon_verticalCantScroll_pointerIsProperty()587     public void onResolvePointerIcon_verticalCantScroll_pointerIsProperty() {
588         onResolvePointerIcon_scrollabilityAffectsPointerIcon(true, false, false);
589     }
590 
591     @Test
onResolvePointerIcon_horizontalCanScroll_pointerIsArrow()592     public void onResolvePointerIcon_horizontalCanScroll_pointerIsArrow() {
593         onResolvePointerIcon_scrollabilityAffectsPointerIcon(false, true, true);
594     }
595 
596     @Test
onResolvePointerIcon_horizontalCantScroll_pointerIsProperty()597     public void onResolvePointerIcon_horizontalCantScroll_pointerIsProperty() {
598         onResolvePointerIcon_scrollabilityAffectsPointerIcon(false, false, false);
599     }
600 
onResolvePointerIcon_scrollabilityAffectsPointerIcon(boolean vertical, boolean canScroll, boolean pointerIsSystemArrow)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
testCreatePointerIcons()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 
assertSystemPointerIcon(int style)700     private void assertSystemPointerIcon(int style) {
701         assertNotNull(PointerIcon.getSystemIcon(mActivity, style));
702     }
703 
704     @UiThreadTest
705     @Test
testAccessTag()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
testOnSizeChanged()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)
testGetHitRectNull()764     public void testGetHitRectNull() {
765         MockView view = new MockView(mActivity);
766         view.getHitRect(null);
767     }
768 
769     @Test
testGetHitRect()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
testForceLayout()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
testIsLayoutRequested()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
testRequestLayout()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
testLayout()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
testGetBaseline()835     public void testGetBaseline() {
836         View view = new View(mActivity);
837 
838         assertEquals(-1, view.getBaseline());
839     }
840 
841     @Test
testAccessBackground()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
testSetBackgroundResource()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
testAccessDrawingCacheBackgroundColor()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
testSetBackgroundColor()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
testVerifyDrawable()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
testGetDrawingRect()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
testGetFocusedRect()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
testGetGlobalVisibleRectPoint()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
testGetGlobalVisibleRect()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
testComputeHorizontalScroll()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
testComputeVerticalScroll()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
testGetFadingEdgeStrength()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
testGetLeftFadingEdgeStrength()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
testGetRightFadingEdgeStrength()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
testGetBottomFadingEdgeStrength()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
testGetTopFadingEdgeStrength()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
testResolveSize()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
testGetDefaultSize()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
testAccessId()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
testAccessLongClickable()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
testAccessClickable()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
testAccessContextClickable()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
testGetContextMenuInfo()1252     public void testGetContextMenuInfo() {
1253         MockView view = new MockView(mActivity);
1254 
1255         assertNull(view.getContextMenuInfo());
1256     }
1257 
1258     @Test
testSetOnCreateContextMenuListener()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
testCreateContextMenu()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)
testCreateContextMenuNull()1290     public void testCreateContextMenuNull() {
1291         MockView view = new MockView(mActivity);
1292         view.createContextMenu(null);
1293     }
1294 
1295     @Test
testAddFocusables()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
testGetFocusables()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
testAddFocusablesWithoutTouchMode()1338     public void testAddFocusablesWithoutTouchMode() {
1339         View view = new View(mActivity);
1340         assertFalse("test sanity", view.isInTouchMode());
1341         focusableInTouchModeTest(view, false);
1342     }
1343 
1344     @Test
testAddFocusablesInTouchMode()1345     public void testAddFocusablesInTouchMode() {
1346         View view = spy(new View(mActivity));
1347         when(view.isInTouchMode()).thenReturn(true);
1348         focusableInTouchModeTest(view, true);
1349     }
1350 
focusableInTouchModeTest(View view, boolean inTouchMode)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
testAddKeyboardNavigationClusters()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
testKeyboardNavigationClusterSearch()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
testGetRootView()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
testGetSolidColor()1468     public void testGetSolidColor() {
1469         View view = new View(mActivity);
1470 
1471         assertEquals(0, view.getSolidColor());
1472     }
1473 
1474     @Test
testSetMinimumWidth()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
testGetSuggestedMinimumWidth()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
testSetMinimumHeight()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
testGetSuggestedMinimumHeight()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
testAccessWillNotCacheDrawing()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
testAccessDrawingCacheEnabled()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
testGetDrawingCache()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
testBuildAndDestroyDrawingCache()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
testAccessWillNotDraw()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
testAccessDrawingCacheQuality()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
testDispatchSetSelected()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
testAccessSelected()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
testDispatchSetPressed()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
testAccessPressed()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
testAccessSoundEffectsEnabled()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
testAccessKeepScreenOn()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
testAccessDuplicateParentStateEnabled()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
testAccessEnabled()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
testAccessSaveEnabled()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)
testShowContextMenuNullParent()1733     public void testShowContextMenuNullParent() {
1734         MockView view = new MockView(mActivity);
1735 
1736         assertNull(view.getParent());
1737         view.showContextMenu();
1738     }
1739 
1740     @Test
testShowContextMenu()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)
testShowContextMenuXYNullParent()1751     public void testShowContextMenuXYNullParent() {
1752         MockView view = new MockView(mActivity);
1753 
1754         assertNull(view.getParent());
1755         view.showContextMenu(0, 0);
1756     }
1757 
1758     @Test
testShowContextMenuXY()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
testFitSystemWindows()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
testPerformClick()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
testSetOnClickListener()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
testSetOnGenericMotionListener()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)
testPerformLongClickNullParent()1845     public void testPerformLongClickNullParent() {
1846         MockView view = new MockView(mActivity);
1847         view.performLongClick();
1848     }
1849 
1850     @Test
testPerformLongClick()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)
testPerformLongClickXYNullParent()1871     public void testPerformLongClickXYNullParent() {
1872         MockView view = new MockView(mActivity);
1873         view.performLongClick(0, 0);
1874     }
1875 
1876     @Test
testPerformLongClickXY()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
testPerformLongClickXY_WithListener()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
testSetOnLongClickListener()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
testPerformContextClick()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
testSetOnContextClickListener()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
testAccessOnFocusChangeListener()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
testAccessNextFocusUpId()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
testAccessNextFocusDownId()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
testAccessNextFocusLeftId()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
testAccessNextFocusRightId()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
testAccessMeasuredDimension()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
testMeasure()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) (75 * density + 0.5);
2048         int size2 = (int) (100 * 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)
setSetLayoutParamsNull()2071     public void setSetLayoutParamsNull() {
2072         View view = new View(mActivity);
2073         assertNull(view.getLayoutParams());
2074 
2075         view.setLayoutParams(null);
2076     }
2077 
2078     @Test
testAccessLayoutParams()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
testIsShown()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
testGetDrawingTime()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
testScheduleDrawable()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
testUnscheduleDrawable()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
testGetWindowVisibility()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
testGetWindowToken()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
testHasWindowFocus()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
testGetHandler()2203     public void testGetHandler() {
2204         MockView view = new MockView(mActivity);
2205         // mAttachInfo is null
2206         assertNull(view.getHandler());
2207     }
2208 
2209     @Test
testRemoveCallbacks()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
testCancelLongPress()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
testGetViewTreeObserver()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
testGetWindowAttachCount()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
testOnAttachedToAndDetachedFromWindow()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
testGetLocationInWindow()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)
testGetLocationInWindowNullArray()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)
testGetLocationInWindowSmallArray()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
testGetLocationOnScreen()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)
testGetLocationOnScreenNullArray()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)
testGetLocationOnScreenSmallArray()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
testAddTouchables()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
testGetTouchables()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
testInflate()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
testIsInTouchMode()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
testIsInEditMode()2415     public void testIsInEditMode() {
2416         View view = new View(mActivity);
2417         assertFalse(view.isInEditMode());
2418     }
2419 
2420     @Test
testPostInvalidate1()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
testPostInvalidate2()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
testPostInvalidateDelayed()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
testPost()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
testPostDelayed()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
testPlaySoundEffect()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
testOnKeyShortcut()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
testOnKeyMultiple()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
testDispatchKeyShortcutEvent()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)
testDispatchKeyShortcutEventNull()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
testOnTrackballEvent()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
testDispatchTrackballMoveEvent()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
testDispatchUnhandledMove()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
testUnhandledKeys()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
testWindowVisibilityChanged()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
testGetLocalVisibleRect()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) (75 * density + 0.5);
2754         int size2 = (int) (100 * 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
testMergeDrawableStates()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
testSaveAndRestoreHierarchyState()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
testOnKeyDownOrUp()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 
checkBounds(final ViewGroup viewGroup, final View view, final CountDownLatch countDownLatch, final int left, final int top, final int width, final int height)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
testAddRemoveAffectsWrapContentLayout()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
testDispatchKeyEvent()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
testDispatchTouchEvent()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      */
compareAndRecycleMotionEvents(MotionEvent event1, MotionEvent event2)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
testOnTouchListener()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
testInvalidate1()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
testInvalidate2()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
testInvalidate3()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
testInvalidateDrawable()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
testOnFocusChanged()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
testRestoreDefaultFocus()3307     public void testRestoreDefaultFocus() {
3308         MockView view = new MockView(mActivity);
3309         view.restoreDefaultFocus();
3310         assertTrue(view.hasCalledRequestFocus());
3311     }
3312 
3313     @Test
testDrawableState()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
testWindowFocusChanged()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
testDraw()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
testRequestFocusFromTouch()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
testRequestRectangleOnScreen1()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
testRequestRectangleOnScreen2()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
testRequestRectangleOnScreen3()3470     public void testRequestRectangleOnScreen3() {
3471         requestRectangleOnScreenTest(false);
3472     }
3473 
3474     @Test
testRequestRectangleOnScreen4()3475     public void testRequestRectangleOnScreen4() {
3476         requestRectangleOnScreenTest(true);
3477     }
3478 
3479     @Test
testRequestRectangleOnScreen5()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 
requestRectangleOnScreenTest(boolean scrollParent)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
testRequestRectangleOnScreenWithScale()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      */
waitPrepressedTimeout()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
testOnTouchEventTap()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
testOnTouchEventScroll()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
testBringToFront()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
testGetApplicationWindowToken()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
testGetBottomPaddingOffset()3672     public void testGetBottomPaddingOffset() {
3673         MockView view = new MockView(mActivity);
3674         assertEquals(0, view.getBottomPaddingOffset());
3675     }
3676 
3677     @Test
testGetLeftPaddingOffset()3678     public void testGetLeftPaddingOffset() {
3679         MockView view = new MockView(mActivity);
3680         assertEquals(0, view.getLeftPaddingOffset());
3681     }
3682 
3683     @Test
testGetRightPaddingOffset()3684     public void testGetRightPaddingOffset() {
3685         MockView view = new MockView(mActivity);
3686         assertEquals(0, view.getRightPaddingOffset());
3687     }
3688 
3689     @Test
testGetTopPaddingOffset()3690     public void testGetTopPaddingOffset() {
3691         MockView view = new MockView(mActivity);
3692         assertEquals(0, view.getTopPaddingOffset());
3693     }
3694 
3695     @Test
testIsPaddingOffsetRequired()3696     public void testIsPaddingOffsetRequired() {
3697         MockView view = new MockView(mActivity);
3698         assertFalse(view.isPaddingOffsetRequired());
3699     }
3700 
3701     @UiThreadTest
3702     @Test
testPadding()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
testGetWindowVisibleDisplayFrame()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
testSetScrollContainer()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
testTouchMode()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
testScrollbarStyle()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
testScrollFading()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
testScrolling()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
testInitializeScrollbarsAndFadingEdge()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
testScrollIndicators()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
testScrollbarSize()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
testOnStartAndFinishTemporaryDetach()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
testKeyPreIme()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
testHapticFeedback()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
testInputConnection()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
testFilterTouchesWhenObscured()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
testBackgroundTint()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
testStartActionModeWithParent()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
testStartActionModeWithoutParent()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
testStartActionModeTypedWithParent()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
testStartActionModeTypedWithoutParent()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
testVisibilityAggregated()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
testOverlappingRendering()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 
startDragAndDrop(View view, View.DragShadowBuilder shadowBuilder)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 
createDragShadowBuidler()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
testUpdateDragShadow()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
testUpdateDragShadow_detachedView()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
testUpdateDragShadow_noActiveDrag()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 
setVisibilityOnUiThread(final View view, final int visibility)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 
MockOverlappingRenderingSubclass(Context context, boolean overlap)4631         public MockOverlappingRenderingSubclass(Context context, boolean overlap) {
4632             super(context);
4633             mOverlap = overlap;
4634         }
4635 
4636         @Override
hasOverlappingRendering()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 
MockViewGroup(Context context)4646         public MockViewGroup(Context context) {
4647             super(context);
4648         }
4649 
4650         @Override
startActionModeForChild(View originalView, ActionMode.Callback callback)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
startActionModeForChild( View originalView, ActionMode.Callback callback, int type)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
onLayout(boolean changed, int l, int t, int r, int b)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
testTranslationSetter()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
testXYZ()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
testOnHoverEvent()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)
testScaleXNaN()4803     public void testScaleXNaN() {
4804         View view = new View(mContext);
4805         view.setScaleX(Float.NaN);
4806     }
4807 
4808     @Test(expected = IllegalArgumentException.class)
testScaleXPositiveInfinity()4809     public void testScaleXPositiveInfinity() {
4810         View view = new View(mContext);
4811         view.setScaleX(Float.POSITIVE_INFINITY);
4812     }
4813 
4814     @Test(expected = IllegalArgumentException.class)
testScaleXNegativeInfinity()4815     public void testScaleXNegativeInfinity() {
4816         View view = new View(mContext);
4817         view.setScaleX(Float.NEGATIVE_INFINITY);
4818     }
4819 
4820     @Test(expected = IllegalArgumentException.class)
testScaleYNaN()4821     public void testScaleYNaN() {
4822         View view = new View(mContext);
4823         view.setScaleY(Float.NaN);
4824     }
4825 
4826     @Test(expected = IllegalArgumentException.class)
testScaleYPositiveInfinity()4827     public void testScaleYPositiveInfinity() {
4828         View view = new View(mContext);
4829         view.setScaleY(Float.POSITIVE_INFINITY);
4830     }
4831 
4832     @Test(expected = IllegalArgumentException.class)
testScaleYNegativeInfinity()4833     public void testScaleYNegativeInfinity() {
4834         View view = new View(mContext);
4835         view.setScaleY(Float.NEGATIVE_INFINITY);
4836     }
4837 
4838     @Test
testTransitionAlpha()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
testSetGetOutlineShadowColor()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
testTransformMatrixToGlobal()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
testTransformMatrixToLocal()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
testPivot()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
testSetLeftTopRightBottom()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
testGetUniqueDrawingId()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
testSetVerticalScrollbarTrack()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
testSetVerticalScrollbarThumb()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
testSetHorizontalScrollbarTrack()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
testSetHorizontalScrollbarThumb()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
testSetTransitionVisibility()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
draw(Canvas canvas)5063         public void draw(Canvas canvas) {}
5064 
5065         @Override
setAlpha(int alpha)5066         public void setAlpha(int alpha) {}
5067 
5068         @Override
setColorFilter(ColorFilter cf)5069         public void setColorFilter(ColorFilter cf) {}
5070 
5071         @Override
getOpacity()5072         public int getOpacity() {
5073             return 0;
5074         }
5075 
5076         @Override
setTintList(ColorStateList tint)5077         public void setTintList(ColorStateList tint) {
5078             super.setTintList(tint);
5079             mCalledSetTint = true;
5080         }
5081 
hasCalledSetTint()5082         public boolean hasCalledSetTint() {
5083             return mCalledSetTint;
5084         }
5085 
reset()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 
MockEditText(Context context)5096         public MockEditText(Context context) {
5097             super(context);
5098         }
5099 
5100         @Override
checkInputConnectionProxy(View view)5101         public boolean checkInputConnectionProxy(View view) {
5102             mCalledCheckInputConnectionProxy = true;
5103             return super.checkInputConnectionProxy(view);
5104         }
5105 
hasCalledCheckInputConnectionProxy()5106         public boolean hasCalledCheckInputConnectionProxy() {
5107             return mCalledCheckInputConnectionProxy;
5108         }
5109 
5110         @Override
onCreateInputConnection(EditorInfo outAttrs)5111         public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
5112             mCalledOnCreateInputConnection = true;
5113             return super.onCreateInputConnection(outAttrs);
5114         }
5115 
hasCalledOnCreateInputConnection()5116         public boolean hasCalledOnCreateInputConnection() {
5117             return mCalledOnCreateInputConnection;
5118         }
5119 
5120         @Override
onCheckIsTextEditor()5121         public boolean onCheckIsTextEditor() {
5122             mCalledOnCheckIsTextEditor = true;
5123             return super.onCheckIsTextEditor();
5124         }
5125 
hasCalledOnCheckIsTextEditor()5126         public boolean hasCalledOnCheckIsTextEditor() {
5127             return mCalledOnCheckIsTextEditor;
5128         }
5129 
reset()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
requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate)5148         public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
5149                 boolean immediate) {
5150             return false;
5151         }
5152 
MockViewParent(Context context)5153         public MockViewParent(Context context) {
5154             super(context);
5155         }
5156 
5157         @Override
bringChildToFront(View child)5158         public void bringChildToFront(View child) {
5159             mHasBroughtChildToFront = true;
5160         }
5161 
hasBroughtChildToFront()5162         public boolean hasBroughtChildToFront() {
5163             return mHasBroughtChildToFront;
5164         }
5165 
5166         @Override
childDrawableStateChanged(View child)5167         public void childDrawableStateChanged(View child) {
5168             mHasChildDrawableStateChanged = true;
5169         }
5170 
hasChildDrawableStateChanged()5171         public boolean hasChildDrawableStateChanged() {
5172             return mHasChildDrawableStateChanged;
5173         }
5174 
5175         @Override
dispatchSetPressed(boolean pressed)5176         public void dispatchSetPressed(boolean pressed) {
5177             super.dispatchSetPressed(pressed);
5178         }
5179 
5180         @Override
dispatchSetSelected(boolean selected)5181         public void dispatchSetSelected(boolean selected) {
5182             super.dispatchSetSelected(selected);
5183         }
5184 
5185         @Override
clearChildFocus(View child)5186         public void clearChildFocus(View child) {
5187 
5188         }
5189 
5190         @Override
createContextMenu(ContextMenu menu)5191         public void createContextMenu(ContextMenu menu) {
5192             mHasCreateContextMenu = true;
5193         }
5194 
hasCreateContextMenu()5195         public boolean hasCreateContextMenu() {
5196             return mHasCreateContextMenu;
5197         }
5198 
5199         @Override
focusSearch(View v, int direction)5200         public View focusSearch(View v, int direction) {
5201             return v;
5202         }
5203 
5204         @Override
focusableViewAvailable(View v)5205         public void focusableViewAvailable(View v) {
5206 
5207         }
5208 
5209         @Override
getChildVisibleRect(View child, Rect r, Point offset)5210         public boolean getChildVisibleRect(View child, Rect r, Point offset) {
5211             return false;
5212         }
5213 
5214         @Override
onLayout(boolean changed, int l, int t, int r, int b)5215         protected void onLayout(boolean changed, int l, int t, int r, int b) {
5216 
5217         }
5218 
5219         @Override
invalidateChildInParent(int[] location, Rect r)5220         public ViewParent invalidateChildInParent(int[] location, Rect r) {
5221             return null;
5222         }
5223 
5224         @Override
isLayoutRequested()5225         public boolean isLayoutRequested() {
5226             return false;
5227         }
5228 
5229         @Override
recomputeViewAttributes(View child)5230         public void recomputeViewAttributes(View child) {
5231 
5232         }
5233 
5234         @Override
requestChildFocus(View child, View focused)5235         public void requestChildFocus(View child, View focused) {
5236 
5237         }
5238 
5239         @Override
requestDisallowInterceptTouchEvent(boolean disallowIntercept)5240         public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
5241 
5242         }
5243 
5244         @Override
requestLayout()5245         public void requestLayout() {
5246             mHasRequestLayout = true;
5247         }
5248 
hasRequestLayout()5249         public boolean hasRequestLayout() {
5250             return mHasRequestLayout;
5251         }
5252 
5253         @Override
requestTransparentRegion(View child)5254         public void requestTransparentRegion(View child) {
5255 
5256         }
5257 
5258         @Override
showContextMenuForChild(View originalView)5259         public boolean showContextMenuForChild(View originalView) {
5260             mHasShowContextMenuForChild = true;
5261             return false;
5262         }
5263 
5264         @Override
showContextMenuForChild(View originalView, float x, float y)5265         public boolean showContextMenuForChild(View originalView, float x, float y) {
5266             mHasShowContextMenuForChildXY = true;
5267             return false;
5268         }
5269 
5270         @Override
startActionModeForChild(View originalView, ActionMode.Callback callback)5271         public ActionMode startActionModeForChild(View originalView,
5272                 ActionMode.Callback callback) {
5273             return null;
5274         }
5275 
5276         @Override
startActionModeForChild(View originalView, ActionMode.Callback callback, int type)5277         public ActionMode startActionModeForChild(View originalView,
5278                 ActionMode.Callback callback, int type) {
5279             return null;
5280         }
5281 
hasShowContextMenuForChild()5282         public boolean hasShowContextMenuForChild() {
5283             return mHasShowContextMenuForChild;
5284         }
5285 
hasShowContextMenuForChildXY()5286         public boolean hasShowContextMenuForChildXY() {
5287             return mHasShowContextMenuForChildXY;
5288         }
5289 
5290         @Override
onCreateDrawableState(int extraSpace)5291         protected int[] onCreateDrawableState(int extraSpace) {
5292             return DEFAULT_PARENT_STATE_SET;
5293         }
5294 
5295         @Override
requestSendAccessibilityEvent(View child, AccessibilityEvent event)5296         public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
5297             return false;
5298         }
5299 
reset()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
childHasTransientStateChanged(View child, boolean hasTransientState)5310         public void childHasTransientStateChanged(View child, boolean hasTransientState) {
5311 
5312         }
5313 
5314         @Override
getParentForAccessibility()5315         public ViewParent getParentForAccessibility() {
5316             return null;
5317         }
5318 
5319         @Override
notifySubtreeAccessibilityStateChanged(View child, View source, int changeType)5320         public void notifySubtreeAccessibilityStateChanged(View child,
5321             View source, int changeType) {
5322 
5323         }
5324 
5325         @Override
onStartNestedScroll(View child, View target, int nestedScrollAxes)5326         public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
5327             return false;
5328         }
5329 
5330         @Override
onNestedScrollAccepted(View child, View target, int nestedScrollAxes)5331         public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
5332         }
5333 
5334         @Override
onStopNestedScroll(View target)5335         public void onStopNestedScroll(View target) {
5336         }
5337 
5338         @Override
onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)5339         public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
5340                                    int dxUnconsumed, int dyUnconsumed) {
5341         }
5342 
5343         @Override
onNestedPreScroll(View target, int dx, int dy, int[] consumed)5344         public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
5345         }
5346 
5347         @Override
onNestedFling(View target, float velocityX, float velocityY, boolean consumed)5348         public boolean onNestedFling(View target, float velocityX, float velocityY,
5349                 boolean consumed) {
5350             return false;
5351         }
5352 
5353         @Override
onNestedPreFling(View target, float velocityX, float velocityY)5354         public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
5355             return false;
5356         }
5357 
5358         @Override
onNestedPrePerformAccessibilityAction(View target, int action, Bundle args)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 
MockViewGroupParent(Context context)5369         public MockViewGroupParent(Context context) {
5370             super(context);
5371         }
5372 
5373         @Override
onLayout(boolean changed, int l, int t, int r, int b)5374         protected void onLayout(boolean changed, int l, int t, int r, int b) {
5375 
5376         }
5377 
5378         @Override
requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate)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 
getLastRequestedChildRectOnScreen()5386         public Rect getLastRequestedChildRectOnScreen() {
5387             return mLastRequestedChildRectOnScreen;
5388         }
5389 
hasRequestChildRectangleOnScreen()5390         public boolean hasRequestChildRectangleOnScreen() {
5391             return mHasRequestChildRectangleOnScreen;
5392         }
5393 
5394         @Override
detachViewFromParent(View child)5395         protected void detachViewFromParent(View child) {
5396             super.detachViewFromParent(child);
5397         }
5398 
reset()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
onUnhandledKeyEvent(View v, KeyEvent event)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         }
reset()5424         public void reset() {
5425             mLastView = null;
5426             mGotUp = false;
5427         }
fired()5428         public boolean fired() {
5429             return mLastView != null && mGotUp;
5430         }
5431     }
5432 
5433     public static class ScrollTestView extends View {
ScrollTestView(Context context)5434         public ScrollTestView(Context context) {
5435             super(context);
5436         }
5437 
5438         @Override
awakenScrollBars()5439         public boolean awakenScrollBars() {
5440             return super.awakenScrollBars();
5441         }
5442 
5443         @Override
computeHorizontalScrollRange()5444         public int computeHorizontalScrollRange() {
5445             return super.computeHorizontalScrollRange();
5446         }
5447 
5448         @Override
computeHorizontalScrollExtent()5449         public int computeHorizontalScrollExtent() {
5450             return super.computeHorizontalScrollExtent();
5451         }
5452 
5453         @Override
computeVerticalScrollRange()5454         public int computeVerticalScrollRange() {
5455             return super.computeVerticalScrollRange();
5456         }
5457 
5458         @Override
computeVerticalScrollExtent()5459         public int computeVerticalScrollExtent() {
5460             return super.computeVerticalScrollExtent();
5461         }
5462 
5463         @Override
getHorizontalScrollbarHeight()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