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