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