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