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