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