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