• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view.cts;
18 
19 import static android.view.flags.Flags.FLAG_TOOLKIT_VIEWGROUP_SET_REQUESTED_FRAME_RATE_API;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertNull;
25 import static org.junit.Assert.assertSame;
26 import static org.junit.Assert.assertTrue;
27 import static org.junit.Assert.fail;
28 import static org.mockito.ArgumentMatchers.any;
29 import static org.mockito.ArgumentMatchers.eq;
30 import static org.mockito.Mockito.inOrder;
31 import static org.mockito.Mockito.mock;
32 import static org.mockito.Mockito.never;
33 import static org.mockito.Mockito.spy;
34 import static org.mockito.Mockito.times;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when;
37 
38 import android.Manifest;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.pm.PackageManager;
42 import android.content.res.XmlResourceParser;
43 import android.graphics.Bitmap;
44 import android.graphics.Bitmap.Config;
45 import android.graphics.Canvas;
46 import android.graphics.Insets;
47 import android.graphics.Point;
48 import android.graphics.Rect;
49 import android.graphics.Region;
50 import android.graphics.drawable.BitmapDrawable;
51 import android.os.Parcelable;
52 import android.os.SystemClock;
53 import android.platform.test.annotations.AppModeSdkSandbox;
54 import android.platform.test.annotations.RequiresFlagsEnabled;
55 import android.platform.test.flag.junit.CheckFlagsRule;
56 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
57 import android.util.AttributeSet;
58 import android.util.DisplayMetrics;
59 import android.util.SparseArray;
60 import android.view.ActionMode;
61 import android.view.Display;
62 import android.view.KeyEvent;
63 import android.view.Menu;
64 import android.view.MenuInflater;
65 import android.view.MenuItem;
66 import android.view.MotionEvent;
67 import android.view.PointerIcon;
68 import android.view.View;
69 import android.view.View.BaseSavedState;
70 import android.view.View.MeasureSpec;
71 import android.view.View.OnApplyWindowInsetsListener;
72 import android.view.ViewGroup;
73 import android.view.ViewGroup.LayoutParams;
74 import android.view.WindowInsets;
75 import android.view.WindowManager;
76 import android.view.animation.AlphaAnimation;
77 import android.view.animation.Animation;
78 import android.view.animation.Animation.AnimationListener;
79 import android.view.animation.LayoutAnimationController;
80 import android.view.animation.RotateAnimation;
81 import android.view.animation.Transformation;
82 import android.view.cts.util.EventUtils;
83 import android.view.cts.util.ScrollBarUtils;
84 import android.view.cts.util.XmlUtils;
85 import android.widget.Button;
86 import android.widget.TextView;
87 
88 import androidx.annotation.NonNull;
89 import androidx.test.InstrumentationRegistry;
90 import androidx.test.annotation.UiThreadTest;
91 import androidx.test.core.app.ActivityScenario;
92 import androidx.test.filters.LargeTest;
93 import androidx.test.filters.MediumTest;
94 import androidx.test.runner.AndroidJUnit4;
95 
96 import com.android.compatibility.common.util.AdoptShellPermissionsRule;
97 import com.android.compatibility.common.util.CTSResult;
98 import com.android.compatibility.common.util.PollingCheck;
99 
100 import org.junit.Before;
101 import org.junit.Ignore;
102 import org.junit.Rule;
103 import org.junit.Test;
104 import org.junit.runner.RunWith;
105 import org.mockito.InOrder;
106 
107 import java.util.ArrayList;
108 
109 @MediumTest
110 @RunWith(AndroidJUnit4.class)
111 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).")
112 public class ViewGroupTest implements CTSResult {
113     public static final long TOUCH_MODE_PROPAGATION_TIMEOUT_MILLIS = 5_000L;
114     private Context mContext;
115     private MotionEvent mMotionEvent;
116     private int mResultCode;
117 
118     private MockViewGroup mMockViewGroup;
119     private TextView mTextView;
120     private MockTextView mMockTextView;
121 
122     private final Sync mSync = new Sync();
123 
124     private static class Sync {
125         boolean mHasNotify;
126     }
127 
128     @Rule
129     public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
130             androidx.test.platform.app.InstrumentationRegistry
131                     .getInstrumentation().getUiAutomation(),
132             Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
133 
134     @Rule
135     public final CheckFlagsRule mCheckFlagsRule =
136             DeviceFlagsValueProvider.createCheckFlagsRule();
137 
138     @UiThreadTest
139     @Before
setup()140     public void setup() {
141         mContext = InstrumentationRegistry.getTargetContext();
142         mMockViewGroup = new MockViewGroup(mContext);
143         mTextView = new TextView(mContext);
144         mMockTextView = new MockTextView(mContext);
145     }
146 
147     @Test
testConstructor()148     public void testConstructor() {
149         new MockViewGroup(mContext);
150         new MockViewGroup(mContext, null);
151         new MockViewGroup(mContext, null, 0);
152     }
153 
154     @UiThreadTest
155     @Test
testAddFocusables()156     public void testAddFocusables() {
157         mMockViewGroup.setFocusable(true);
158 
159         // Child is focusable.
160         ArrayList<View> list = new ArrayList<>();
161         list.add(mTextView);
162         mMockViewGroup.addView(mTextView);
163         mMockViewGroup.addFocusables(list, 0);
164 
165         assertEquals(2, list.size());
166 
167         // Parent blocks descendants.
168         list = new ArrayList<>();
169         list.add(mTextView);
170         mMockViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
171         mMockViewGroup.setFocusable(false);
172         mMockViewGroup.addFocusables(list, 0);
173         assertEquals(1, list.size());
174 
175         // Both parent and child are focusable.
176         list.clear();
177         mMockViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
178         mTextView.setFocusable(true);
179         mMockViewGroup.setFocusable(true);
180         mMockViewGroup.addFocusables(list, 0);
181         assertEquals(2, list.size());
182     }
183 
184     @UiThreadTest
185     @Test
testAddKeyboardNavigationClusters()186     public void testAddKeyboardNavigationClusters() {
187         View v1 = new MockView(mContext);
188         v1.setFocusableInTouchMode(true);
189         View v2 = new MockView(mContext);
190         v2.setFocusableInTouchMode(true);
191         mMockViewGroup.addView(v1);
192         mMockViewGroup.addView(v2);
193 
194         // No clusters.
195         ArrayList<View> list = new ArrayList<>();
196         mMockViewGroup.addKeyboardNavigationClusters(list, 0);
197         assertEquals(0, list.size());
198 
199         // A cluster and a non-cluster child.
200         v1.setKeyboardNavigationCluster(true);
201         mMockViewGroup.addKeyboardNavigationClusters(list, 0);
202         assertEquals(1, list.size());
203         assertEquals(v1, list.get(0));
204         list.clear();
205 
206         // Blocking descendants from getting focus also blocks group search.
207         mMockViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
208         mMockViewGroup.addKeyboardNavigationClusters(list, 0);
209         assertEquals(0, list.size());
210         mMockViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
211 
212         // Testing the results ordering.
213         v2.setKeyboardNavigationCluster(true);
214         mMockViewGroup.addKeyboardNavigationClusters(list, 0);
215         assertEquals(2, list.size());
216         assertEquals(v1, list.get(0));
217         assertEquals(v2, list.get(1));
218         list.clear();
219 
220         // 3-level hierarchy.
221         ViewGroup parent = new MockViewGroup(mContext);
222         parent.addView(mMockViewGroup);
223         mMockViewGroup.removeView(v2);
224         parent.addKeyboardNavigationClusters(list, 0);
225         assertEquals(1, list.size());
226         assertEquals(v1, list.get(0));
227         list.clear();
228 
229         // Cluster with no focusables gets ignored
230         mMockViewGroup.addView(v2);
231         v2.setFocusable(false);
232         mMockViewGroup.addKeyboardNavigationClusters(list, 0);
233         assertEquals(1, list.size());
234         list.clear();
235 
236         // Invisible children get ignored.
237         mMockViewGroup.setVisibility(View.GONE);
238         parent.addKeyboardNavigationClusters(list, 0);
239         assertEquals(0, list.size());
240         list.clear();
241 
242         // Nested clusters are ignored
243         TestClusterHier h = new TestClusterHier();
244         h.nestedGroup.setKeyboardNavigationCluster(true);
245         h.cluster2.setKeyboardNavigationCluster(false);
246         h.top.addKeyboardNavigationClusters(list, View.FOCUS_FORWARD);
247         assertTrue(list.contains(h.nestedGroup));
248         list.clear();
249         h.cluster2.setKeyboardNavigationCluster(true);
250         h.top.addKeyboardNavigationClusters(list, View.FOCUS_FORWARD);
251         assertFalse(list.contains(h.nestedGroup));
252         list.clear();
253     }
254 
255     @UiThreadTest
256     @Test
testAddStatesFromChildren()257     public void testAddStatesFromChildren() {
258         mMockViewGroup.addView(mTextView);
259         assertFalse(mMockViewGroup.addStatesFromChildren());
260 
261         mMockViewGroup.setAddStatesFromChildren(true);
262         mTextView.performClick();
263         assertTrue(mMockViewGroup.addStatesFromChildren());
264         assertTrue(mMockViewGroup.isDrawableStateChangedCalled);
265     }
266 
267     @UiThreadTest
268     @Test
testAddTouchables()269     public void testAddTouchables() {
270         mMockViewGroup.setFocusable(true);
271 
272         ArrayList<View> list = new ArrayList<>();
273         mTextView.setVisibility(View.VISIBLE);
274         mTextView.setClickable(true);
275         mTextView.setEnabled(true);
276 
277         list.add(mTextView);
278         mMockViewGroup.addView(mTextView);
279         mMockViewGroup.addTouchables(list);
280 
281         assertEquals(2, list.size());
282 
283         View v = mMockViewGroup.getChildAt(0);
284         assertSame(mTextView, v);
285 
286         v = mMockViewGroup.getChildAt(-1);
287         assertNull(v);
288 
289         v = mMockViewGroup.getChildAt(1);
290         assertNull(v);
291 
292         v = mMockViewGroup.getChildAt(100);
293         assertNull(v);
294 
295         v = mMockViewGroup.getChildAt(-100);
296         assertNull(v);
297     }
298 
299     @UiThreadTest
300     @Test
testAddView()301     public void testAddView() {
302         assertEquals(0, mMockViewGroup.getChildCount());
303 
304         mMockViewGroup.addView(mTextView);
305         assertEquals(1, mMockViewGroup.getChildCount());
306         assertTrue(mMockViewGroup.isOnViewAddedCalled);
307     }
308 
309     @UiThreadTest
310     @Test
testAddViewWithParaViewInt()311     public void testAddViewWithParaViewInt() {
312         assertEquals(0, mMockViewGroup.getChildCount());
313 
314         mMockViewGroup.addView(mTextView, -1);
315         assertEquals(1, mMockViewGroup.getChildCount());
316         assertTrue(mMockViewGroup.isOnViewAddedCalled);
317     }
318 
319     @UiThreadTest
320     @Test
testAddViewWithParaViewLayoutPara()321     public void testAddViewWithParaViewLayoutPara() {
322         assertEquals(0, mMockViewGroup.getChildCount());
323 
324         mMockViewGroup.addView(mTextView, new ViewGroup.LayoutParams(100, 200));
325 
326         assertEquals(1, mMockViewGroup.getChildCount());
327         assertTrue(mMockViewGroup.isOnViewAddedCalled);
328     }
329 
330     @UiThreadTest
331     @Test
testAddViewWithParaViewIntInt()332     public void testAddViewWithParaViewIntInt() {
333         final int width = 100;
334         final int height = 200;
335 
336         assertEquals(0, mMockViewGroup.getChildCount());
337 
338         mMockViewGroup.addView(mTextView, width, height);
339         assertEquals(width, mTextView.getLayoutParams().width);
340         assertEquals(height, mTextView.getLayoutParams().height);
341 
342         assertEquals(1, mMockViewGroup.getChildCount());
343         assertTrue(mMockViewGroup.isOnViewAddedCalled);
344     }
345 
346     @UiThreadTest
347     @Test
testAddViewWidthParaViewIntLayoutParam()348     public void testAddViewWidthParaViewIntLayoutParam() {
349         assertEquals(0, mMockViewGroup.getChildCount());
350 
351         mMockViewGroup.addView(mTextView, -1, new ViewGroup.LayoutParams(100, 200));
352 
353         assertEquals(1, mMockViewGroup.getChildCount());
354         assertTrue(mMockViewGroup.isOnViewAddedCalled);
355     }
356 
357     @UiThreadTest
358     @Test
testAddViewInLayout()359     public void testAddViewInLayout() {
360         assertEquals(0, mMockViewGroup.getChildCount());
361 
362         assertTrue(mMockViewGroup.isRequestLayoutCalled);
363         mMockViewGroup.isRequestLayoutCalled = false;
364         assertTrue(mMockViewGroup.addViewInLayout(
365                 mTextView, -1, new ViewGroup.LayoutParams(100, 200)));
366         assertEquals(1, mMockViewGroup.getChildCount());
367         // check that calling addViewInLayout() does not trigger a
368         // requestLayout() on this ViewGroup
369         assertFalse(mMockViewGroup.isRequestLayoutCalled);
370         assertTrue(mMockViewGroup.isOnViewAddedCalled);
371     }
372 
373     @UiThreadTest
374     @Test
testAttachLayoutAnimationParameters()375     public void testAttachLayoutAnimationParameters() {
376         ViewGroup.LayoutParams param = new ViewGroup.LayoutParams(10, 10);
377 
378         mMockViewGroup.attachLayoutAnimationParameters(null, param, 1, 2);
379         assertEquals(2, param.layoutAnimationParameters.count);
380         assertEquals(1, param.layoutAnimationParameters.index);
381     }
382 
383     @UiThreadTest
384     @Test
testAttachViewToParent()385     public void testAttachViewToParent() {
386         mMockViewGroup.setFocusable(true);
387         assertEquals(0, mMockViewGroup.getChildCount());
388 
389         ViewGroup.LayoutParams param = new ViewGroup.LayoutParams(10, 10);
390 
391         mTextView.setFocusable(true);
392         mMockViewGroup.attachViewToParent(mTextView, -1, param);
393         assertSame(mMockViewGroup, mTextView.getParent());
394         assertEquals(1, mMockViewGroup.getChildCount());
395         assertSame(mTextView, mMockViewGroup.getChildAt(0));
396     }
397 
398     @UiThreadTest
399     @Test
testAddViewInLayoutWithParamViewIntLayB()400     public void testAddViewInLayoutWithParamViewIntLayB() {
401         assertEquals(0, mMockViewGroup.getChildCount());
402 
403         assertTrue(mMockViewGroup.isRequestLayoutCalled);
404         mMockViewGroup.isRequestLayoutCalled = false;
405         assertTrue(mMockViewGroup.addViewInLayout(
406                 mTextView, -1, new ViewGroup.LayoutParams(100, 200), true));
407 
408         assertEquals(1, mMockViewGroup.getChildCount());
409         // check that calling addViewInLayout() does not trigger a
410         // requestLayout() on this ViewGroup
411         assertFalse(mMockViewGroup.isRequestLayoutCalled);
412         assertTrue(mMockViewGroup.isOnViewAddedCalled);
413     }
414 
415     @UiThreadTest
416     @Test
testBringChildToFront()417     public void testBringChildToFront() {
418         TextView textView1 = new TextView(mContext);
419         TextView textView2 = new TextView(mContext);
420 
421         assertEquals(0, mMockViewGroup.getChildCount());
422 
423         mMockViewGroup.addView(textView1);
424         mMockViewGroup.addView(textView2);
425         assertEquals(2, mMockViewGroup.getChildCount());
426 
427         mMockViewGroup.bringChildToFront(textView1);
428         assertEquals(mMockViewGroup, textView1.getParent());
429         assertEquals(2, mMockViewGroup.getChildCount());
430         assertNotNull(mMockViewGroup.getChildAt(0));
431         assertSame(textView2, mMockViewGroup.getChildAt(0));
432 
433         mMockViewGroup.bringChildToFront(textView2);
434         assertEquals(mMockViewGroup, textView2.getParent());
435         assertEquals(2, mMockViewGroup.getChildCount());
436         assertNotNull(mMockViewGroup.getChildAt(0));
437         assertSame(textView1, mMockViewGroup.getChildAt(0));
438     }
439 
440     @UiThreadTest
441     @Test
testCanAnimate()442     public void testCanAnimate() {
443         assertFalse(mMockViewGroup.canAnimate());
444 
445         RotateAnimation animation = new RotateAnimation(0.1f, 0.1f);
446         LayoutAnimationController la = new LayoutAnimationController(animation);
447         mMockViewGroup.setLayoutAnimation(la);
448         assertTrue(mMockViewGroup.canAnimate());
449     }
450 
451     @UiThreadTest
452     @Test
testCheckLayoutParams()453     public void testCheckLayoutParams() {
454         assertFalse(mMockViewGroup.checkLayoutParams(null));
455 
456         assertTrue(mMockViewGroup.checkLayoutParams(new ViewGroup.LayoutParams(100, 200)));
457     }
458 
459     @UiThreadTest
460     @Test
testChildDrawableStateChanged()461     public void testChildDrawableStateChanged() {
462         mMockViewGroup.setAddStatesFromChildren(true);
463 
464         mMockViewGroup.childDrawableStateChanged(null);
465         assertTrue(mMockViewGroup.isRefreshDrawableStateCalled);
466     }
467 
468     @UiThreadTest
469     @Test
testCleanupLayoutState()470     public void testCleanupLayoutState() {
471         assertTrue(mTextView.isLayoutRequested());
472 
473         mMockViewGroup.cleanupLayoutState(mTextView);
474         assertFalse(mTextView.isLayoutRequested());
475     }
476 
477     @UiThreadTest
478     @Test
testClearChildFocus()479     public void testClearChildFocus() {
480         mMockViewGroup.addView(mTextView);
481         mMockViewGroup.requestChildFocus(mTextView, null);
482 
483         View focusedView = mMockViewGroup.getFocusedChild();
484         assertSame(mTextView, focusedView);
485 
486         mMockViewGroup.clearChildFocus(mTextView);
487         assertNull(mMockViewGroup.getFocusedChild());
488     }
489 
490     @UiThreadTest
491     @Test
testClearDisappearingChildren()492     public void testClearDisappearingChildren() {
493         Canvas canvas = new Canvas();
494         MockViewGroup child = new MockViewGroup(mContext);
495         child.setAnimation(new MockAnimation());
496         mMockViewGroup.addView(child);
497         assertEquals(1, mMockViewGroup.getChildCount());
498 
499         assertNotNull(child.getAnimation());
500         mMockViewGroup.dispatchDraw(canvas);
501         assertEquals(1, mMockViewGroup.drawChildCalledTime);
502 
503         child.setAnimation(new MockAnimation());
504         mMockViewGroup.removeAllViewsInLayout();
505 
506         mMockViewGroup.drawChildCalledTime = 0;
507         mMockViewGroup.dispatchDraw(canvas);
508         assertEquals(1, mMockViewGroup.drawChildCalledTime);
509 
510         child.setAnimation(new MockAnimation());
511         mMockViewGroup.clearDisappearingChildren();
512 
513         mMockViewGroup.drawChildCalledTime = 0;
514         mMockViewGroup.dispatchDraw(canvas);
515         assertEquals(0, mMockViewGroup.drawChildCalledTime);
516     }
517 
518     @UiThreadTest
519     @Test
testClearFocus()520     public void testClearFocus() {
521         mMockViewGroup.addView(mMockTextView);
522         mMockViewGroup.requestChildFocus(mMockTextView, null);
523         mMockViewGroup.clearFocus();
524         assertTrue(mMockTextView.isClearFocusCalled);
525     }
526 
527     @UiThreadTest
528     @Test
testDetachAllViewsFromParent()529     public void testDetachAllViewsFromParent() {
530         mMockViewGroup.addView(mTextView);
531         assertEquals(1, mMockViewGroup.getChildCount());
532         assertSame(mMockViewGroup, mTextView.getParent());
533         mMockViewGroup.detachAllViewsFromParent();
534         assertEquals(0, mMockViewGroup.getChildCount());
535         assertNull(mTextView.getParent());
536     }
537 
538     @UiThreadTest
539     @Test
testDetachViewFromParent()540     public void testDetachViewFromParent() {
541         mMockViewGroup.addView(mTextView);
542         assertEquals(1, mMockViewGroup.getChildCount());
543 
544         mMockViewGroup.detachViewFromParent(0);
545 
546         assertEquals(0, mMockViewGroup.getChildCount());
547         assertNull(mTextView.getParent());
548     }
549 
550     @UiThreadTest
551     @Test
testDetachViewFromParentWithParamView()552     public void testDetachViewFromParentWithParamView() {
553         mMockViewGroup.addView(mTextView);
554         assertEquals(1, mMockViewGroup.getChildCount());
555         assertSame(mMockViewGroup, mTextView.getParent());
556 
557         mMockViewGroup.detachViewFromParent(mTextView);
558 
559         assertEquals(0, mMockViewGroup.getChildCount());
560         assertNull(mMockViewGroup.getParent());
561     }
562 
563     @UiThreadTest
564     @Test
testDetachViewsFromParent()565     public void testDetachViewsFromParent() {
566         TextView textView1 = new TextView(mContext);
567         TextView textView2 = new TextView(mContext);
568         TextView textView3 = new TextView(mContext);
569 
570         mMockViewGroup.addView(textView1);
571         mMockViewGroup.addView(textView2);
572         mMockViewGroup.addView(textView3);
573         assertEquals(3, mMockViewGroup.getChildCount());
574 
575         mMockViewGroup.detachViewsFromParent(0, 2);
576 
577         assertEquals(1, mMockViewGroup.getChildCount());
578         assertNull(textView1.getParent());
579         assertNull(textView2.getParent());
580     }
581 
582     @UiThreadTest
583     @Test
testDispatchDraw()584     public void testDispatchDraw() {
585         Canvas canvas = new Canvas();
586 
587         mMockViewGroup.draw(canvas);
588         assertTrue(mMockViewGroup.isDispatchDrawCalled);
589         assertSame(canvas, mMockViewGroup.canvas);
590     }
591 
592     @UiThreadTest
593     @Test
testDispatchFreezeSelfOnly()594     public void testDispatchFreezeSelfOnly() {
595         mMockViewGroup.setId(1);
596         mMockViewGroup.setSaveEnabled(true);
597 
598         SparseArray container = new SparseArray();
599         assertEquals(0, container.size());
600         mMockViewGroup.dispatchFreezeSelfOnly(container);
601         assertEquals(1, container.size());
602     }
603 
604     @UiThreadTest
605     @Test
testDispatchKeyEvent()606     public void testDispatchKeyEvent() {
607         KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER);
608         assertFalse(mMockViewGroup.dispatchKeyEvent(event));
609 
610         mMockViewGroup.addView(mMockTextView);
611         mMockViewGroup.requestChildFocus(mMockTextView, null);
612         mMockTextView.layout(1, 1, 100, 100);
613 
614         assertTrue(mMockViewGroup.dispatchKeyEvent(event));
615     }
616 
617     @UiThreadTest
618     @Test
testDispatchSaveInstanceState()619     public void testDispatchSaveInstanceState() {
620         mMockViewGroup.setId(2);
621         mMockViewGroup.setSaveEnabled(true);
622         mMockTextView.setSaveEnabled(true);
623         mMockTextView.setId(1);
624         mMockViewGroup.addView(mMockTextView);
625 
626         SparseArray array = new SparseArray();
627         mMockViewGroup.dispatchSaveInstanceState(array);
628 
629         assertTrue(array.size() > 0);
630         assertNotNull(array.get(2));
631 
632         array = new SparseArray();
633         mMockViewGroup.dispatchRestoreInstanceState(array);
634         assertTrue(mMockTextView.isDispatchRestoreInstanceStateCalled);
635     }
636 
637     @UiThreadTest
638     @Test
testDispatchSetPressed()639     public void testDispatchSetPressed() {
640         mMockViewGroup.addView(mMockTextView);
641 
642         mMockViewGroup.dispatchSetPressed(true);
643         assertTrue(mMockTextView.isPressed());
644 
645         mMockViewGroup.dispatchSetPressed(false);
646         assertFalse(mMockTextView.isPressed());
647     }
648 
649     @UiThreadTest
650     @Test
testDispatchSetSelected()651     public void testDispatchSetSelected() {
652         mMockViewGroup.addView(mMockTextView);
653 
654         mMockViewGroup.dispatchSetSelected(true);
655         assertTrue(mMockTextView.isSelected());
656 
657         mMockViewGroup.dispatchSetSelected(false);
658         assertFalse(mMockTextView.isSelected());
659     }
660 
661     @UiThreadTest
662     @Test
testDispatchThawSelfOnly()663     public void testDispatchThawSelfOnly() {
664         mMockViewGroup.setId(1);
665         SparseArray array = new SparseArray();
666         array.put(1, BaseSavedState.EMPTY_STATE);
667 
668         mMockViewGroup.dispatchThawSelfOnly(array);
669         assertTrue(mMockViewGroup.isOnRestoreInstanceStateCalled);
670     }
671 
672     @UiThreadTest
673     @Test
testDispatchTouchEvent()674     public void testDispatchTouchEvent() {
675         DisplayMetrics metrics = new DisplayMetrics();
676         WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
677         Display d = wm.getDefaultDisplay();
678         d.getMetrics(metrics);
679         int screenWidth = metrics.widthPixels;
680         int screenHeight = metrics.heightPixels;
681         mMockViewGroup.layout(0, 0, screenWidth, screenHeight);
682         mMockViewGroup.setLayoutParams(new ViewGroup.LayoutParams(screenWidth, screenHeight));
683 
684         mMotionEvent = null;
685         mMockTextView.setOnTouchListener((View v, MotionEvent event) -> {
686             mMotionEvent = event;
687             return true;
688         });
689 
690         mMockTextView.setVisibility(View.VISIBLE);
691         mMockTextView.setEnabled(true);
692 
693         mMockViewGroup.addView(mMockTextView, new LayoutParams(screenWidth, screenHeight));
694 
695         mMockViewGroup.requestDisallowInterceptTouchEvent(true);
696         MotionEvent me = MotionEvent.obtain(SystemClock.uptimeMillis(),
697                 SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN,
698                 screenWidth / 2, screenHeight / 2, 0);
699 
700         assertFalse(mMockViewGroup.dispatchTouchEvent(me));
701         assertNull(mMotionEvent);
702 
703         mMockTextView.layout(0, 0, screenWidth, screenHeight);
704         assertTrue(mMockViewGroup.dispatchTouchEvent(me));
705         assertSame(me, mMotionEvent);
706     }
707 
708     @UiThreadTest
709     @Test
testDispatchTrackballEvent()710     public void testDispatchTrackballEvent() {
711         MotionEvent me = MotionEvent.obtain(SystemClock.uptimeMillis(),
712                 SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 100, 100,
713                 0);
714         assertFalse(mMockViewGroup.dispatchTrackballEvent(me));
715 
716         mMockViewGroup.addView(mMockTextView);
717         mMockTextView.layout(1, 1, 100, 100);
718         mMockViewGroup.requestChildFocus(mMockTextView, null);
719         assertTrue(mMockViewGroup.dispatchTrackballEvent(me));
720     }
721 
722     @UiThreadTest
723     @Test
testDispatchUnhandledMove()724     public void testDispatchUnhandledMove() {
725         assertFalse(mMockViewGroup.dispatchUnhandledMove(mMockTextView, View.FOCUS_DOWN));
726 
727         mMockViewGroup.addView(mMockTextView);
728         mMockTextView.layout(1, 1, 100, 100);
729         mMockViewGroup.requestChildFocus(mMockTextView, null);
730         assertTrue(mMockViewGroup.dispatchUnhandledMove(mMockTextView, View.FOCUS_DOWN));
731     }
732 
733     @UiThreadTest
734     @Test
testDispatchWindowFocusChanged()735     public void testDispatchWindowFocusChanged() {
736         mMockViewGroup.addView(mMockTextView);
737         mMockTextView.setPressed(true);
738         assertTrue(mMockTextView.isPressed());
739 
740         mMockViewGroup.dispatchWindowFocusChanged(false);
741         assertFalse(mMockTextView.isPressed());
742     }
743 
744     @UiThreadTest
745     @Test
testDispatchWindowVisibilityChanged()746     public void testDispatchWindowVisibilityChanged() {
747         int expected = 10;
748 
749         mMockViewGroup.addView(mMockTextView);
750         mMockViewGroup.dispatchWindowVisibilityChanged(expected);
751         assertEquals(expected, mMockTextView.visibility);
752     }
753 
754     @UiThreadTest
755     @Test
testDrawableStateChanged()756     public void testDrawableStateChanged() {
757         mMockTextView.setDuplicateParentStateEnabled(true);
758 
759         mMockViewGroup.addView(mMockTextView);
760         mMockViewGroup.setAddStatesFromChildren(false);
761         mMockViewGroup.drawableStateChanged();
762         assertTrue(mMockTextView.mIsRefreshDrawableStateCalled);
763     }
764 
765     @UiThreadTest
766     @Test
testDrawChild()767     public void testDrawChild() {
768         mMockViewGroup.addView(mMockTextView);
769 
770         MockCanvas canvas = new MockCanvas();
771         mMockTextView.setBackgroundDrawable(new BitmapDrawable(Bitmap.createBitmap(100, 100,
772                 Config.ALPHA_8)));
773         // Configure the size of the view to be non-empty to ensure canvas quickReject calls
774         // will not skip drawing the child
775         mMockTextView.setLeftTopRightBottom(0, 0, 100, 100);
776         assertFalse(mMockViewGroup.drawChild(canvas, mMockTextView, 100));
777         // test whether child's draw method is called.
778         assertTrue(mMockTextView.isDrawCalled);
779     }
780 
781     @UiThreadTest
782     @Test
testFindFocus()783     public void testFindFocus() {
784         assertNull(mMockViewGroup.findFocus());
785         mMockViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
786         mMockViewGroup.setFocusable(true);
787         mMockViewGroup.setVisibility(View.VISIBLE);
788         mMockViewGroup.setFocusableInTouchMode(true);
789         assertTrue(mMockViewGroup.requestFocus(1, new Rect()));
790 
791         assertSame(mMockViewGroup, mMockViewGroup.findFocus());
792     }
793 
794     static class MockView extends ViewGroup {
795 
796         public int mWidthMeasureSpec;
797         public int mHeightMeasureSpec;
798 
MockView(Context context)799         public MockView(Context context) {
800             super(context);
801         }
802 
803         @Override
onLayout(boolean changed, int l, int t, int r, int b)804         public void onLayout(boolean changed, int l, int t, int r, int b) {
805         }
806 
807         @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)808         public void onMeasure(int widthMeasureSpec,
809                 int heightMeasureSpec) {
810             mWidthMeasureSpec = widthMeasureSpec;
811             mHeightMeasureSpec = heightMeasureSpec;
812             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
813         }
814     }
815 
816     @UiThreadTest
817     @Test
testFocusableViewAvailable()818     public void testFocusableViewAvailable() {
819         MockView child = new MockView(mContext);
820         mMockViewGroup.addView(child);
821 
822         child.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
823         child.focusableViewAvailable(mMockViewGroup);
824 
825         assertTrue(mMockViewGroup.isFocusableViewAvailable);
826     }
827 
828     @UiThreadTest
829     @Test
testFocusSearch()830     public void testFocusSearch() {
831         MockView child = new MockView(mContext);
832         mMockViewGroup.addView(child);
833         child.addView(mMockTextView);
834         assertSame(mMockTextView, child.focusSearch(mMockTextView, 1));
835     }
836 
837     @UiThreadTest
838     @Test
testGatherTransparentRegion()839     public void testGatherTransparentRegion() {
840         Region region = new Region();
841         mMockTextView.setAnimation(new AlphaAnimation(mContext, null));
842         mMockTextView.setVisibility(100);
843         mMockViewGroup.addView(mMockTextView);
844         assertEquals(1, mMockViewGroup.getChildCount());
845 
846         assertTrue(mMockViewGroup.gatherTransparentRegion(region));
847         assertTrue(mMockViewGroup.gatherTransparentRegion(null));
848     }
849 
850     @UiThreadTest
851     @Test
testGenerateDefaultLayoutParams()852     public void testGenerateDefaultLayoutParams() {
853         LayoutParams lp = mMockViewGroup.generateDefaultLayoutParams();
854 
855         assertEquals(LayoutParams.WRAP_CONTENT, lp.width);
856         assertEquals(LayoutParams.WRAP_CONTENT, lp.height);
857     }
858 
859     @UiThreadTest
860     @Test
testGenerateLayoutParamsWithParaAttributeSet()861     public void testGenerateLayoutParamsWithParaAttributeSet() throws Exception {
862         XmlResourceParser set = mContext.getResources().getLayout(
863                 android.view.cts.R.layout.abslistview_layout);
864         XmlUtils.beginDocument(set, "ViewGroup_Layout");
865         LayoutParams lp = mMockViewGroup.generateLayoutParams(set);
866         assertNotNull(lp);
867         assertEquals(25, lp.height);
868         assertEquals(25, lp.width);
869     }
870 
871     @UiThreadTest
872     @Test
testGenerateLayoutParams()873     public void testGenerateLayoutParams() {
874         LayoutParams p = new LayoutParams(LayoutParams.WRAP_CONTENT,
875                 LayoutParams.MATCH_PARENT);
876         LayoutParams generatedParams = mMockViewGroup.generateLayoutParams(p);
877         assertEquals(generatedParams.getClass(), p.getClass());
878         assertEquals(p.width, generatedParams.width);
879         assertEquals(p.height, generatedParams.height);
880     }
881 
882     @UiThreadTest
883     @Test
testGetChildDrawingOrder()884     public void testGetChildDrawingOrder() {
885         assertEquals(1, mMockViewGroup.getChildDrawingOrder(0, 1));
886         assertEquals(2, mMockViewGroup.getChildDrawingOrder(0, 2));
887     }
888 
889     @Test
testGetChildMeasureSpec()890     public void testGetChildMeasureSpec() {
891         int spec = 1;
892         int padding = 1;
893         int childDimension = 1;
894         assertEquals(MeasureSpec.makeMeasureSpec(childDimension, MeasureSpec.EXACTLY),
895                 ViewGroup.getChildMeasureSpec(spec, padding, childDimension));
896         spec = 4;
897         padding = 6;
898         childDimension = 9;
899         assertEquals(MeasureSpec.makeMeasureSpec(childDimension, MeasureSpec.EXACTLY),
900                 ViewGroup.getChildMeasureSpec(spec, padding, childDimension));
901     }
902 
903     @UiThreadTest
904     @Test
testGetChildStaticTransformation()905     public void testGetChildStaticTransformation() {
906         assertFalse(mMockViewGroup.getChildStaticTransformation(null, null));
907     }
908 
909     @UiThreadTest
910     @Test
testGetChildVisibleRect()911     public void testGetChildVisibleRect() {
912         mMockTextView.layout(1, 1, 100, 100);
913         Rect rect = new Rect(1, 1, 50, 50);
914         Point p = new Point();
915         assertFalse(mMockViewGroup.getChildVisibleRect(mMockTextView, rect, p));
916 
917         mMockTextView.layout(0, 0, 0, 0);
918         mMockViewGroup.layout(20, 20, 60, 60);
919         rect = new Rect(10, 10, 40, 40);
920         p = new Point();
921         assertTrue(mMockViewGroup.getChildVisibleRect(mMockTextView, rect, p));
922     }
923 
924     @UiThreadTest
925     @Test
testGetDescendantFocusability()926     public void testGetDescendantFocusability() {
927         final int FLAG_MASK_FOCUSABILITY = 0x60000;
928         assertFalse((mMockViewGroup.getDescendantFocusability() & FLAG_MASK_FOCUSABILITY) == 0);
929 
930         mMockViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
931         assertFalse((mMockViewGroup.getDescendantFocusability() & FLAG_MASK_FOCUSABILITY) == 0);
932     }
933 
934     @UiThreadTest
935     @Test
testGetLayoutAnimation()936     public void testGetLayoutAnimation() {
937         assertNull(mMockViewGroup.getLayoutAnimation());
938         RotateAnimation animation = new RotateAnimation(0.1f, 0.1f);
939         LayoutAnimationController la = new LayoutAnimationController(animation);
940         mMockViewGroup.setLayoutAnimation(la);
941         assertTrue(mMockViewGroup.canAnimate());
942         assertSame(la, mMockViewGroup.getLayoutAnimation());
943     }
944 
945     @UiThreadTest
946     @Test
testGetLayoutAnimationListener()947     public void testGetLayoutAnimationListener() {
948         assertNull(mMockViewGroup.getLayoutAnimationListener());
949 
950         AnimationListener al = new AnimationListener() {
951             @Override
952             public void onAnimationEnd(Animation animation) {
953             }
954 
955             @Override
956             public void onAnimationRepeat(Animation animation) {
957             }
958 
959             @Override
960             public void onAnimationStart(Animation animation) {
961             }
962         };
963         mMockViewGroup.setLayoutAnimationListener(al);
964         assertSame(al, mMockViewGroup.getLayoutAnimationListener());
965     }
966 
967     @UiThreadTest
968     @Test
testGetPersistentDrawingCache()969     public void testGetPersistentDrawingCache() {
970         final int mPersistentDrawingCache1 = 2;
971         final int mPersistentDrawingCache2 = 3;
972         assertEquals(mPersistentDrawingCache1, mMockViewGroup.getPersistentDrawingCache());
973 
974         mMockViewGroup.setPersistentDrawingCache(mPersistentDrawingCache2);
975         assertEquals(mPersistentDrawingCache2, mMockViewGroup.getPersistentDrawingCache());
976     }
977 
978     @UiThreadTest
979     @Test
testHasFocus()980     public void testHasFocus() {
981         assertFalse(mMockViewGroup.hasFocus());
982 
983         mMockViewGroup.addView(mTextView);
984         mMockViewGroup.requestChildFocus(mTextView, null);
985 
986         assertTrue(mMockViewGroup.hasFocus());
987     }
988 
989     @UiThreadTest
990     @Test
testHasFocusable()991     public void testHasFocusable() {
992         assertFalse(mMockViewGroup.hasFocusable());
993 
994         mMockViewGroup.setVisibility(View.VISIBLE);
995         mMockViewGroup.setFocusable(true);
996         assertTrue(mMockViewGroup.hasFocusable());
997     }
998 
999     @UiThreadTest
1000     @Test
testIndexOfChild()1001     public void testIndexOfChild() {
1002         assertEquals(-1, mMockViewGroup.indexOfChild(mTextView));
1003 
1004         mMockViewGroup.addView(mTextView);
1005         assertEquals(0, mMockViewGroup.indexOfChild(mTextView));
1006     }
1007 
1008     @LargeTest
1009     @Test
testInvalidateChild()1010     public void testInvalidateChild() {
1011         ViewGroupInvalidateChildCtsActivity.setResult(this);
1012 
1013         Context context = InstrumentationRegistry.getTargetContext();
1014         Intent intent = new Intent(context, ViewGroupInvalidateChildCtsActivity.class);
1015         intent.setAction(ViewGroupInvalidateChildCtsActivity.ACTION_INVALIDATE_CHILD);
1016         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1017         context.startActivity(intent);
1018 
1019         waitForResult();
1020         assertEquals(CTSResult.RESULT_OK, mResultCode);
1021     }
1022 
1023     @Test
onInterceptHoverEvent_verticalCanScroll_intercepts()1024     public void onInterceptHoverEvent_verticalCanScroll_intercepts() {
1025         onInterceptHoverEvent_scrollabilityAffectsResult(true, true, true);
1026     }
1027 
1028     @Test
onInterceptHoverEvent_verticalCantScroll_doesntIntercept()1029     public void onInterceptHoverEvent_verticalCantScroll_doesntIntercept() {
1030         onInterceptHoverEvent_scrollabilityAffectsResult(true, false, false);
1031     }
1032 
1033     @Test
onInterceptHoverEvent_horizontalCanScroll_intercepts()1034     public void onInterceptHoverEvent_horizontalCanScroll_intercepts() {
1035         onInterceptHoverEvent_scrollabilityAffectsResult(false, true, true);
1036     }
1037 
1038     @Test
onInterceptHoverEvent_horizontalCantScroll_doesntIntercept()1039     public void onInterceptHoverEvent_horizontalCantScroll_doesntIntercept() {
1040         onInterceptHoverEvent_scrollabilityAffectsResult(false, false, false);
1041     }
1042 
onInterceptHoverEvent_scrollabilityAffectsResult(boolean vertical, boolean canScroll, boolean intercepts)1043     private void onInterceptHoverEvent_scrollabilityAffectsResult(boolean vertical,
1044             boolean canScroll, boolean intercepts) {
1045 
1046         // Arrange
1047 
1048         int range = canScroll ? 101 : 100;
1049 
1050         final ScrollTestView viewGroup = spy(new ScrollTestView(mContext));
1051         viewGroup.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
1052         viewGroup.setHorizontalScrollBarEnabled(true);
1053         viewGroup.setVerticalScrollBarEnabled(true);
1054         viewGroup.setScrollBarSize(10);
1055         viewGroup.layout(0, 0, 100, 100);
1056 
1057         when(viewGroup.computeVerticalScrollExtent()).thenReturn(100);
1058         when(viewGroup.computeVerticalScrollRange()).thenReturn(range);
1059         when(viewGroup.computeHorizontalScrollExtent()).thenReturn(100);
1060         when(viewGroup.computeHorizontalScrollRange()).thenReturn(range);
1061 
1062         int touchX = vertical ? 95 : 50;
1063         int touchY = vertical ? 50 : 95;
1064         MotionEvent event =
1065                 EventUtils.generateMouseEvent(touchX, touchY, MotionEvent.ACTION_HOVER_ENTER, 0);
1066 
1067         // Act
1068 
1069         boolean actualResult = viewGroup.onInterceptHoverEvent(event);
1070         event.recycle();
1071 
1072         // Assert
1073 
1074         assertEquals(actualResult, intercepts);
1075     }
1076 
1077     @Test
onInterceptTouchEvent_verticalCanScroll_intercepts()1078     public void onInterceptTouchEvent_verticalCanScroll_intercepts() {
1079         onInterceptTouchEvent_scrollabilityAffectsResult(true, true, true);
1080     }
1081 
1082     @Test
onInterceptTouchEvent_verticalCantScroll_doesntIntercept()1083     public void onInterceptTouchEvent_verticalCantScroll_doesntIntercept() {
1084         onInterceptTouchEvent_scrollabilityAffectsResult(true, false, false);
1085     }
1086 
1087     @Test
onInterceptTouchEvent_horizontalCanScroll_intercepts()1088     public void onInterceptTouchEvent_horizontalCanScroll_intercepts() {
1089         onInterceptTouchEvent_scrollabilityAffectsResult(false, true, true);
1090     }
1091 
1092     @Test
onInterceptTouchEvent_horizontalCantScroll_doesntIntercept()1093     public void onInterceptTouchEvent_horizontalCantScroll_doesntIntercept() {
1094         onInterceptTouchEvent_scrollabilityAffectsResult(false, false, false);
1095     }
1096 
onInterceptTouchEvent_scrollabilityAffectsResult(boolean vertical, boolean canScroll, boolean intercepts)1097     private void onInterceptTouchEvent_scrollabilityAffectsResult(boolean vertical,
1098             boolean canScroll, boolean intercepts) {
1099         int range = canScroll ? 101 : 100;
1100         int thumbLength = ScrollBarUtils.getThumbLength(1, 10, 100, range);
1101 
1102         PointerIcon expectedPointerIcon = PointerIcon.getSystemIcon(mContext,
1103                 PointerIcon.TYPE_HAND);
1104 
1105         final ScrollTestView viewGroup = spy(new ScrollTestView(mContext));
1106         viewGroup.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
1107         viewGroup.setHorizontalScrollBarEnabled(true);
1108         viewGroup.setVerticalScrollBarEnabled(true);
1109         viewGroup.setScrollBarSize(10);
1110         viewGroup.setPointerIcon(expectedPointerIcon);
1111         viewGroup.layout(0, 0, 100, 100);
1112 
1113         when(viewGroup.computeVerticalScrollExtent()).thenReturn(100);
1114         when(viewGroup.computeVerticalScrollRange()).thenReturn(range);
1115         when(viewGroup.computeHorizontalScrollExtent()).thenReturn(100);
1116         when(viewGroup.computeHorizontalScrollRange()).thenReturn(range);
1117 
1118         int touchX = vertical ? 95 : thumbLength / 2;
1119         int touchY = vertical ? thumbLength / 2 : 95;
1120         MotionEvent event = EventUtils.generateMouseEvent(touchX, touchY, MotionEvent.ACTION_DOWN,
1121                 MotionEvent.BUTTON_PRIMARY);
1122 
1123         // Act
1124 
1125         boolean actualResult = viewGroup.onInterceptTouchEvent(event);
1126         event.recycle();
1127 
1128         // Assert
1129 
1130         assertEquals(intercepts, actualResult);
1131     }
1132 
1133     @Test
onResolvePointerIcon_verticalCanScroll_pointerIsArrow()1134     public void onResolvePointerIcon_verticalCanScroll_pointerIsArrow() {
1135         onResolvePointerIcon_scrollabilityAffectsPointerIcon(true, true, true);
1136     }
1137 
1138     @Test
onResolvePointerIcon_verticalCantScroll_pointerIsProperty()1139     public void onResolvePointerIcon_verticalCantScroll_pointerIsProperty() {
1140         onResolvePointerIcon_scrollabilityAffectsPointerIcon(true, false, false);
1141     }
1142 
1143     @Test
onResolvePointerIcon_horizontalCanScroll_pointerIsArrow()1144     public void onResolvePointerIcon_horizontalCanScroll_pointerIsArrow() {
1145         onResolvePointerIcon_scrollabilityAffectsPointerIcon(false, true, true);
1146     }
1147 
1148     @Test
onResolvePointerIcon_horizontalCantScroll_pointerIsProperty()1149     public void onResolvePointerIcon_horizontalCantScroll_pointerIsProperty() {
1150         onResolvePointerIcon_scrollabilityAffectsPointerIcon(false, false, false);
1151     }
1152 
onResolvePointerIcon_scrollabilityAffectsPointerIcon(boolean vertical, boolean canScroll, boolean pointerIsArrayOrNull)1153     private void onResolvePointerIcon_scrollabilityAffectsPointerIcon(boolean vertical,
1154             boolean canScroll, boolean pointerIsArrayOrNull) {
1155 
1156         // Arrange
1157 
1158         int range = canScroll ? 101 : 100;
1159         int thumbLength = ScrollBarUtils.getThumbLength(1, 10, 100, range);
1160 
1161         PointerIcon expectedPointerIcon = PointerIcon.getSystemIcon(mContext,
1162                 PointerIcon.TYPE_HAND);
1163 
1164         final ScrollTestView viewGroup = spy(new ScrollTestView(mContext));
1165         viewGroup.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
1166         viewGroup.setHorizontalScrollBarEnabled(true);
1167         viewGroup.setVerticalScrollBarEnabled(true);
1168         viewGroup.setScrollBarSize(10);
1169         viewGroup.setPointerIcon(expectedPointerIcon);
1170         viewGroup.layout(0, 0, 100, 100);
1171 
1172         when(viewGroup.computeVerticalScrollExtent()).thenReturn(100);
1173         when(viewGroup.computeVerticalScrollRange()).thenReturn(range);
1174         when(viewGroup.computeHorizontalScrollExtent()).thenReturn(100);
1175         when(viewGroup.computeHorizontalScrollRange()).thenReturn(range);
1176 
1177         int touchX = vertical ? 95 : thumbLength / 2;
1178         int touchY = vertical ? thumbLength / 2 : 95;
1179         MotionEvent event =
1180                 EventUtils.generateMouseEvent(touchX, touchY, MotionEvent.ACTION_HOVER_ENTER, 0);
1181 
1182         // Act
1183 
1184         PointerIcon actualResult = viewGroup.onResolvePointerIcon(event, 0);
1185         event.recycle();
1186 
1187         // Assert
1188 
1189         if (pointerIsArrayOrNull) {
1190             // When the returned PointerIcon is null it will fallback to the system default for the
1191             // given source devices. For mouse devices, the default is TYPE_ARROW.
1192             // We also allow it to return TYPE_ARROW so that we don't break CTS test compatibility.
1193             if (actualResult != null) {
1194                 assertEquals(PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW),
1195                         actualResult);
1196             }
1197         } else {
1198             assertEquals(expectedPointerIcon, actualResult);
1199         }
1200     }
1201 
1202 
1203     @Test
testOnDescendantInvalidated()1204     public void testOnDescendantInvalidated() throws Throwable {
1205         try (ActivityScenario<CtsActivity> scenario = ActivityScenario.launch(CtsActivity.class)) {
1206             scenario.onActivity(activity -> {
1207                 View child = mTextView;
1208                 MockViewGroup parent = mMockViewGroup;
1209                 MockViewGroup grandParent = new MockViewGroup(mContext);
1210                 parent.addView(child);
1211                 grandParent.addView(parent);
1212                 activity.setContentView(grandParent);
1213 
1214                 parent.isOnDescendantInvalidatedCalled = false;
1215                 grandParent.isOnDescendantInvalidatedCalled = false;
1216 
1217                 parent.invalidateChild(child, new Rect(0, 0, 1, 1));
1218 
1219                 assertTrue(parent.isOnDescendantInvalidatedCalled);
1220                 assertTrue(grandParent.isOnDescendantInvalidatedCalled);
1221 
1222                 parent.isOnDescendantInvalidatedCalled = false;
1223                 grandParent.isOnDescendantInvalidatedCalled = false;
1224 
1225                 grandParent.invalidateChild(child, new Rect(0, 0, 1, 1));
1226 
1227                 assertFalse(parent.isOnDescendantInvalidatedCalled);
1228                 assertTrue(grandParent.isOnDescendantInvalidatedCalled);
1229             });
1230         }
1231     }
1232 
waitForResult()1233     private void waitForResult() {
1234         synchronized (mSync) {
1235             while (!mSync.mHasNotify) {
1236                 try {
1237                     mSync.wait();
1238                 } catch (InterruptedException e) {
1239                     Thread.currentThread().interrupt();
1240                     throw new RuntimeException("Interrupted thread", e);
1241                 }
1242             }
1243         }
1244     }
1245 
1246     @UiThreadTest
1247     @Test
testIsAlwaysDrawnWithCacheEnabled()1248     public void testIsAlwaysDrawnWithCacheEnabled() {
1249         assertTrue(mMockViewGroup.isAlwaysDrawnWithCacheEnabled());
1250 
1251         mMockViewGroup.setAlwaysDrawnWithCacheEnabled(false);
1252         assertFalse(mMockViewGroup.isAlwaysDrawnWithCacheEnabled());
1253         mMockViewGroup.setAlwaysDrawnWithCacheEnabled(true);
1254         assertTrue(mMockViewGroup.isAlwaysDrawnWithCacheEnabled());
1255     }
1256 
1257     @UiThreadTest
1258     @Test
testIsAnimationCacheEnabled()1259     public void testIsAnimationCacheEnabled() {
1260         assertTrue(mMockViewGroup.isAnimationCacheEnabled());
1261 
1262         mMockViewGroup.setAnimationCacheEnabled(false);
1263         assertFalse(mMockViewGroup.isAnimationCacheEnabled());
1264         mMockViewGroup.setAnimationCacheEnabled(true);
1265         assertTrue(mMockViewGroup.isAnimationCacheEnabled());
1266     }
1267 
1268     @UiThreadTest
1269     @Test
testIsChildrenDrawnWithCacheEnabled()1270     public void testIsChildrenDrawnWithCacheEnabled() {
1271         assertFalse(mMockViewGroup.isChildrenDrawnWithCacheEnabled());
1272 
1273         mMockViewGroup.setChildrenDrawnWithCacheEnabled(true);
1274         assertTrue(mMockViewGroup.isChildrenDrawnWithCacheEnabled());
1275     }
1276 
1277     @UiThreadTest
1278     @Test
testMeasureChild()1279     public void testMeasureChild() {
1280         final int width = 100;
1281         final int height = 200;
1282         MockView child = new MockView(mContext);
1283         child.setLayoutParams(new LayoutParams(width, height));
1284         child.forceLayout();
1285         mMockViewGroup.addView(child);
1286 
1287         final int parentWidthMeasureSpec = 1;
1288         final int parentHeightMeasureSpec = 2;
1289         mMockViewGroup.measureChild(child, parentWidthMeasureSpec, parentHeightMeasureSpec);
1290         assertEquals(ViewGroup.getChildMeasureSpec(parentWidthMeasureSpec, 0, width),
1291                 child.mWidthMeasureSpec);
1292         assertEquals(ViewGroup.getChildMeasureSpec(parentHeightMeasureSpec, 0, height),
1293                 child.mHeightMeasureSpec);
1294     }
1295 
1296     @UiThreadTest
1297     @Test
testMeasureChildren()1298     public void testMeasureChildren() {
1299         final int widthMeasureSpec = 100;
1300         final int heightMeasureSpec = 200;
1301         MockTextView textView1 = new MockTextView(mContext);
1302 
1303         mMockViewGroup.addView(textView1);
1304         mMockViewGroup.measureChildCalledTime = 0;
1305         mMockViewGroup.measureChildren(widthMeasureSpec, heightMeasureSpec);
1306         assertEquals(1, mMockViewGroup.measureChildCalledTime);
1307 
1308         MockTextView textView2 = new MockTextView(mContext);
1309         textView2.setVisibility(View.GONE);
1310         mMockViewGroup.addView(textView2);
1311 
1312         mMockViewGroup.measureChildCalledTime = 0;
1313         mMockViewGroup.measureChildren(widthMeasureSpec, heightMeasureSpec);
1314         assertEquals(1, mMockViewGroup.measureChildCalledTime);
1315     }
1316 
1317     @UiThreadTest
1318     @Test
testMeasureChildWithMargins()1319     public void testMeasureChildWithMargins() {
1320         final int width = 10;
1321         final int height = 20;
1322         final int parentWidthMeasureSpec = 1;
1323         final int widthUsed = 2;
1324         final int parentHeightMeasureSpec = 3;
1325         final int heightUsed = 4;
1326         MockView child = new MockView(mContext);
1327 
1328         mMockViewGroup.addView(child);
1329         child.setLayoutParams(new ViewGroup.LayoutParams(width, height));
1330         try {
1331             mMockViewGroup.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
1332                     parentHeightMeasureSpec, heightUsed);
1333             fail("measureChildWithMargins should throw out class cast exception");
1334         } catch (RuntimeException e) {
1335         }
1336         child.setLayoutParams(new ViewGroup.MarginLayoutParams(width, height));
1337 
1338         mMockViewGroup.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
1339                 parentHeightMeasureSpec, heightUsed);
1340         assertEquals(ViewGroup.getChildMeasureSpec(parentWidthMeasureSpec, parentHeightMeasureSpec,
1341                 width), child.mWidthMeasureSpec);
1342         assertEquals(ViewGroup.getChildMeasureSpec(widthUsed, heightUsed, height),
1343                 child.mHeightMeasureSpec);
1344     }
1345 
1346     @UiThreadTest
1347     @Test
testOffsetDescendantRectToMyCoords()1348     public void testOffsetDescendantRectToMyCoords() {
1349         try {
1350             mMockViewGroup.offsetDescendantRectToMyCoords(mMockTextView, new Rect());
1351             fail("offsetDescendantRectToMyCoords should throw out "
1352                     + "IllegalArgumentException");
1353         } catch (RuntimeException e) {
1354             // expected
1355         }
1356         mMockViewGroup.addView(mMockTextView);
1357         mMockTextView.layout(1, 2, 3, 4);
1358         Rect rect = new Rect();
1359         mMockViewGroup.offsetDescendantRectToMyCoords(mMockTextView, rect);
1360         assertEquals(2, rect.bottom);
1361         assertEquals(2, rect.top);
1362         assertEquals(1, rect.left);
1363         assertEquals(1, rect.right);
1364     }
1365 
1366     @UiThreadTest
1367     @Test
testOffsetRectIntoDescendantCoords()1368     public void testOffsetRectIntoDescendantCoords() {
1369         mMockViewGroup.layout(10, 20, 30, 40);
1370 
1371         try {
1372             mMockViewGroup.offsetRectIntoDescendantCoords(mMockTextView, new Rect());
1373             fail("offsetRectIntoDescendantCoords should throw out "
1374                     + "IllegalArgumentException");
1375         } catch (RuntimeException e) {
1376             // expected
1377         }
1378         mMockTextView.layout(1, 2, 3, 4);
1379         mMockViewGroup.addView(mMockTextView);
1380 
1381         Rect rect = new Rect(5, 6, 7, 8);
1382         mMockViewGroup.offsetRectIntoDescendantCoords(mMockTextView, rect);
1383         assertEquals(6, rect.bottom);
1384         assertEquals(4, rect.top);
1385         assertEquals(4, rect.left);
1386         assertEquals(6, rect.right);
1387     }
1388 
1389     @UiThreadTest
1390     @Test
testOnAnimationEnd()1391     public void testOnAnimationEnd() {
1392         // this function is a call back function it should be tested in ViewGroup#drawChild.
1393         MockViewGroup parent = new MockViewGroup(mContext);
1394         MockViewGroup child = new MockViewGroup(mContext);
1395         child.setAnimation(new MockAnimation());
1396         // this call will make mPrivateFlags |= ANIMATION_STARTED;
1397         child.onAnimationStart();
1398         parent.addView(child);
1399 
1400         MockCanvas canvas = new MockCanvas();
1401         assertFalse(parent.drawChild(canvas, child, 100));
1402         assertTrue(child.isOnAnimationEndCalled);
1403     }
1404 
1405     private class MockAnimation extends Animation {
MockAnimation()1406         public MockAnimation() {
1407             super();
1408         }
1409 
MockAnimation(Context context, AttributeSet attrs)1410         public MockAnimation(Context context, AttributeSet attrs) {
1411             super(context, attrs);
1412         }
1413 
1414         @Override
getTransformation(long currentTime, Transformation outTransformation)1415         public boolean getTransformation(long currentTime, Transformation outTransformation) {
1416             super.getTransformation(currentTime, outTransformation);
1417             return false;
1418         }
1419     }
1420 
1421     @UiThreadTest
1422     @Test
testOnAnimationStart()1423     public void testOnAnimationStart() {
1424         // This is a call back method. It should be tested in ViewGroup#drawChild.
1425         MockViewGroup parent = new MockViewGroup(mContext);
1426         MockViewGroup child = new MockViewGroup(mContext);
1427 
1428         parent.addView(child);
1429 
1430         MockCanvas canvas = new MockCanvas();
1431         try {
1432             assertFalse(parent.drawChild(canvas, child, 100));
1433             assertFalse(child.isOnAnimationStartCalled);
1434         } catch (Exception e) {
1435             // expected
1436         }
1437 
1438         child.setAnimation(new MockAnimation());
1439         assertFalse(parent.drawChild(canvas, child, 100));
1440         assertTrue(child.isOnAnimationStartCalled);
1441     }
1442 
1443     @UiThreadTest
1444     @Test
testOnCreateDrawableState()1445     public void testOnCreateDrawableState() {
1446         // Call back function. Called in View#getDrawableState()
1447         int[] data = mMockViewGroup.getDrawableState();
1448         assertTrue(mMockViewGroup.isOnCreateDrawableStateCalled);
1449         assertEquals(1, data.length);
1450     }
1451 
1452     @UiThreadTest
1453     @Test
testOnInterceptTouchEvent()1454     public void testOnInterceptTouchEvent() {
1455         MotionEvent me = MotionEvent.obtain(SystemClock.uptimeMillis(),
1456                 SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 100, 100, 0);
1457 
1458         assertFalse(mMockViewGroup.dispatchTouchEvent(me));
1459         assertTrue(mMockViewGroup.isOnInterceptTouchEventCalled);
1460     }
1461 
1462     @UiThreadTest
1463     @Test
testOnLayout()1464     public void testOnLayout() {
1465         final int left = 1;
1466         final int top = 2;
1467         final int right = 100;
1468         final int bottom = 200;
1469         mMockViewGroup.layout(left, top, right, bottom);
1470         assertEquals(left, mMockViewGroup.left);
1471         assertEquals(top, mMockViewGroup.top);
1472         assertEquals(right, mMockViewGroup.right);
1473         assertEquals(bottom, mMockViewGroup.bottom);
1474     }
1475 
1476     @UiThreadTest
1477     @Test
testOnRequestFocusInDescendants()1478     public void testOnRequestFocusInDescendants() {
1479         mMockViewGroup.requestFocus(View.FOCUS_DOWN, new Rect());
1480         assertTrue(mMockViewGroup.isOnRequestFocusInDescendantsCalled);
1481     }
1482 
1483     @UiThreadTest
1484     @Test
testRemoveAllViews()1485     public void testRemoveAllViews() {
1486         assertEquals(0, mMockViewGroup.getChildCount());
1487 
1488         mMockViewGroup.addView(mMockTextView);
1489         assertEquals(1, mMockViewGroup.getChildCount());
1490 
1491         mMockViewGroup.removeAllViews();
1492         assertEquals(0, mMockViewGroup.getChildCount());
1493         assertNull(mMockTextView.getParent());
1494     }
1495 
1496     @UiThreadTest
1497     @Test
testRemoveAllViewsInLayout()1498     public void testRemoveAllViewsInLayout() {
1499         MockViewGroup parent = new MockViewGroup(mContext);
1500         MockViewGroup child = new MockViewGroup(mContext);
1501 
1502         assertEquals(0, parent.getChildCount());
1503 
1504         child.addView(mMockTextView);
1505         parent.addView(child);
1506         assertEquals(1, parent.getChildCount());
1507 
1508         parent.removeAllViewsInLayout();
1509         assertEquals(0, parent.getChildCount());
1510         assertEquals(1, child.getChildCount());
1511         assertNull(child.getParent());
1512         assertSame(child, mMockTextView.getParent());
1513     }
1514 
1515     @UiThreadTest
1516     @Test
testRemoveDetachedView()1517     public void testRemoveDetachedView() {
1518         MockViewGroup parent = new MockViewGroup(mContext);
1519         MockViewGroup child1 = new MockViewGroup(mContext);
1520         MockViewGroup child2 = new MockViewGroup(mContext);
1521         ViewGroup.OnHierarchyChangeListener listener =
1522                 mock(ViewGroup.OnHierarchyChangeListener.class);
1523         parent.setOnHierarchyChangeListener(listener);
1524         parent.addView(child1);
1525         parent.addView(child2);
1526 
1527         parent.removeDetachedView(child1, false);
1528 
1529         InOrder inOrder = inOrder(listener);
1530         inOrder.verify(listener, times(1)).onChildViewAdded(parent, child1);
1531         inOrder.verify(listener, times(1)).onChildViewAdded(parent, child2);
1532         inOrder.verify(listener, times(1)).onChildViewRemoved(parent, child1);
1533     }
1534 
1535     @UiThreadTest
1536     @Test
testRemoveView()1537     public void testRemoveView() {
1538         MockViewGroup parent = new MockViewGroup(mContext);
1539         MockViewGroup child = new MockViewGroup(mContext);
1540 
1541         assertEquals(0, parent.getChildCount());
1542 
1543         parent.addView(child);
1544         assertEquals(1, parent.getChildCount());
1545 
1546         parent.removeView(child);
1547         assertEquals(0, parent.getChildCount());
1548         assertNull(child.getParent());
1549         assertTrue(parent.isOnViewRemovedCalled);
1550     }
1551 
1552     @UiThreadTest
1553     @Test
testRemoveViewAt()1554     public void testRemoveViewAt() {
1555         MockViewGroup parent = new MockViewGroup(mContext);
1556         MockViewGroup child = new MockViewGroup(mContext);
1557 
1558         assertEquals(0, parent.getChildCount());
1559 
1560         parent.addView(child);
1561         assertEquals(1, parent.getChildCount());
1562 
1563         try {
1564             parent.removeViewAt(2);
1565             fail("should throw out null pointer exception");
1566         } catch (RuntimeException e) {
1567             // expected
1568         }
1569         assertEquals(1, parent.getChildCount());
1570 
1571         parent.removeViewAt(0);
1572         assertEquals(0, parent.getChildCount());
1573         assertNull(child.getParent());
1574         assertTrue(parent.isOnViewRemovedCalled);
1575     }
1576 
1577     @UiThreadTest
1578     @Test
testRemoveViewInLayout()1579     public void testRemoveViewInLayout() {
1580         MockViewGroup parent = new MockViewGroup(mContext);
1581         MockViewGroup child = new MockViewGroup(mContext);
1582 
1583         assertEquals(0, parent.getChildCount());
1584 
1585         parent.addView(child);
1586         assertEquals(1, parent.getChildCount());
1587 
1588         parent.removeViewInLayout(child);
1589         assertEquals(0, parent.getChildCount());
1590         assertNull(child.getParent());
1591         assertTrue(parent.isOnViewRemovedCalled);
1592     }
1593 
1594     @UiThreadTest
1595     @Test
testRemoveViews()1596     public void testRemoveViews() {
1597         MockViewGroup parent = new MockViewGroup(mContext);
1598         MockViewGroup child1 = new MockViewGroup(mContext);
1599         MockViewGroup child2 = new MockViewGroup(mContext);
1600 
1601         assertEquals(0, parent.getChildCount());
1602         parent.addView(child1);
1603         parent.addView(child2);
1604         assertEquals(2, parent.getChildCount());
1605 
1606         try {
1607             parent.removeViews(-1, 1); // negative begin
1608             fail("should fail with IndexOutOfBoundsException");
1609         } catch (IndexOutOfBoundsException e) {}
1610 
1611         try {
1612             parent.removeViews(0, -1); // negative count
1613             fail("should fail with IndexOutOfBoundsException");
1614         } catch (IndexOutOfBoundsException e) {}
1615 
1616         try {
1617             parent.removeViews(1, 2); // past end
1618             fail("should fail with IndexOutOfBoundsException");
1619         } catch (IndexOutOfBoundsException e) {}
1620         assertEquals(2, parent.getChildCount()); // child list unmodified
1621 
1622         parent.removeViews(0, 1);
1623         assertEquals(1, parent.getChildCount());
1624         assertNull(child1.getParent());
1625 
1626         parent.removeViews(0, 1);
1627         assertEquals(0, parent.getChildCount());
1628         assertNull(child2.getParent());
1629         assertTrue(parent.isOnViewRemovedCalled);
1630     }
1631 
1632     @UiThreadTest
1633     @Test
testRemoveViewsInLayout()1634     public void testRemoveViewsInLayout() {
1635         MockViewGroup parent = new MockViewGroup(mContext);
1636         MockViewGroup child1 = new MockViewGroup(mContext);
1637         MockViewGroup child2 = new MockViewGroup(mContext);
1638 
1639         assertEquals(0, parent.getChildCount());
1640         parent.addView(child1);
1641         parent.addView(child2);
1642         assertEquals(2, parent.getChildCount());
1643 
1644         try {
1645             parent.removeViewsInLayout(-1, 1); // negative begin
1646             fail("should fail with IndexOutOfBoundsException");
1647         } catch (IndexOutOfBoundsException e) {}
1648 
1649         try {
1650             parent.removeViewsInLayout(0, -1); // negative count
1651             fail("should fail with IndexOutOfBoundsException");
1652         } catch (IndexOutOfBoundsException e) {}
1653 
1654         try {
1655             parent.removeViewsInLayout(1, 2); // past end
1656             fail("should fail with IndexOutOfBoundsException");
1657         } catch (IndexOutOfBoundsException e) {}
1658         assertEquals(2, parent.getChildCount()); // child list unmodified
1659 
1660         parent.removeViewsInLayout(0, 1);
1661         assertEquals(1, parent.getChildCount());
1662         assertNull(child1.getParent());
1663 
1664         parent.removeViewsInLayout(0, 1);
1665         assertEquals(0, parent.getChildCount());
1666         assertNull(child2.getParent());
1667         assertTrue(parent.isOnViewRemovedCalled);
1668     }
1669 
1670     @UiThreadTest
1671     @Test
testRequestChildFocus()1672     public void testRequestChildFocus() {
1673         mMockViewGroup.addView(mTextView);
1674         mMockViewGroup.requestChildFocus(mTextView, null);
1675 
1676         assertNotNull(mMockViewGroup.getFocusedChild());
1677 
1678         mMockViewGroup.clearChildFocus(mTextView);
1679         assertNull(mMockViewGroup.getFocusedChild());
1680     }
1681 
1682     @UiThreadTest
1683     @Test
testRequestChildRectangleOnScreen()1684     public void testRequestChildRectangleOnScreen() {
1685         assertFalse(mMockViewGroup.requestChildRectangleOnScreen(null, null, false));
1686     }
1687 
1688     @UiThreadTest
1689     @Test
testRequestDisallowInterceptTouchEvent()1690     public void testRequestDisallowInterceptTouchEvent() {
1691         MockView child = new MockView(mContext);
1692 
1693         mMockViewGroup.addView(child);
1694         child.requestDisallowInterceptTouchEvent(true);
1695         child.requestDisallowInterceptTouchEvent(false);
1696         assertTrue(mMockViewGroup.isRequestDisallowInterceptTouchEventCalled);
1697     }
1698 
1699     @UiThreadTest
1700     @Test
testRequestFocus()1701     public void testRequestFocus() {
1702         mMockViewGroup.requestFocus(View.FOCUS_DOWN, new Rect());
1703         assertTrue(mMockViewGroup.isOnRequestFocusInDescendantsCalled);
1704     }
1705 
1706     private class TestClusterHier {
1707         public MockViewGroup top = new MockViewGroup(mContext);
1708         public MockViewGroup cluster1 = new MockViewGroup(mContext);
1709         public Button c1view1 = new Button(mContext);
1710         public Button c1view2 = new Button(mContext);
1711         public MockViewGroup cluster2 = new MockViewGroup(mContext);
1712         public MockViewGroup nestedGroup = new MockViewGroup(mContext);
1713         public Button c2view1 = new Button(mContext);
1714         public Button c2view2 = new Button(mContext);
1715 
TestClusterHier()1716         TestClusterHier() {
1717             this(true);
1718         }
1719 
TestClusterHier(boolean inTouchMode)1720         TestClusterHier(boolean inTouchMode) {
1721             for (Button bt : new Button[]{c1view1, c1view2, c2view1, c2view2}) {
1722                 // Otherwise this test won't work during suite-run.
1723                 bt.setFocusableInTouchMode(inTouchMode);
1724             }
1725             for (MockViewGroup mvg : new MockViewGroup[]{top, cluster1, cluster2, nestedGroup}) {
1726                 mvg.returnActualFocusSearchResult = true;
1727             }
1728             top.setIsRootNamespace(true);
1729             cluster1.setKeyboardNavigationCluster(true);
1730             cluster2.setKeyboardNavigationCluster(true);
1731             cluster1.addView(c1view1);
1732             cluster1.addView(c1view2);
1733             cluster2.addView(c2view1);
1734             nestedGroup.addView(c2view2);
1735             cluster2.addView(nestedGroup);
1736             top.addView(cluster1);
1737             top.addView(cluster2);
1738         }
1739     }
1740 
1741     @UiThreadTest
1742     @Test
testRestoreFocusInCluster()1743     public void testRestoreFocusInCluster() {
1744         TestClusterHier h = new TestClusterHier();
1745         h.cluster1.restoreFocusInCluster(View.FOCUS_DOWN);
1746         assertSame(h.c1view1, h.top.findFocus());
1747 
1748         h.cluster2.restoreFocusInCluster(View.FOCUS_DOWN);
1749         assertSame(h.c2view1, h.top.findFocus());
1750 
1751         h.c2view2.setFocusedInCluster();
1752         h.cluster2.restoreFocusInCluster(View.FOCUS_DOWN);
1753         assertSame(h.c2view2, h.top.findFocus());
1754         h.c2view1.setFocusedInCluster();
1755         h.cluster2.restoreFocusInCluster(View.FOCUS_DOWN);
1756         assertSame(h.c2view1, h.top.findFocus());
1757 
1758         h.c1view2.setFocusedInCluster();
1759         h.cluster1.restoreFocusInCluster(View.FOCUS_DOWN);
1760         assertSame(h.c1view2, h.top.findFocus());
1761 
1762         h = new TestClusterHier();
1763         h.cluster1.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
1764         h.cluster1.restoreFocusInCluster(View.FOCUS_DOWN);
1765         assertNull(h.top.findFocus());
1766 
1767         h.c2view1.setVisibility(View.INVISIBLE);
1768         h.cluster2.restoreFocusInCluster(View.FOCUS_DOWN);
1769         assertSame(h.c2view2, h.top.findFocus());
1770 
1771         // Nested clusters should be ignored.
1772         h = new TestClusterHier();
1773         h.c1view1.setFocusedInCluster();
1774         h.nestedGroup.setKeyboardNavigationCluster(true);
1775         h.c2view2.setFocusedInCluster();
1776         h.cluster2.restoreFocusInCluster(View.FOCUS_DOWN);
1777         assertSame(h.c2view2, h.top.findFocus());
1778     }
1779 
1780     @UiThreadTest
1781     @Test
testDefaultCluster()1782     public void testDefaultCluster() {
1783         TestClusterHier h = new TestClusterHier();
1784         h.cluster2.setKeyboardNavigationCluster(false);
1785         assertTrue(h.top.restoreFocusNotInCluster());
1786         assertSame(h.c2view1, h.top.findFocus());
1787 
1788         // Check saves state within non-cluster
1789         h = new TestClusterHier();
1790         h.cluster2.setKeyboardNavigationCluster(false);
1791         h.c2view2.setFocusedInCluster();
1792         assertTrue(h.top.restoreFocusNotInCluster());
1793         assertSame(h.c2view2, h.top.findFocus());
1794 
1795         // Check that focusable view groups have descendantFocusability honored.
1796         h = new TestClusterHier();
1797         h.cluster2.setKeyboardNavigationCluster(false);
1798         h.cluster2.setFocusableInTouchMode(true);
1799         h.cluster2.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
1800         assertTrue(h.top.restoreFocusNotInCluster());
1801         assertSame(h.c2view1, h.top.findFocus());
1802         h = new TestClusterHier();
1803         h.cluster2.setKeyboardNavigationCluster(false);
1804         h.cluster2.setFocusableInTouchMode(true);
1805         h.cluster2.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
1806         assertTrue(h.top.restoreFocusNotInCluster());
1807         assertSame(h.cluster2, h.top.findFocus());
1808 
1809         // Check that we return false if nothing out-of-cluster is focusable
1810         // (also tests FOCUS_BLOCK_DESCENDANTS)
1811         h = new TestClusterHier();
1812         h.cluster2.setKeyboardNavigationCluster(false);
1813         h.cluster2.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
1814         assertFalse(h.top.restoreFocusNotInCluster());
1815         assertNull(h.top.findFocus());
1816     }
1817 
1818     @UiThreadTest
1819     @Test
testFocusInClusterRemovals()1820     public void testFocusInClusterRemovals() {
1821         // Removing focused-in-cluster view from its parent in various ways.
1822         TestClusterHier h = new TestClusterHier();
1823         h.c1view1.setFocusedInCluster();
1824         h.cluster1.removeView(h.c1view1);
1825         h.cluster1.restoreFocusInCluster(View.FOCUS_DOWN);
1826         assertSame(h.c1view2, h.cluster1.findFocus());
1827 
1828         h = new TestClusterHier();
1829         h.c1view1.setFocusedInCluster();
1830         h.cluster1.removeViews(0, 1);
1831         h.cluster1.restoreFocusInCluster(View.FOCUS_DOWN);
1832         assertSame(h.c1view2, h.cluster1.findFocus());
1833 
1834         h = new TestClusterHier();
1835         h.c2view1.setFocusedInCluster();
1836         h.cluster2.removeAllViewsInLayout();
1837         h.cluster2.restoreFocusInCluster(View.FOCUS_DOWN);
1838         assertNull(h.cluster2.findFocus());
1839 
1840         h = new TestClusterHier();
1841         h.c1view1.setFocusedInCluster();
1842         h.cluster1.detachViewFromParent(h.c1view1);
1843         h.cluster1.attachViewToParent(h.c1view1, 1, null);
1844         h.cluster1.restoreFocusInCluster(View.FOCUS_DOWN);
1845         assertSame(h.c1view1, h.cluster1.findFocus());
1846 
1847         h = new TestClusterHier();
1848         h.c1view1.setFocusedInCluster();
1849         h.cluster1.detachViewFromParent(h.c1view1);
1850         h.cluster1.removeDetachedView(h.c1view1, false);
1851         h.cluster1.restoreFocusInCluster(View.FOCUS_DOWN);
1852         assertSame(h.c1view2, h.cluster1.findFocus());
1853     }
1854 
1855     @UiThreadTest
1856     @Test
testFocusInClusterFocusableChanges()1857     public void testFocusInClusterFocusableChanges() {
1858         TestClusterHier h = new TestClusterHier();
1859         h.cluster1.setKeyboardNavigationCluster(false);
1860         h.c1view2.setFocusedInCluster();
1861         h.c2view1.requestFocus();
1862         assertSame(h.top.findFocus(), h.c2view1);
1863         assertTrue(h.top.restoreFocusNotInCluster());
1864         assertSame(h.top.findFocus(), h.c1view2);
1865         h.c1view1.setFocusable(false);
1866         // making it invisible should clear focusNotInCluster chain
1867         h.c1view2.setVisibility(View.INVISIBLE);
1868         assertFalse(h.top.restoreFocusNotInCluster());
1869         h.c1view2.setVisibility(View.VISIBLE);
1870         h.c1view2.requestFocus();
1871         h.c1view2.setFocusedInCluster();
1872         h.c2view1.setFocusable(false);
1873         h.c2view2.setFocusable(false);
1874         assertFalse(h.cluster2.restoreFocusInCluster(View.FOCUS_DOWN));
1875     }
1876 
1877     @UiThreadTest
1878     @Test
testRestoreDefaultFocus()1879     public void testRestoreDefaultFocus() {
1880         TestClusterHier h = new TestClusterHier();
1881         h.c1view2.setFocusedByDefault(true);
1882         h.top.restoreDefaultFocus();
1883         assertSame(h.c1view2, h.top.findFocus());
1884 
1885         h.c1view2.setFocusedByDefault(false);
1886         h.top.restoreDefaultFocus();
1887         assertSame(h.c1view1, h.top.findFocus());
1888 
1889         // default focus favors higher-up views
1890         h.c1view2.setFocusedByDefault(true);
1891         h.cluster1.setFocusedByDefault(true);
1892         h.top.restoreDefaultFocus();
1893         assertSame(h.c1view2, h.top.findFocus());
1894         h.c2view1.setFocusedByDefault(true);
1895         h.top.restoreDefaultFocus();
1896         assertSame(h.c1view2, h.top.findFocus());
1897         h.cluster2.setFocusedByDefault(true);
1898         h.cluster1.setFocusedByDefault(false);
1899         h.top.restoreDefaultFocus();
1900         assertSame(h.c2view1, h.top.findFocus());
1901 
1902         // removing default receivers should resolve to an existing default
1903         h = new TestClusterHier();
1904         h.c1view2.setFocusedByDefault(true);
1905         h.cluster1.setFocusedByDefault(true);
1906         h.c2view2.setFocusedByDefault(true);
1907         h.top.restoreDefaultFocus();
1908         assertSame(h.c1view2, h.top.findFocus());
1909         h.c1view2.setFocusedByDefault(false);
1910         h.cluster1.setFocusedByDefault(false);
1911         // only 1 focused-by-default view left, but its in a different branch. Should still pull
1912         // default focus.
1913         h.top.restoreDefaultFocus();
1914         assertSame(h.c2view2, h.top.findFocus());
1915     }
1916 
1917     @UiThreadTest
1918     @Test
testDefaultFocusViewRemoved()1919     public void testDefaultFocusViewRemoved() {
1920         // Removing default-focus view from its parent in various ways.
1921         TestClusterHier h = new TestClusterHier();
1922         h.c1view1.setFocusedByDefault(true);
1923         h.cluster1.removeView(h.c1view1);
1924         h.cluster1.restoreDefaultFocus();
1925         assertSame(h.c1view2, h.cluster1.findFocus());
1926 
1927         h = new TestClusterHier();
1928         h.c1view1.setFocusedByDefault(true);
1929         h.cluster1.removeViews(0, 1);
1930         h.cluster1.restoreDefaultFocus();
1931         assertSame(h.c1view2, h.cluster1.findFocus());
1932 
1933         h = new TestClusterHier();
1934         h.c1view1.setFocusedByDefault(true);
1935         h.cluster1.removeAllViewsInLayout();
1936         h.cluster1.restoreDefaultFocus();
1937         assertNull(h.cluster1.findFocus());
1938 
1939         h = new TestClusterHier();
1940         h.c1view1.setFocusedByDefault(true);
1941         h.cluster1.detachViewFromParent(h.c1view1);
1942         h.cluster1.attachViewToParent(h.c1view1, 1, null);
1943         h.cluster1.restoreDefaultFocus();
1944         assertSame(h.c1view1, h.cluster1.findFocus());
1945 
1946         h = new TestClusterHier();
1947         h.c1view1.setFocusedByDefault(true);
1948         h.cluster1.detachViewFromParent(h.c1view1);
1949         h.cluster1.removeDetachedView(h.c1view1, false);
1950         h.cluster1.restoreDefaultFocus();
1951         assertSame(h.c1view2, h.cluster1.findFocus());
1952     }
1953 
1954     @UiThreadTest
1955     @Test
testAddViewWithDefaultFocus()1956     public void testAddViewWithDefaultFocus() {
1957         // Adding a view that has default focus propagates the default focus chain to the root.
1958         mMockViewGroup = new MockViewGroup(mContext);
1959         mMockTextView = new MockTextView(mContext);
1960         mMockTextView.setFocusable(true);
1961         mTextView = new TextView(mContext);
1962         mTextView.setFocusable(true);
1963         mTextView.setFocusableInTouchMode(true);
1964         mTextView.setFocusedByDefault(true);
1965         mMockViewGroup.addView(mMockTextView);
1966         mMockViewGroup.addView(mTextView);
1967         mMockViewGroup.restoreDefaultFocus();
1968         assertTrue(mTextView.isFocused());
1969     }
1970 
1971     @UiThreadTest
1972     @Test
testDefaultFocusWorksForClusters()1973     public void testDefaultFocusWorksForClusters() {
1974         TestClusterHier h = new TestClusterHier();
1975         h.c2view2.setFocusedByDefault(true);
1976         h.cluster1.setFocusedByDefault(true);
1977         h.top.restoreDefaultFocus();
1978         assertSame(h.c1view1, h.top.findFocus());
1979         h.cluster2.restoreFocusInCluster(View.FOCUS_DOWN);
1980         assertSame(h.c2view2, h.top.findFocus());
1981 
1982         // make sure focused in cluster takes priority in cluster-focus
1983         h.c1view2.setFocusedByDefault(true);
1984         h.c1view1.setFocusedInCluster();
1985         h.cluster1.restoreFocusInCluster(View.FOCUS_DOWN);
1986         assertSame(h.c1view1, h.top.findFocus());
1987     }
1988 
1989     @Test
testTouchscreenBlocksFocus()1990     public void testTouchscreenBlocksFocus() {
1991         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)) {
1992             return;
1993         }
1994 
1995         final ArrayList<View> views = new ArrayList<>();
1996         try (ActivityScenario<CtsActivity> scenario = ActivityScenario.launch(CtsActivity.class)) {
1997 
1998             // Can't focus/default-focus an element in touchscreenBlocksFocus
1999             final TestClusterHier h = new TestClusterHier(/* inTouchMode= */ false);
2000             // Attach top view group, so touch mode gets affected when set to false
2001             scenario.onActivity(a -> a.setContentView(h.top));
2002             InstrumentationRegistry.getInstrumentation().setInTouchMode(false);
2003             PollingCheck.waitFor(5_000L, () -> !h.top.isInTouchMode());
2004 
2005             scenario.onActivity(a -> {
2006                 h.cluster1.setTouchscreenBlocksFocus(true);
2007                 h.c1view2.setFocusedByDefault(true);
2008                 h.top.restoreDefaultFocus();
2009                 assertSame(h.c2view1, h.top.findFocus());
2010                 h.top.addFocusables(views, View.FOCUS_DOWN);
2011                 for (View v : views) {
2012                     assertFalse(v.getParent() == h.cluster1);
2013                 }
2014                 views.clear();
2015 
2016                 // Can cluster navigate into it though
2017                 h.top.addKeyboardNavigationClusters(views, View.FOCUS_DOWN);
2018                 assertTrue(views.contains(h.cluster1));
2019                 views.clear();
2020                 h.cluster1.restoreFocusInCluster(View.FOCUS_DOWN);
2021                 assertSame(h.c1view2, h.top.findFocus());
2022                 // can normal-navigate around once inside
2023                 h.top.addFocusables(views, View.FOCUS_DOWN);
2024                 assertTrue(views.contains(h.c1view1));
2025                 views.clear();
2026                 h.c1view1.requestFocus();
2027                 assertSame(h.c1view1, h.top.findFocus());
2028                 // focus loops within cluster (doesn't leave)
2029                 h.c1view2.requestFocus();
2030                 View next = h.top.focusSearch(h.c1view2, View.FOCUS_FORWARD);
2031                 assertSame(h.c1view1, next);
2032                 // but once outside, can no-longer navigate in.
2033                 h.c2view2.requestFocus();
2034                 h.c1view1.requestFocus();
2035                 assertSame(h.c2view2, h.top.findFocus());
2036             });
2037 
2038             final TestClusterHier h2 = new TestClusterHier(/* inTouchMode = */ false);
2039             // Attach top view group, so touch mode gets affected when set to false
2040             scenario.onActivity(a -> a.setContentView(h2.top));
2041             InstrumentationRegistry.getInstrumentation().setInTouchMode(false);
2042             PollingCheck.waitFor(TOUCH_MODE_PROPAGATION_TIMEOUT_MILLIS,
2043                     () -> !h2.top.isInTouchMode());
2044 
2045             scenario.onActivity(a -> {
2046                 h2.c1view1.requestFocus();
2047                 h2.nestedGroup.setKeyboardNavigationCluster(true);
2048                 h2.nestedGroup.setTouchscreenBlocksFocus(true);
2049                 // since cluster is nested, it should ignore its touchscreenBlocksFocus behavior.
2050                 h2.c2view2.requestFocus();
2051                 assertSame(h2.c2view2, h2.top.findFocus());
2052                 h2.top.addFocusables(views, View.FOCUS_DOWN);
2053                 assertTrue(views.contains(h2.c2view2));
2054                 views.clear();
2055             });
2056         }
2057     }
2058 
2059     @UiThreadTest
2060     @Test
testRequestTransparentRegion()2061     public void testRequestTransparentRegion() {
2062         MockViewGroup parent = new MockViewGroup(mContext);
2063         MockView child1 = new MockView(mContext);
2064         MockView child2 = new MockView(mContext);
2065         child1.addView(child2);
2066         parent.addView(child1);
2067         child1.requestTransparentRegion(child2);
2068         assertTrue(parent.isRequestTransparentRegionCalled);
2069     }
2070 
2071     @UiThreadTest
2072     @Test
testScheduleLayoutAnimation()2073     public void testScheduleLayoutAnimation() {
2074         Animation animation = new AlphaAnimation(mContext, null);
2075 
2076         LayoutAnimationController al = spy(new LayoutAnimationController(animation));
2077         mMockViewGroup.setLayoutAnimation(al);
2078         mMockViewGroup.scheduleLayoutAnimation();
2079         mMockViewGroup.dispatchDraw(new Canvas());
2080         verify(al, times(1)).start();
2081     }
2082 
2083     @UiThreadTest
2084     @Test
testSetAddStatesFromChildren()2085     public void testSetAddStatesFromChildren() {
2086         mMockViewGroup.setAddStatesFromChildren(true);
2087         assertTrue(mMockViewGroup.addStatesFromChildren());
2088 
2089         mMockViewGroup.setAddStatesFromChildren(false);
2090         assertFalse(mMockViewGroup.addStatesFromChildren());
2091     }
2092 
2093     @UiThreadTest
2094     @Test
testSetChildrenDrawingCacheEnabled()2095     public void testSetChildrenDrawingCacheEnabled() {
2096         assertTrue(mMockViewGroup.isAnimationCacheEnabled());
2097 
2098         mMockViewGroup.setAnimationCacheEnabled(false);
2099         assertFalse(mMockViewGroup.isAnimationCacheEnabled());
2100 
2101         mMockViewGroup.setAnimationCacheEnabled(true);
2102         assertTrue(mMockViewGroup.isAnimationCacheEnabled());
2103     }
2104 
2105     @UiThreadTest
2106     @Test
testSetChildrenDrawnWithCacheEnabled()2107     public void testSetChildrenDrawnWithCacheEnabled() {
2108         assertFalse(mMockViewGroup.isChildrenDrawnWithCacheEnabled());
2109 
2110         mMockViewGroup.setChildrenDrawnWithCacheEnabled(true);
2111         assertTrue(mMockViewGroup.isChildrenDrawnWithCacheEnabled());
2112 
2113         mMockViewGroup.setChildrenDrawnWithCacheEnabled(false);
2114         assertFalse(mMockViewGroup.isChildrenDrawnWithCacheEnabled());
2115     }
2116 
2117     @UiThreadTest
2118     @Test
testSetClipChildren()2119     public void testSetClipChildren() {
2120         Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
2121 
2122         mMockTextView.layout(1, 2, 30, 40);
2123         mMockViewGroup.layout(1, 1, 100, 200);
2124         mMockViewGroup.setClipChildren(true);
2125 
2126         MockCanvas canvas = new MockCanvas(bitmap);
2127         mMockViewGroup.drawChild(canvas, mMockTextView, 100);
2128         Rect rect = canvas.getClipBounds();
2129         assertEquals(0, rect.top);
2130         assertEquals(100, rect.bottom);
2131         assertEquals(0, rect.left);
2132         assertEquals(100, rect.right);
2133     }
2134 
2135     class MockCanvas extends Canvas {
2136 
2137         public int mLeft;
2138         public int mTop;
2139         public int mRight;
2140         public int mBottom;
2141 
MockCanvas()2142         public MockCanvas() {
2143             super(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888));
2144         }
2145 
MockCanvas(Bitmap bitmap)2146         public MockCanvas(Bitmap bitmap) {
2147             super(bitmap);
2148         }
2149 
2150         @Override
quickReject(float left, float top, float right, float bottom, EdgeType type)2151         public boolean quickReject(float left, float top, float right,
2152                 float bottom, EdgeType type) {
2153             super.quickReject(left, top, right, bottom, type);
2154             return false;
2155         }
2156 
2157         @Override
clipRect(int left, int top, int right, int bottom)2158         public boolean clipRect(int left, int top, int right, int bottom) {
2159             mLeft = left;
2160             mTop = top;
2161             mRight = right;
2162             mBottom = bottom;
2163             return super.clipRect(left, top, right, bottom);
2164         }
2165     }
2166 
2167     @UiThreadTest
2168     @Test
testSetClipToPadding()2169     public void testSetClipToPadding() {
2170         final int frameLeft = 1;
2171         final int frameTop = 2;
2172         final int frameRight = 100;
2173         final int frameBottom = 200;
2174         mMockViewGroup.layout(frameLeft, frameTop, frameRight, frameBottom);
2175 
2176         mMockViewGroup.setClipToPadding(true);
2177         MockCanvas canvas = new MockCanvas();
2178         final int paddingLeft = 10;
2179         final int paddingTop = 20;
2180         final int paddingRight = 100;
2181         final int paddingBottom = 200;
2182         mMockViewGroup.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
2183         mMockViewGroup.dispatchDraw(canvas);
2184         //check that the clip region does not contain the padding area
2185         assertEquals(10, canvas.mLeft);
2186         assertEquals(20, canvas.mTop);
2187         assertEquals(-frameLeft, canvas.mRight);
2188         assertEquals(-frameTop, canvas.mBottom);
2189 
2190         mMockViewGroup.setClipToPadding(false);
2191         canvas = new MockCanvas();
2192         mMockViewGroup.dispatchDraw(canvas);
2193         assertEquals(0, canvas.mLeft);
2194         assertEquals(0, canvas.mTop);
2195         assertEquals(0, canvas.mRight);
2196         assertEquals(0, canvas.mBottom);
2197     }
2198 
2199     @UiThreadTest
2200     @Test
testSetDescendantFocusability()2201     public void testSetDescendantFocusability() {
2202         final int FLAG_MASK_FOCUSABILITY = 0x60000;
2203         assertFalse((mMockViewGroup.getDescendantFocusability() & FLAG_MASK_FOCUSABILITY) == 0);
2204 
2205         mMockViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
2206         assertFalse((mMockViewGroup.getDescendantFocusability() & FLAG_MASK_FOCUSABILITY) == 0);
2207 
2208         mMockViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
2209         assertFalse((mMockViewGroup.getDescendantFocusability() & FLAG_MASK_FOCUSABILITY) == 0);
2210         assertFalse((mMockViewGroup.getDescendantFocusability() &
2211                 ViewGroup.FOCUS_BEFORE_DESCENDANTS) == 0);
2212     }
2213 
2214     @UiThreadTest
2215     @Test
testSetOnHierarchyChangeListener()2216     public void testSetOnHierarchyChangeListener() {
2217         MockViewGroup parent = new MockViewGroup(mContext);
2218         MockViewGroup child = new MockViewGroup(mContext);
2219         ViewGroup.OnHierarchyChangeListener listener =
2220                 mock(ViewGroup.OnHierarchyChangeListener.class);
2221         parent.setOnHierarchyChangeListener(listener);
2222         parent.addView(child);
2223 
2224         parent.removeDetachedView(child, false);
2225         InOrder inOrder = inOrder(listener);
2226         inOrder.verify(listener, times(1)).onChildViewAdded(parent, child);
2227         inOrder.verify(listener, times(1)).onChildViewRemoved(parent, child);
2228     }
2229 
2230     @UiThreadTest
2231     @Test
testSetPadding()2232     public void testSetPadding() {
2233         final int left = 1;
2234         final int top = 2;
2235         final int right = 3;
2236         final int bottom = 4;
2237 
2238         assertEquals(0, mMockViewGroup.getPaddingBottom());
2239         assertEquals(0, mMockViewGroup.getPaddingTop());
2240         assertEquals(0, mMockViewGroup.getPaddingLeft());
2241         assertEquals(0, mMockViewGroup.getPaddingRight());
2242         assertEquals(0, mMockViewGroup.getPaddingStart());
2243         assertEquals(0, mMockViewGroup.getPaddingEnd());
2244 
2245         mMockViewGroup.setPadding(left, top, right, bottom);
2246 
2247         assertEquals(bottom, mMockViewGroup.getPaddingBottom());
2248         assertEquals(top, mMockViewGroup.getPaddingTop());
2249         assertEquals(left, mMockViewGroup.getPaddingLeft());
2250         assertEquals(right, mMockViewGroup.getPaddingRight());
2251 
2252         assertEquals(left, mMockViewGroup.getPaddingStart());
2253         assertEquals(right, mMockViewGroup.getPaddingEnd());
2254         assertEquals(false, mMockViewGroup.isPaddingRelative());
2255 
2256         // force RTL direction
2257         mMockViewGroup.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
2258 
2259         assertEquals(bottom, mMockViewGroup.getPaddingBottom());
2260         assertEquals(top, mMockViewGroup.getPaddingTop());
2261         assertEquals(left, mMockViewGroup.getPaddingLeft());
2262         assertEquals(right, mMockViewGroup.getPaddingRight());
2263 
2264         assertEquals(right, mMockViewGroup.getPaddingStart());
2265         assertEquals(left, mMockViewGroup.getPaddingEnd());
2266         assertEquals(false, mMockViewGroup.isPaddingRelative());
2267     }
2268 
2269     @UiThreadTest
2270     @Test
testSetPaddingRelative()2271     public void testSetPaddingRelative() {
2272         final int start = 1;
2273         final int top = 2;
2274         final int end = 3;
2275         final int bottom = 4;
2276 
2277         assertEquals(0, mMockViewGroup.getPaddingBottom());
2278         assertEquals(0, mMockViewGroup.getPaddingTop());
2279         assertEquals(0, mMockViewGroup.getPaddingLeft());
2280         assertEquals(0, mMockViewGroup.getPaddingRight());
2281         assertEquals(0, mMockViewGroup.getPaddingStart());
2282         assertEquals(0, mMockViewGroup.getPaddingEnd());
2283 
2284         mMockViewGroup.setPaddingRelative(start, top, end, bottom);
2285 
2286         assertEquals(bottom, mMockViewGroup.getPaddingBottom());
2287         assertEquals(top, mMockViewGroup.getPaddingTop());
2288         assertEquals(start, mMockViewGroup.getPaddingLeft());
2289         assertEquals(end, mMockViewGroup.getPaddingRight());
2290 
2291         assertEquals(start, mMockViewGroup.getPaddingStart());
2292         assertEquals(end, mMockViewGroup.getPaddingEnd());
2293         assertEquals(true, mMockViewGroup.isPaddingRelative());
2294 
2295         // force RTL direction after setting relative padding
2296         mMockViewGroup.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
2297 
2298         assertEquals(bottom, mMockViewGroup.getPaddingBottom());
2299         assertEquals(top, mMockViewGroup.getPaddingTop());
2300         assertEquals(end, mMockViewGroup.getPaddingLeft());
2301         assertEquals(start, mMockViewGroup.getPaddingRight());
2302 
2303         assertEquals(start, mMockViewGroup.getPaddingStart());
2304         assertEquals(end, mMockViewGroup.getPaddingEnd());
2305         assertEquals(true, mMockViewGroup.isPaddingRelative());
2306 
2307         // force RTL direction before setting relative padding
2308         mMockViewGroup = new MockViewGroup(mContext);
2309         mMockViewGroup.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
2310 
2311         assertEquals(0, mMockViewGroup.getPaddingBottom());
2312         assertEquals(0, mMockViewGroup.getPaddingTop());
2313         assertEquals(0, mMockViewGroup.getPaddingLeft());
2314         assertEquals(0, mMockViewGroup.getPaddingRight());
2315         assertEquals(0, mMockViewGroup.getPaddingStart());
2316         assertEquals(0, mMockViewGroup.getPaddingEnd());
2317 
2318         mMockViewGroup.setPaddingRelative(start, top, end, bottom);
2319 
2320         assertEquals(bottom, mMockViewGroup.getPaddingBottom());
2321         assertEquals(top, mMockViewGroup.getPaddingTop());
2322         assertEquals(end, mMockViewGroup.getPaddingLeft());
2323         assertEquals(start, mMockViewGroup.getPaddingRight());
2324 
2325         assertEquals(start, mMockViewGroup.getPaddingStart());
2326         assertEquals(end, mMockViewGroup.getPaddingEnd());
2327         assertEquals(true, mMockViewGroup.isPaddingRelative());
2328     }
2329 
2330     @UiThreadTest
2331     @Test
testSetPersistentDrawingCache()2332     public void testSetPersistentDrawingCache() {
2333         mMockViewGroup.setPersistentDrawingCache(1);
2334         assertEquals(1 & ViewGroup.PERSISTENT_ALL_CACHES, mMockViewGroup
2335                 .getPersistentDrawingCache());
2336     }
2337 
2338     @UiThreadTest
2339     @Test
testShowContextMenuForChild()2340     public void testShowContextMenuForChild() {
2341         MockViewGroup parent = new MockViewGroup(mContext);
2342         MockViewGroup child = new MockViewGroup(mContext);
2343         parent.addView(child);
2344 
2345         child.showContextMenuForChild(null);
2346         assertTrue(parent.isShowContextMenuForChildCalled);
2347     }
2348 
2349     @UiThreadTest
2350     @Test
testShowContextMenuForChild_WithXYCoords()2351     public void testShowContextMenuForChild_WithXYCoords() {
2352         MockViewGroup parent = new MockViewGroup(mContext);
2353         MockViewGroup child = new MockViewGroup(mContext);
2354         parent.addView(child);
2355 
2356         child.showContextMenuForChild(null, 48, 48);
2357         assertTrue(parent.isShowContextMenuForChildCalledWithXYCoords);
2358     }
2359 
2360     @UiThreadTest
2361     @Test
testStartLayoutAnimation()2362     public void testStartLayoutAnimation() {
2363         RotateAnimation animation = new RotateAnimation(0.1f, 0.1f);
2364         LayoutAnimationController la = new LayoutAnimationController(animation);
2365         mMockViewGroup.setLayoutAnimation(la);
2366 
2367         mMockViewGroup.layout(1, 1, 100, 100);
2368         assertFalse(mMockViewGroup.isLayoutRequested());
2369         mMockViewGroup.startLayoutAnimation();
2370         assertTrue(mMockViewGroup.isLayoutRequested());
2371     }
2372 
2373     @UiThreadTest
2374     @Test
testUpdateViewLayout()2375     public void testUpdateViewLayout() {
2376         MockViewGroup parent = new MockViewGroup(mContext);
2377         MockViewGroup child = new MockViewGroup(mContext);
2378 
2379         parent.addView(child);
2380         LayoutParams param = new LayoutParams(100, 200);
2381         parent.updateViewLayout(child, param);
2382         assertEquals(param.width, child.getLayoutParams().width);
2383         assertEquals(param.height, child.getLayoutParams().height);
2384     }
2385 
2386     @UiThreadTest
2387     @Test
testDebug()2388     public void testDebug() {
2389         final int EXPECTED = 100;
2390         MockViewGroup parent = new MockViewGroup(mContext);
2391         MockViewGroup child = new MockViewGroup(mContext);
2392         parent.addView(child);
2393 
2394         parent.debug(EXPECTED);
2395         assertEquals(EXPECTED + 1, child.debugDepth);
2396     }
2397 
2398     @UiThreadTest
2399     @Test
testDispatchKeyEventPreIme()2400     public void testDispatchKeyEventPreIme() {
2401         KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER);
2402         assertFalse(mMockViewGroup.dispatchKeyEventPreIme(event));
2403         assertFalse(mMockViewGroup.dispatchKeyShortcutEvent(event));
2404 
2405         mMockViewGroup.addView(mMockTextView);
2406         mMockViewGroup.requestChildFocus(mMockTextView, null);
2407         mMockViewGroup.layout(0, 0, 100, 200);
2408         assertFalse(mMockViewGroup.dispatchKeyEventPreIme(event));
2409         assertFalse(mMockViewGroup.dispatchKeyShortcutEvent(event));
2410 
2411         mMockViewGroup.requestChildFocus(mMockTextView, null);
2412         mMockTextView.layout(0, 0, 50, 50);
2413         assertTrue(mMockViewGroup.dispatchKeyEventPreIme(event));
2414         assertTrue(mMockViewGroup.dispatchKeyShortcutEvent(event));
2415 
2416         mMockViewGroup.setStaticTransformationsEnabled(true);
2417         Canvas canvas = new Canvas();
2418         mMockViewGroup.drawChild(canvas, mMockTextView, 100);
2419         assertTrue(mMockViewGroup.isGetChildStaticTransformationCalled);
2420         mMockViewGroup.isGetChildStaticTransformationCalled = false;
2421         mMockViewGroup.setStaticTransformationsEnabled(false);
2422         mMockViewGroup.drawChild(canvas, mMockTextView, 100);
2423         assertFalse(mMockViewGroup.isGetChildStaticTransformationCalled);
2424     }
2425 
2426     @UiThreadTest
2427     @Test
testStartActionModeForChildRespectsSubclassModeOnPrimary()2428     public void testStartActionModeForChildRespectsSubclassModeOnPrimary() {
2429         MockViewGroupSubclass vgParent = new MockViewGroupSubclass(mContext);
2430         MockViewGroupSubclass vg = new MockViewGroupSubclass(mContext);
2431         vg.shouldReturnOwnTypelessActionMode = true;
2432         vgParent.addView(vg);
2433         vg.addView(mMockTextView);
2434 
2435         mMockTextView.startActionMode(NO_OP_ACTION_MODE_CALLBACK, ActionMode.TYPE_PRIMARY);
2436 
2437         assertTrue(vg.isStartActionModeForChildTypedCalled);
2438         assertTrue(vg.isStartActionModeForChildTypelessCalled);
2439         // Call should not bubble up as we have an intercepting implementation.
2440         assertFalse(vgParent.isStartActionModeForChildTypedCalled);
2441     }
2442 
2443     @UiThreadTest
2444     @Test
testStartActionModeForChildIgnoresSubclassModeOnFloating()2445     public void testStartActionModeForChildIgnoresSubclassModeOnFloating() {
2446         MockViewGroupSubclass vgParent = new MockViewGroupSubclass(mContext);
2447         MockViewGroupSubclass vg = new MockViewGroupSubclass(mContext);
2448         vg.shouldReturnOwnTypelessActionMode = true;
2449         vgParent.addView(vg);
2450         vg.addView(mMockTextView);
2451 
2452         mMockTextView.startActionMode(NO_OP_ACTION_MODE_CALLBACK, ActionMode.TYPE_FLOATING);
2453 
2454         assertTrue(vg.isStartActionModeForChildTypedCalled);
2455         assertFalse(vg.isStartActionModeForChildTypelessCalled);
2456         // Call should bubble up as we have a floating type.
2457         assertTrue(vgParent.isStartActionModeForChildTypedCalled);
2458     }
2459 
2460     @UiThreadTest
2461     @Test
testStartActionModeForChildTypedBubblesUpToParent()2462     public void testStartActionModeForChildTypedBubblesUpToParent() {
2463         MockViewGroupSubclass vgParent = new MockViewGroupSubclass(mContext);
2464         MockViewGroupSubclass vg = new MockViewGroupSubclass(mContext);
2465         vgParent.addView(vg);
2466         vg.addView(mMockTextView);
2467 
2468         mMockTextView.startActionMode(NO_OP_ACTION_MODE_CALLBACK, ActionMode.TYPE_FLOATING);
2469 
2470         assertTrue(vg.isStartActionModeForChildTypedCalled);
2471         assertTrue(vgParent.isStartActionModeForChildTypedCalled);
2472     }
2473 
2474     @UiThreadTest
2475     @Test
testStartActionModeForChildTypelessBubblesUpToParent()2476     public void testStartActionModeForChildTypelessBubblesUpToParent() {
2477         MockViewGroupSubclass vgParent = new MockViewGroupSubclass(mContext);
2478         MockViewGroupSubclass vg = new MockViewGroupSubclass(mContext);
2479         vgParent.addView(vg);
2480         vg.addView(mMockTextView);
2481 
2482         mMockTextView.startActionMode(NO_OP_ACTION_MODE_CALLBACK);
2483 
2484         assertTrue(vg.isStartActionModeForChildTypedCalled);
2485         assertTrue(vg.isStartActionModeForChildTypelessCalled);
2486         assertTrue(vgParent.isStartActionModeForChildTypedCalled);
2487     }
2488 
2489     @UiThreadTest
2490     @Test
testTemporaryDetach()2491     public void testTemporaryDetach() {
2492         // [vgParent]
2493         //   - [viewParent1]
2494         //   - [viewParent1]
2495         //   - [mMockViewGroup]
2496         //     - [view1]
2497         //     - [view2]
2498         MockViewGroupSubclass vgParent = new MockViewGroupSubclass(mContext);
2499         TemporaryDetachingMockView viewParent1 = new TemporaryDetachingMockView(mContext);
2500         TemporaryDetachingMockView viewParent2 = new TemporaryDetachingMockView(mContext);
2501         vgParent.addView(viewParent1);
2502         vgParent.addView(viewParent2);
2503         MockViewGroupSubclass vg = new MockViewGroupSubclass(mContext);
2504         vgParent.addView(vg);
2505         TemporaryDetachingMockView view1 = new TemporaryDetachingMockView(mContext);
2506         TemporaryDetachingMockView view2 = new TemporaryDetachingMockView(mContext);
2507         vg.addView(view1);
2508         vg.addView(view2);
2509 
2510         // Make sure that no View is temporarity detached in the initial state.
2511         assertFalse(viewParent1.isTemporarilyDetached());
2512         assertEquals(0, viewParent1.getDispatchStartTemporaryDetachCount());
2513         assertEquals(0, viewParent1.getDispatchFinishTemporaryDetachCount());
2514         assertEquals(0, viewParent1.getOnStartTemporaryDetachCount());
2515         assertEquals(0, viewParent1.getOnFinishTemporaryDetachCount());
2516         assertFalse(viewParent2.isTemporarilyDetached());
2517         assertEquals(0, viewParent2.getDispatchStartTemporaryDetachCount());
2518         assertEquals(0, viewParent2.getDispatchFinishTemporaryDetachCount());
2519         assertEquals(0, viewParent2.getOnStartTemporaryDetachCount());
2520         assertEquals(0, viewParent2.getOnFinishTemporaryDetachCount());
2521         assertFalse(view1.isTemporarilyDetached());
2522         assertEquals(0, view1.getDispatchStartTemporaryDetachCount());
2523         assertEquals(0, view1.getDispatchFinishTemporaryDetachCount());
2524         assertEquals(0, view1.getOnStartTemporaryDetachCount());
2525         assertEquals(0, view1.getOnFinishTemporaryDetachCount());
2526         assertFalse(view2.isTemporarilyDetached());
2527         assertEquals(0, view2.getDispatchStartTemporaryDetachCount());
2528         assertEquals(0, view2.getDispatchFinishTemporaryDetachCount());
2529         assertEquals(0, view2.getOnStartTemporaryDetachCount());
2530         assertEquals(0, view2.getOnFinishTemporaryDetachCount());
2531 
2532         // [vgParent]
2533         //   - [viewParent1]
2534         //   - [viewParent1]
2535         //   - [mMockViewGroup]           <- dispatchStartTemporaryDetach()
2536         //     - [view1]
2537         //     - [view2]
2538         vg.dispatchStartTemporaryDetach();
2539 
2540         assertFalse(viewParent1.isTemporarilyDetached());
2541         assertEquals(0, viewParent1.getDispatchStartTemporaryDetachCount());
2542         assertEquals(0, viewParent1.getDispatchFinishTemporaryDetachCount());
2543         assertEquals(0, viewParent1.getOnStartTemporaryDetachCount());
2544         assertEquals(0, viewParent1.getOnFinishTemporaryDetachCount());
2545         assertFalse(viewParent2.isTemporarilyDetached());
2546         assertEquals(0, viewParent2.getDispatchStartTemporaryDetachCount());
2547         assertEquals(0, viewParent2.getDispatchFinishTemporaryDetachCount());
2548         assertEquals(0, viewParent2.getOnStartTemporaryDetachCount());
2549         assertEquals(0, viewParent2.getOnFinishTemporaryDetachCount());
2550         assertTrue(view1.isTemporarilyDetached());
2551         assertEquals(1, view1.getDispatchStartTemporaryDetachCount());
2552         assertEquals(0, view1.getDispatchFinishTemporaryDetachCount());
2553         assertEquals(1, view1.getOnStartTemporaryDetachCount());
2554         assertEquals(0, view1.getOnFinishTemporaryDetachCount());
2555         assertTrue(view2.isTemporarilyDetached());
2556         assertEquals(1, view2.getDispatchStartTemporaryDetachCount());
2557         assertEquals(0, view2.getDispatchFinishTemporaryDetachCount());
2558         assertEquals(1, view2.getOnStartTemporaryDetachCount());
2559         assertEquals(0, view2.getOnFinishTemporaryDetachCount());
2560 
2561         // [vgParent]
2562         //   - [viewParent1]
2563         //   - [viewParent1]
2564         //   - [mMockViewGroup]           <- dispatchFinishTemporaryDetach()
2565         //     - [view1]
2566         //     - [view2]
2567         vg.dispatchFinishTemporaryDetach();
2568 
2569         assertFalse(viewParent1.isTemporarilyDetached());
2570         assertEquals(0, viewParent1.getDispatchStartTemporaryDetachCount());
2571         assertEquals(0, viewParent1.getDispatchFinishTemporaryDetachCount());
2572         assertEquals(0, viewParent1.getOnStartTemporaryDetachCount());
2573         assertEquals(0, viewParent1.getOnFinishTemporaryDetachCount());
2574         assertFalse(viewParent2.isTemporarilyDetached());
2575         assertEquals(0, viewParent2.getDispatchStartTemporaryDetachCount());
2576         assertEquals(0, viewParent2.getDispatchFinishTemporaryDetachCount());
2577         assertEquals(0, viewParent2.getOnStartTemporaryDetachCount());
2578         assertEquals(0, viewParent2.getOnFinishTemporaryDetachCount());
2579         assertFalse(view1.isTemporarilyDetached());
2580         assertEquals(1, view1.getDispatchStartTemporaryDetachCount());
2581         assertEquals(1, view1.getDispatchFinishTemporaryDetachCount());
2582         assertEquals(1, view1.getOnStartTemporaryDetachCount());
2583         assertEquals(1, view1.getOnFinishTemporaryDetachCount());
2584         assertFalse(view2.isTemporarilyDetached());
2585         assertEquals(1, view2.getDispatchStartTemporaryDetachCount());
2586         assertEquals(1, view2.getDispatchFinishTemporaryDetachCount());
2587         assertEquals(1, view2.getOnStartTemporaryDetachCount());
2588         assertEquals(1, view2.getOnFinishTemporaryDetachCount());
2589 
2590         // [vgParent]         <- dispatchStartTemporaryDetach()
2591         //   - [viewParent1]
2592         //   - [viewParent1]
2593         //   - [mMockViewGroup]
2594         //     - [view1]
2595         //     - [view2]
2596         vgParent.dispatchStartTemporaryDetach();
2597 
2598         assertTrue(viewParent1.isTemporarilyDetached());
2599         assertEquals(1, viewParent1.getDispatchStartTemporaryDetachCount());
2600         assertEquals(0, viewParent1.getDispatchFinishTemporaryDetachCount());
2601         assertEquals(1, viewParent1.getOnStartTemporaryDetachCount());
2602         assertEquals(0, viewParent1.getOnFinishTemporaryDetachCount());
2603         assertTrue(viewParent2.isTemporarilyDetached());
2604         assertEquals(1, viewParent2.getDispatchStartTemporaryDetachCount());
2605         assertEquals(0, viewParent2.getDispatchFinishTemporaryDetachCount());
2606         assertEquals(1, viewParent2.getOnStartTemporaryDetachCount());
2607         assertEquals(0, viewParent2.getOnFinishTemporaryDetachCount());
2608         assertTrue(view1.isTemporarilyDetached());
2609         assertEquals(2, view1.getDispatchStartTemporaryDetachCount());
2610         assertEquals(1, view1.getDispatchFinishTemporaryDetachCount());
2611         assertEquals(2, view1.getOnStartTemporaryDetachCount());
2612         assertEquals(1, view1.getOnFinishTemporaryDetachCount());
2613         assertTrue(view2.isTemporarilyDetached());
2614         assertEquals(2, view2.getDispatchStartTemporaryDetachCount());
2615         assertEquals(1, view2.getDispatchFinishTemporaryDetachCount());
2616         assertEquals(2, view2.getOnStartTemporaryDetachCount());
2617         assertEquals(1, view2.getOnFinishTemporaryDetachCount());
2618 
2619         // [vgParent]         <- dispatchFinishTemporaryDetach()
2620         //   - [viewParent1]
2621         //   - [viewParent1]
2622         //   - [mMockViewGroup]
2623         //     - [view1]
2624         //     - [view2]
2625         vgParent.dispatchFinishTemporaryDetach();
2626 
2627         assertFalse(viewParent1.isTemporarilyDetached());
2628         assertEquals(1, viewParent1.getDispatchStartTemporaryDetachCount());
2629         assertEquals(1, viewParent1.getDispatchFinishTemporaryDetachCount());
2630         assertEquals(1, viewParent1.getOnStartTemporaryDetachCount());
2631         assertEquals(1, viewParent1.getOnFinishTemporaryDetachCount());
2632         assertFalse(viewParent2.isTemporarilyDetached());
2633         assertEquals(1, viewParent2.getDispatchStartTemporaryDetachCount());
2634         assertEquals(1, viewParent2.getDispatchFinishTemporaryDetachCount());
2635         assertEquals(1, viewParent2.getOnStartTemporaryDetachCount());
2636         assertEquals(1, viewParent2.getOnFinishTemporaryDetachCount());
2637         assertFalse(view1.isTemporarilyDetached());
2638         assertEquals(2, view1.getDispatchStartTemporaryDetachCount());
2639         assertEquals(2, view1.getDispatchFinishTemporaryDetachCount());
2640         assertEquals(2, view1.getOnStartTemporaryDetachCount());
2641         assertEquals(2, view1.getOnFinishTemporaryDetachCount());
2642         assertFalse(view2.isTemporarilyDetached());
2643         assertEquals(2, view2.getDispatchStartTemporaryDetachCount());
2644         assertEquals(2, view2.getDispatchFinishTemporaryDetachCount());
2645         assertEquals(2, view2.getOnStartTemporaryDetachCount());
2646         assertEquals(2, view2.getOnFinishTemporaryDetachCount());
2647     }
2648 
2649     private static final ActionMode.Callback NO_OP_ACTION_MODE_CALLBACK =
2650             new ActionMode.Callback() {
2651                 @Override
2652                 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2653                     return false;
2654                 }
2655 
2656                 @Override
2657                 public void onDestroyActionMode(ActionMode mode) {}
2658 
2659                 @Override
2660                 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2661                     return false;
2662                 }
2663 
2664                 @Override
2665                 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2666                     return false;
2667                 }
2668             };
2669 
2670     private static final ActionMode NO_OP_ACTION_MODE =
2671             new ActionMode() {
2672                 @Override
2673                 public void setTitle(CharSequence title) {}
2674 
2675                 @Override
2676                 public void setTitle(int resId) {}
2677 
2678                 @Override
2679                 public void setSubtitle(CharSequence subtitle) {}
2680 
2681                 @Override
2682                 public void setSubtitle(int resId) {}
2683 
2684                 @Override
2685                 public void setCustomView(View view) {}
2686 
2687                 @Override
2688                 public void invalidate() {}
2689 
2690                 @Override
2691                 public void finish() {}
2692 
2693                 @Override
2694                 public Menu getMenu() {
2695                     return null;
2696                 }
2697 
2698                 @Override
2699                 public CharSequence getTitle() {
2700                     return null;
2701                 }
2702 
2703                 @Override
2704                 public CharSequence getSubtitle() {
2705                     return null;
2706                 }
2707 
2708                 @Override
2709                 public View getCustomView() {
2710                     return null;
2711                 }
2712 
2713                 @Override
2714                 public MenuInflater getMenuInflater() {
2715                     return null;
2716                 }
2717             };
2718 
2719     private static class MockViewGroupSubclass extends ViewGroup {
2720         boolean isStartActionModeForChildTypedCalled = false;
2721         boolean isStartActionModeForChildTypelessCalled = false;
2722         boolean shouldReturnOwnTypelessActionMode = false;
2723 
MockViewGroupSubclass(Context context)2724         public MockViewGroupSubclass(Context context) {
2725             super(context);
2726         }
2727 
2728         @Override
startActionModeForChild(View originalView, ActionMode.Callback callback)2729         public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
2730             isStartActionModeForChildTypelessCalled = true;
2731             if (shouldReturnOwnTypelessActionMode) {
2732                 return NO_OP_ACTION_MODE;
2733             }
2734             return super.startActionModeForChild(originalView, callback);
2735         }
2736 
2737         @Override
startActionModeForChild( View originalView, ActionMode.Callback callback, int type)2738         public ActionMode startActionModeForChild(
2739                 View originalView, ActionMode.Callback callback, int type) {
2740             isStartActionModeForChildTypedCalled = true;
2741             return super.startActionModeForChild(originalView, callback, type);
2742         }
2743 
2744         @Override
onLayout(boolean changed, int l, int t, int r, int b)2745         protected void onLayout(boolean changed, int l, int t, int r, int b) {
2746             // no-op
2747         }
2748     }
2749 
2750     static public int resetRtlPropertiesCount;
2751     static public int resetResolvedLayoutDirectionCount;
2752     static public int resetResolvedTextDirectionCount;
2753     static public int resetResolvedTextAlignmentCount;
2754     static public int resetResolvedPaddingCount;
2755     static public int resetResolvedDrawablesCount;
2756 
2757 
clearRtlCounters()2758     private static void clearRtlCounters() {
2759         resetRtlPropertiesCount = 0;
2760         resetResolvedLayoutDirectionCount = 0;
2761         resetResolvedTextDirectionCount = 0;
2762         resetResolvedTextAlignmentCount = 0;
2763         resetResolvedPaddingCount = 0;
2764         resetResolvedDrawablesCount = 0;
2765     }
2766 
2767     @UiThreadTest
2768     @Test
testResetRtlProperties()2769     public void testResetRtlProperties() {
2770         clearRtlCounters();
2771 
2772         MockView2 v1 = new MockView2(mContext);
2773         MockView2 v2 = new MockView2(mContext);
2774 
2775         MockViewGroup v3 = new MockViewGroup(mContext);
2776         MockView2 v4 = new MockView2(mContext);
2777 
2778         v3.addView(v4);
2779         assertEquals(1, resetRtlPropertiesCount);
2780         assertEquals(1, resetResolvedLayoutDirectionCount);
2781         assertEquals(1, resetResolvedTextDirectionCount);
2782         assertEquals(1, resetResolvedTextAlignmentCount);
2783         assertEquals(1, resetResolvedPaddingCount);
2784         assertEquals(1, resetResolvedDrawablesCount);
2785 
2786         clearRtlCounters();
2787         mMockViewGroup.addView(v1);
2788         mMockViewGroup.addView(v2);
2789         mMockViewGroup.addView(v3);
2790 
2791         assertEquals(3, resetRtlPropertiesCount); // for v1 / v2 / v3 only
2792         assertEquals(4, resetResolvedLayoutDirectionCount); // for v1 / v2 / v3 / v4
2793         assertEquals(4, resetResolvedTextDirectionCount);
2794         assertEquals(3, resetResolvedTextAlignmentCount); // for v1 / v2 / v3 only
2795         assertEquals(4, resetResolvedPaddingCount);
2796         assertEquals(4, resetResolvedDrawablesCount);
2797 
2798         clearRtlCounters();
2799         mMockViewGroup.resetRtlProperties();
2800         assertEquals(1, resetRtlPropertiesCount); // for mMockViewGroup only
2801         assertEquals(5, resetResolvedLayoutDirectionCount); // for all
2802         assertEquals(5, resetResolvedTextDirectionCount);
2803         // for mMockViewGroup only as TextAlignment is not inherited (default is Gravity)
2804         assertEquals(1, resetResolvedTextAlignmentCount);
2805         assertEquals(5, resetResolvedPaddingCount);
2806         assertEquals(5, resetResolvedDrawablesCount);
2807     }
2808 
2809     @UiThreadTest
2810     @Test
testLayoutNotCalledWithSuppressLayoutTrue()2811     public void testLayoutNotCalledWithSuppressLayoutTrue() {
2812         mMockViewGroup.isRequestLayoutCalled = false;
2813         mMockViewGroup.suppressLayout(true);
2814         mMockViewGroup.layout(0, 0, 100, 100);
2815 
2816         assertTrue(mMockViewGroup.isLayoutSuppressed());
2817         assertFalse(mMockViewGroup.isOnLayoutCalled);
2818         assertFalse(mMockViewGroup.isRequestLayoutCalled);
2819     }
2820 
2821     @UiThreadTest
2822     @Test
testLayoutCalledAfterSettingBackSuppressLayoutToFalseTrue()2823     public void testLayoutCalledAfterSettingBackSuppressLayoutToFalseTrue() {
2824         mMockViewGroup.suppressLayout(true);
2825         mMockViewGroup.suppressLayout(false);
2826         mMockViewGroup.layout(0, 0, 100, 100);
2827 
2828         assertFalse(mMockViewGroup.isLayoutSuppressed());
2829         assertTrue(mMockViewGroup.isOnLayoutCalled);
2830     }
2831 
2832     @UiThreadTest
2833     @Test
testRequestLayoutCalledAfterSettingSuppressToFalseWhenItWasCalledWithTrue()2834     public void testRequestLayoutCalledAfterSettingSuppressToFalseWhenItWasCalledWithTrue() {
2835         mMockViewGroup.isRequestLayoutCalled = false;
2836         mMockViewGroup.suppressLayout(true);
2837         // now we call layout while in suppressed state
2838         mMockViewGroup.layout(0, 0, 100, 100);
2839         // then we undo suppressing. it should call requestLayout as we swallowed one layout call
2840         mMockViewGroup.suppressLayout(false);
2841 
2842         assertTrue(mMockViewGroup.isRequestLayoutCalled);
2843     }
2844 
2845     @UiThreadTest
2846     @Ignore("Turn on once ViewRootImpl.USE_NEW_INSETS is switched to true")
2847     @Test
testDispatchInsets_affectsChildren()2848     public void testDispatchInsets_affectsChildren() {
2849         View v1 = new View(mContext);
2850         mMockViewGroup.addView(v1);
2851 
2852         mMockViewGroup.setOnApplyWindowInsetsListener((v, insets) -> insets.inset(0, 0, 0, 10));
2853 
2854         OnApplyWindowInsetsListener listenerMock = mock(OnApplyWindowInsetsListener.class);
2855         v1.setOnApplyWindowInsetsListener(listenerMock);
2856 
2857         WindowInsets insets = new WindowInsets.Builder().setSystemWindowInsets(
2858                 Insets.of(10, 10, 10, 10)).build();
2859         mMockViewGroup.dispatchApplyWindowInsets(insets);
2860         verify(listenerMock).onApplyWindowInsets(any(),
2861                 eq(new WindowInsets.Builder()
2862                         .setSystemWindowInsets(Insets.of(10, 10, 10, 0)).build()));
2863     }
2864 
2865     @UiThreadTest
2866     @Ignore("Turn on once ViewRootImpl.USE_NEW_INSETS is switched to true")
2867     @Test
testDispatchInsets_doesntAffectSiblings()2868     public void testDispatchInsets_doesntAffectSiblings() {
2869         View v1 = new View(mContext);
2870         View v2 = new View(mContext);
2871         mMockViewGroup.addView(v1);
2872         mMockViewGroup.addView(v2);
2873 
2874         v1.setOnApplyWindowInsetsListener((v, insets) -> insets.inset(0, 0, 0, 10));
2875 
2876         OnApplyWindowInsetsListener listenerMock = mock(OnApplyWindowInsetsListener.class);
2877         v2.setOnApplyWindowInsetsListener(listenerMock);
2878 
2879         WindowInsets insets = new WindowInsets.Builder().setSystemWindowInsets(
2880                 Insets.of(10, 10, 10, 10)).build();
2881         mMockViewGroup.dispatchApplyWindowInsets(insets);
2882         verify(listenerMock).onApplyWindowInsets(any(),
2883                 eq(new WindowInsets.Builder()
2884                         .setSystemWindowInsets(Insets.of(10, 10, 10, 10)).build()));
2885     }
2886 
2887     @UiThreadTest
2888     @Ignore("Turn on once ViewRootImpl.USE_NEW_INSETS is switched to true")
2889     @Test
testDispatchInsets_doesntAffectParentSiblings()2890     public void testDispatchInsets_doesntAffectParentSiblings() {
2891         ViewGroup v1 = new MockViewGroup(mContext);
2892         View v11 = new View(mContext);
2893         View v2 = new View(mContext);
2894         mMockViewGroup.addView(v1);
2895         v1.addView(v11);
2896         mMockViewGroup.addView(v2);
2897 
2898         v11.setOnApplyWindowInsetsListener((v, insets) -> insets.inset(0, 0, 0, 10));
2899 
2900         OnApplyWindowInsetsListener listenerMock = mock(OnApplyWindowInsetsListener.class);
2901         v2.setOnApplyWindowInsetsListener(listenerMock);
2902 
2903         WindowInsets insets = new WindowInsets.Builder().setSystemWindowInsets(
2904                 Insets.of(10, 10, 10, 10)).build();
2905         mMockViewGroup.dispatchApplyWindowInsets(insets);
2906         verify(listenerMock).onApplyWindowInsets(any(),
2907                 eq(new WindowInsets.Builder()
2908                         .setSystemWindowInsets(Insets.of(10, 10, 10, 10)).build()));
2909     }
2910 
2911     @UiThreadTest
2912     @Test
testDispatchInsets_consumeStopsDispatch()2913     public void testDispatchInsets_consumeStopsDispatch() {
2914         View v1 = new View(mContext);
2915         mMockViewGroup.addView(v1);
2916 
2917         mMockViewGroup.setOnApplyWindowInsetsListener(
2918                 (v, insets) -> insets.consumeSystemWindowInsets());
2919 
2920         OnApplyWindowInsetsListener listenerMock = mock(OnApplyWindowInsetsListener.class);
2921         v1.setOnApplyWindowInsetsListener(listenerMock);
2922 
2923         WindowInsets insets = new WindowInsets.Builder().setSystemWindowInsets(
2924                 Insets.of(10, 10, 10, 10)).build();
2925         mMockViewGroup.dispatchApplyWindowInsets(insets);
2926         verify(listenerMock, never()).onApplyWindowInsets(any(),
2927                 eq(new WindowInsets.Builder().build()));
2928     }
2929 
2930     @UiThreadTest
2931     @Test
testFindViewById_shouldBeDFS()2932     public void testFindViewById_shouldBeDFS() {
2933         View v1 = new View(mContext);
2934         View v2 = new View(mContext);
2935         View w1 = new View(mContext);
2936         View w2 = new View(mContext);
2937         v1.setId(2);
2938         v2.setId(2);
2939         w1.setId(3);
2940         w2.setId(3);
2941         ViewGroup vg1 = new MockViewGroup(mContext);
2942         mMockViewGroup.addView(vg1);
2943         vg1.addView(v1);
2944         mMockViewGroup.addView(v2);
2945         vg1.addView(w1);
2946         vg1.addView(w2);
2947 
2948         assertSame(v1, mMockViewGroup.findViewById(2));
2949         assertSame(w1, mMockViewGroup.findViewById(3));
2950     }
2951 
2952     @UiThreadTest
2953     @Test
2954     @RequiresFlagsEnabled(FLAG_TOOLKIT_VIEWGROUP_SET_REQUESTED_FRAME_RATE_API)
testRequestedFrameRateApi()2955     public void testRequestedFrameRateApi() {
2956         ViewGroup vg = new MockViewGroup(mContext);
2957         vg.setRequestedFrameRate(30f);
2958         assertEquals(30f, vg.getRequestedFrameRate(), 0);
2959 
2960         vg.setRequestedFrameRate(60f);
2961         assertEquals(60f, vg.getRequestedFrameRate(), 0);
2962 
2963         vg.setRequestedFrameRate(120f);
2964         assertEquals(120f, vg.getRequestedFrameRate(), 0);
2965 
2966         vg.setRequestedFrameRate(-1f);
2967         assertEquals(-1f, vg.getRequestedFrameRate(), 0);
2968     }
2969 
2970     @UiThreadTest
2971     @Test
2972     @RequiresFlagsEnabled(FLAG_TOOLKIT_VIEWGROUP_SET_REQUESTED_FRAME_RATE_API)
testPropagateRequestedFrameRate_shouldNotOverride()2973     public void testPropagateRequestedFrameRate_shouldNotOverride() {
2974         /*
2975          * UI hierarchy
2976          *  - a1
2977          *  - vg1
2978          *      - b1
2979          *      - b2
2980          *      - b3
2981          *      - vg2
2982          *          - c1
2983          *          - c2
2984          *          - c3
2985          *          - vg4
2986          *              - e1
2987          *              - e2
2988          *      - vg3
2989          *          - d1
2990          *          - d2
2991          *          - vg5
2992          *              -f1
2993          *              -f2
2994          */
2995 
2996         View a1 = new View(mContext);
2997         mMockViewGroup.addView(a1);
2998 
2999         ViewGroup vg1 = new MockViewGroup(mContext);
3000         View b1 = new View(mContext);
3001         View b2 = new View(mContext);
3002         View b3 = new View(mContext);
3003         b1.setRequestedFrameRate(30f);
3004         vg1.addView(b1);
3005         vg1.addView(b2);
3006         vg1.addView(b3);
3007         mMockViewGroup.addView(vg1);
3008 
3009         ViewGroup vg2 = new MockViewGroup(mContext);
3010         View c1 = new View(mContext);
3011         View c2 = new View(mContext);
3012         View c3 = new View(mContext);
3013         vg2.addView(c1);
3014         vg2.addView(c2);
3015         vg2.propagateRequestedFrameRate(120f, false);
3016         vg2.addView(c3);
3017 
3018         vg1.addView(vg2);
3019         vg1.propagateRequestedFrameRate(60f, false);
3020 
3021         ViewGroup vg3 = new MockViewGroup(mContext);
3022         vg3.setRequestedFrameRate(80f);
3023         View d1 = new View(mContext);
3024         View d2 = new View(mContext);
3025         vg3.addView(d1);
3026         vg3.addView(d2);
3027 
3028         vg1.addView(vg3);
3029 
3030         ViewGroup vg4 = new MockViewGroup(mContext);
3031         View e1 = new View(mContext);
3032         View e2 = new View(mContext);
3033 
3034         vg4.addView(e1);
3035         vg2.addView(vg4);
3036         vg4.addView(e2);
3037 
3038         ViewGroup vg5 = new MockViewGroup(mContext);
3039         View f1 = new View(mContext);
3040         View f2 = new View(mContext);
3041         vg5.addView(f1);
3042         vg5.addView(f2);
3043 
3044         vg3.addView(vg5);
3045         vg5.propagateRequestedFrameRate(Float.NaN, false);
3046 
3047         assertTrue(Float.isNaN(a1.getRequestedFrameRate())); // should be default value
3048 
3049         assertEquals(60f, vg1.getRequestedFrameRate(), 0); // PropagateRequestedFrameRate 60
3050         assertEquals(30f, b1.getRequestedFrameRate(), 0); // explicitly request 30
3051         assertEquals(60f, b2.getRequestedFrameRate(), 0); // should inherit from vg1
3052         assertEquals(60f, b3.getRequestedFrameRate(), 0);
3053 
3054         assertEquals(120f, vg2.getRequestedFrameRate(), 0); // PropagateRequestedFrameRate 120
3055         assertEquals(120f, c1.getRequestedFrameRate(), 0); // should inherit from vg2
3056         assertEquals(120f, c2.getRequestedFrameRate(), 0);
3057         assertEquals(120f, c3.getRequestedFrameRate(), 0);
3058 
3059         assertEquals(80f, vg3.getRequestedFrameRate(), 0); // explicitly request 80
3060         assertTrue(Float.isNaN(d1.getRequestedFrameRate())); // should not inherit from vg1 or vg3
3061         assertTrue(Float.isNaN(d2.getRequestedFrameRate()));
3062 
3063         assertEquals(120f, vg4.getRequestedFrameRate(), 0); // should inherit from vg2
3064         assertEquals(120f, e1.getRequestedFrameRate(), 0); // should inherit from vg4
3065         assertEquals(120f, e2.getRequestedFrameRate(), 0);
3066 
3067         assertTrue(Float.isNaN(vg5.getRequestedFrameRate())); // frame rate should be reset
3068         assertTrue(Float.isNaN(f1.getRequestedFrameRate()));
3069         assertTrue(Float.isNaN(f2.getRequestedFrameRate()));
3070     }
3071 
3072     @UiThreadTest
3073     @Test
3074     @RequiresFlagsEnabled(FLAG_TOOLKIT_VIEWGROUP_SET_REQUESTED_FRAME_RATE_API)
testPropagateRequestedFrameRate_shouldOverride()3075     public void testPropagateRequestedFrameRate_shouldOverride() {
3076         /*
3077          * UI hierarchy
3078          *  - a1
3079          *  - vg1
3080          *      - b1
3081          *      - b2
3082          *      - b3
3083          *      - vg2
3084          *          - c1
3085          *          - c2
3086          *          - c3
3087          *          - vg4
3088          *              - e1
3089          *              - e2
3090          *      - vg3
3091          *          - d1
3092          *          - d2
3093          *          - vg5
3094          *              -f1
3095          *              -f2
3096          */
3097 
3098         View a1 = new View(mContext);
3099         mMockViewGroup.addView(a1);
3100 
3101         ViewGroup vg1 = new MockViewGroup(mContext);
3102         View b1 = new View(mContext);
3103         View b2 = new View(mContext);
3104         View b3 = new View(mContext);
3105         b1.setRequestedFrameRate(30f);
3106         vg1.addView(b1);
3107         vg1.addView(b2);
3108         vg1.addView(b3);
3109         mMockViewGroup.addView(vg1);
3110 
3111         ViewGroup vg2 = new MockViewGroup(mContext);
3112         View c1 = new View(mContext);
3113         View c2 = new View(mContext);
3114         View c3 = new View(mContext);
3115         vg2.addView(c1);
3116         vg2.addView(c2);
3117         vg2.propagateRequestedFrameRate(120f, false);
3118         vg2.addView(c3);
3119 
3120         vg1.addView(vg2);
3121         vg1.propagateRequestedFrameRate(60f, true);
3122 
3123         ViewGroup vg3 = new MockViewGroup(mContext);
3124         vg3.setRequestedFrameRate(80f);
3125         View d1 = new View(mContext);
3126         View d2 = new View(mContext);
3127         vg3.addView(d1);
3128         vg3.addView(d2);
3129 
3130         vg1.addView(vg3);
3131 
3132         ViewGroup vg4 = new MockViewGroup(mContext);
3133         View e1 = new View(mContext);
3134         View e2 = new View(mContext);
3135 
3136         vg4.addView(e1);
3137         vg2.addView(vg4);
3138         vg4.addView(e2);
3139 
3140         vg4.propagateRequestedFrameRate(120f, false);
3141 
3142         e1.setRequestedFrameRate(30);
3143 
3144         ViewGroup vg5 = new MockViewGroup(mContext);
3145         View f1 = new View(mContext);
3146         View f2 = new View(mContext);
3147         vg5.addView(f1);
3148         vg5.addView(f2);
3149 
3150         vg3.addView(vg5);
3151         vg5.propagateRequestedFrameRate(Float.NaN, false);
3152 
3153         assertTrue(Float.isNaN(a1.getRequestedFrameRate())); // should be default value
3154 
3155         assertEquals(60f, vg1.getRequestedFrameRate(), 0); // should inherit from vg1
3156 
3157         // the frame rate should be overridden to 60.
3158         assertEquals(60f, b1.getRequestedFrameRate(), 0);
3159         assertEquals(60f, b2.getRequestedFrameRate(), 0); // should inherit from vg1
3160         assertEquals(60f, b3.getRequestedFrameRate(), 0);
3161 
3162         // the frame rate should be overridden to 60.
3163         assertEquals(60f, vg2.getRequestedFrameRate(), 0);
3164         assertEquals(60f, c1.getRequestedFrameRate(), 0); // should inherit from vg2
3165         assertEquals(60f, c2.getRequestedFrameRate(), 0);
3166         assertEquals(60f, c3.getRequestedFrameRate(), 0);
3167 
3168         assertEquals(60f, vg3.getRequestedFrameRate(), 0); // should inherit from vg1
3169         assertEquals(60f, d1.getRequestedFrameRate(), 0);
3170         assertEquals(60f, d2.getRequestedFrameRate(), 0);
3171 
3172          // the frame rate should be overridden to 60.
3173         assertEquals(60f, vg4.getRequestedFrameRate(), 0);
3174         assertEquals(60f, e1.getRequestedFrameRate(), 0);
3175         assertEquals(60f, e2.getRequestedFrameRate(), 0);
3176 
3177         // the frame rate should be overridden to 60.
3178         assertEquals(60f, vg5.getRequestedFrameRate(), 0);
3179         assertEquals(60f, f1.getRequestedFrameRate(), 0);
3180         assertEquals(60f, f2.getRequestedFrameRate(), 0);
3181     }
3182 
3183     static class MockTextView extends TextView {
3184 
3185         public boolean isClearFocusCalled;
3186         public boolean isDispatchRestoreInstanceStateCalled;
3187         public int visibility;
3188         public boolean mIsRefreshDrawableStateCalled;
3189         public boolean isDrawCalled;
3190 
MockTextView(Context context)3191         public MockTextView(Context context) {
3192             super(context);
3193         }
3194 
3195         @Override
draw(Canvas canvas)3196         public void draw(Canvas canvas) {
3197             super.draw(canvas);
3198             isDrawCalled = true;
3199         }
3200 
3201         @Override
clearFocus()3202         public void clearFocus() {
3203             isClearFocusCalled = true;
3204             super.clearFocus();
3205         }
3206 
3207         @Override
dispatchKeyEvent(KeyEvent event)3208         public boolean dispatchKeyEvent(KeyEvent event) {
3209             return true;
3210         }
3211 
3212         @Override
dispatchRestoreInstanceState( SparseArray<Parcelable> container)3213         public void dispatchRestoreInstanceState(
3214                 SparseArray<Parcelable> container) {
3215             isDispatchRestoreInstanceStateCalled = true;
3216             super.dispatchRestoreInstanceState(container);
3217         }
3218 
3219         @Override
onTrackballEvent(MotionEvent event)3220         public boolean onTrackballEvent(MotionEvent event) {
3221             return true;
3222         }
3223 
3224         @Override
dispatchUnhandledMove(View focused, int direction)3225         public boolean dispatchUnhandledMove(View focused, int direction) {
3226             return true;
3227         }
3228 
3229         @Override
onWindowVisibilityChanged(int visibility)3230         public void onWindowVisibilityChanged(int visibility) {
3231             this.visibility = visibility;
3232             super.onWindowVisibilityChanged(visibility);
3233         }
3234 
3235         @Override
refreshDrawableState()3236         public void refreshDrawableState() {
3237             mIsRefreshDrawableStateCalled = true;
3238             super.refreshDrawableState();
3239         }
3240 
3241         @Override
dispatchTouchEvent(MotionEvent event)3242         public boolean dispatchTouchEvent(MotionEvent event) {
3243             super.dispatchTouchEvent(event);
3244             return true;
3245         }
3246 
3247         @Override
dispatchKeyEventPreIme(KeyEvent event)3248         public boolean dispatchKeyEventPreIme(KeyEvent event) {
3249             return true;
3250         }
3251 
3252         @Override
dispatchKeyShortcutEvent(KeyEvent event)3253         public boolean dispatchKeyShortcutEvent(KeyEvent event) {
3254             return true;
3255         }
3256     }
3257 
3258     static class MockViewGroup extends ViewGroup {
3259 
3260         public boolean isRecomputeViewAttributesCalled;
3261         public boolean isShowContextMenuForChildCalled;
3262         public boolean isShowContextMenuForChildCalledWithXYCoords;
3263         public boolean isRefreshDrawableStateCalled;
3264         public boolean isOnRestoreInstanceStateCalled;
3265         public boolean isOnCreateDrawableStateCalled;
3266         public boolean isOnInterceptTouchEventCalled;
3267         public boolean isOnRequestFocusInDescendantsCalled;
3268         public boolean isOnViewAddedCalled;
3269         public boolean isOnViewRemovedCalled;
3270         public boolean isFocusableViewAvailable;
3271         public boolean isDispatchDrawCalled;
3272         public boolean isRequestDisallowInterceptTouchEventCalled;
3273         public boolean isRequestTransparentRegionCalled;
3274         public boolean isGetChildStaticTransformationCalled;
3275         public int[] location;
3276         public int measureChildCalledTime;
3277         public boolean isOnAnimationEndCalled;
3278         public boolean isOnAnimationStartCalled;
3279         public int debugDepth;
3280         public int drawChildCalledTime;
3281         public Canvas canvas;
3282         public boolean isDrawableStateChangedCalled;
3283         public boolean isRequestLayoutCalled;
3284         public boolean isOnLayoutCalled;
3285         public boolean isOnDescendantInvalidatedCalled;
3286         public int left;
3287         public int top;
3288         public int right;
3289         public int bottom;
3290         public boolean returnActualFocusSearchResult;
3291 
MockViewGroup(Context context, AttributeSet attrs, int defStyle)3292         public MockViewGroup(Context context, AttributeSet attrs, int defStyle) {
3293             super(context, attrs, defStyle);
3294         }
3295 
MockViewGroup(Context context, AttributeSet attrs)3296         public MockViewGroup(Context context, AttributeSet attrs) {
3297             super(context, attrs);
3298         }
3299 
MockViewGroup(Context context)3300         public MockViewGroup(Context context) {
3301             super(context);
3302         }
3303 
3304         @Override
onLayout(boolean changed, int l, int t, int r, int b)3305         public void onLayout(boolean changed, int l, int t, int r, int b) {
3306             isOnLayoutCalled = true;
3307             left = l;
3308             top = t;
3309             right = r;
3310             bottom = b;
3311         }
3312 
3313         @Override
addViewInLayout(View child, int index, ViewGroup.LayoutParams params)3314         public boolean addViewInLayout(View child, int index,
3315                 ViewGroup.LayoutParams params) {
3316             return super.addViewInLayout(child, index, params);
3317         }
3318 
3319         @Override
addViewInLayout(View child, int index, ViewGroup.LayoutParams params, boolean preventRequestLayout)3320         public boolean addViewInLayout(View child, int index,
3321                 ViewGroup.LayoutParams params, boolean preventRequestLayout) {
3322             return super.addViewInLayout(child, index, params, preventRequestLayout);
3323         }
3324 
3325         @Override
attachLayoutAnimationParameters(View child, ViewGroup.LayoutParams params, int index, int count)3326         public void attachLayoutAnimationParameters(View child,
3327                 ViewGroup.LayoutParams params, int index, int count) {
3328             super.attachLayoutAnimationParameters(child, params, index, count);
3329         }
3330 
3331         @Override
attachViewToParent(View child, int index, LayoutParams params)3332         public void attachViewToParent(View child, int index,
3333                 LayoutParams params) {
3334             super.attachViewToParent(child, index, params);
3335         }
3336 
3337         @Override
canAnimate()3338         public boolean canAnimate() {
3339             return super.canAnimate();
3340         }
3341 
3342         @Override
checkLayoutParams(LayoutParams p)3343         public boolean checkLayoutParams(LayoutParams p) {
3344             return super.checkLayoutParams(p);
3345         }
3346 
3347         @Override
refreshDrawableState()3348         public void refreshDrawableState() {
3349             isRefreshDrawableStateCalled = true;
3350             super.refreshDrawableState();
3351         }
3352 
3353         @Override
cleanupLayoutState(View child)3354         public void cleanupLayoutState(View child) {
3355             super.cleanupLayoutState(child);
3356         }
3357 
3358         @Override
detachAllViewsFromParent()3359         public void detachAllViewsFromParent() {
3360             super.detachAllViewsFromParent();
3361         }
3362 
3363         @Override
detachViewFromParent(int index)3364         public void detachViewFromParent(int index) {
3365             super.detachViewFromParent(index);
3366         }
3367 
3368         @Override
detachViewFromParent(View child)3369         public void detachViewFromParent(View child) {
3370             super.detachViewFromParent(child);
3371         }
3372 
3373         @Override
3374 
detachViewsFromParent(int start, int count)3375         public void detachViewsFromParent(int start, int count) {
3376             super.detachViewsFromParent(start, count);
3377         }
3378 
3379         @Override
dispatchDraw(Canvas canvas)3380         public void dispatchDraw(Canvas canvas) {
3381             isDispatchDrawCalled = true;
3382             super.dispatchDraw(canvas);
3383             this.canvas = canvas;
3384         }
3385 
3386         @Override
dispatchFreezeSelfOnly(SparseArray<Parcelable> container)3387         public void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
3388             super.dispatchFreezeSelfOnly(container);
3389         }
3390 
3391         @Override
dispatchRestoreInstanceState( SparseArray<Parcelable> container)3392         public void dispatchRestoreInstanceState(
3393                 SparseArray<Parcelable> container) {
3394             super.dispatchRestoreInstanceState(container);
3395         }
3396 
3397         @Override
dispatchSaveInstanceState( SparseArray<Parcelable> container)3398         public void dispatchSaveInstanceState(
3399                 SparseArray<Parcelable> container) {
3400             super.dispatchSaveInstanceState(container);
3401         }
3402 
3403         @Override
dispatchSetPressed(boolean pressed)3404         public void dispatchSetPressed(boolean pressed) {
3405             super.dispatchSetPressed(pressed);
3406         }
3407 
3408         @Override
dispatchThawSelfOnly(SparseArray<Parcelable> container)3409         public void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
3410             super.dispatchThawSelfOnly(container);
3411         }
3412 
3413         @Override
onRestoreInstanceState(Parcelable state)3414         public void onRestoreInstanceState(Parcelable state) {
3415             isOnRestoreInstanceStateCalled = true;
3416             super.onRestoreInstanceState(state);
3417         }
3418 
3419         @Override
drawableStateChanged()3420         public void drawableStateChanged() {
3421             isDrawableStateChangedCalled = true;
3422             super.drawableStateChanged();
3423         }
3424 
3425         @Override
drawChild(Canvas canvas, View child, long drawingTime)3426         public boolean drawChild(Canvas canvas, View child, long drawingTime) {
3427             drawChildCalledTime++;
3428             return super.drawChild(canvas, child, drawingTime);
3429         }
3430 
3431         @Override
fitSystemWindows(Rect insets)3432         public boolean fitSystemWindows(Rect insets) {
3433             return super.fitSystemWindows(insets);
3434         }
3435 
3436         @Override
generateDefaultLayoutParams()3437         public LayoutParams generateDefaultLayoutParams() {
3438             return super.generateDefaultLayoutParams();
3439         }
3440 
3441         @Override
generateLayoutParams(LayoutParams p)3442         public LayoutParams generateLayoutParams(LayoutParams p) {
3443             return super.generateLayoutParams(p);
3444         }
3445 
3446         @Override
getChildDrawingOrder(int childCount, int i)3447         public int getChildDrawingOrder(int childCount, int i) {
3448             return super.getChildDrawingOrder(childCount, i);
3449         }
3450 
3451         @Override
getChildStaticTransformation(View child, Transformation t)3452         public boolean getChildStaticTransformation(View child,
3453                 Transformation t) {
3454             isGetChildStaticTransformationCalled = true;
3455             return super.getChildStaticTransformation(child, t);
3456         }
3457 
3458         @Override
measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)3459         public void measureChild(View child, int parentWidthMeasureSpec,
3460                 int parentHeightMeasureSpec) {
3461             measureChildCalledTime++;
3462             super.measureChild(child, parentWidthMeasureSpec, parentHeightMeasureSpec);
3463         }
3464 
3465         @Override
measureChildren(int widthMeasureSpec, int heightMeasureSpec)3466         public void measureChildren(int widthMeasureSpec,
3467                 int heightMeasureSpec) {
3468             super.measureChildren(widthMeasureSpec, heightMeasureSpec);
3469         }
3470 
3471         @Override
measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)3472         public void measureChildWithMargins(View child,
3473                 int parentWidthMeasureSpec, int widthUsed,
3474                 int parentHeightMeasureSpec, int heightUsed) {
3475             super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
3476                     parentHeightMeasureSpec, heightUsed);
3477         }
3478 
3479         @Override
onAnimationEnd()3480         public void onAnimationEnd() {
3481             isOnAnimationEndCalled = true;
3482             super.onAnimationEnd();
3483         }
3484 
3485         @Override
onAnimationStart()3486         public void onAnimationStart() {
3487             super.onAnimationStart();
3488             isOnAnimationStartCalled = true;
3489         }
3490 
3491         @Override
onCreateDrawableState(int extraSpace)3492         public int[] onCreateDrawableState(int extraSpace) {
3493             isOnCreateDrawableStateCalled = true;
3494             return super.onCreateDrawableState(extraSpace);
3495         }
3496 
3497         @Override
onInterceptTouchEvent(MotionEvent ev)3498         public boolean onInterceptTouchEvent(MotionEvent ev) {
3499             isOnInterceptTouchEventCalled = true;
3500             return super.onInterceptTouchEvent(ev);
3501         }
3502 
3503         @Override
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)3504         public boolean onRequestFocusInDescendants(int direction,
3505                 Rect previouslyFocusedRect) {
3506             isOnRequestFocusInDescendantsCalled = true;
3507             return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
3508         }
3509 
3510         @Override
onViewAdded(View child)3511         public void onViewAdded(View child) {
3512             isOnViewAddedCalled = true;
3513             super.onViewAdded(child);
3514         }
3515 
3516         @Override
onViewRemoved(View child)3517         public void onViewRemoved(View child) {
3518             isOnViewRemovedCalled = true;
3519             super.onViewRemoved(child);
3520         }
3521 
3522         @Override
recomputeViewAttributes(View child)3523         public void recomputeViewAttributes(View child) {
3524             isRecomputeViewAttributesCalled = true;
3525             super.recomputeViewAttributes(child);
3526         }
3527 
3528         @Override
removeDetachedView(View child, boolean animate)3529         public void removeDetachedView(View child, boolean animate) {
3530             super.removeDetachedView(child, animate);
3531         }
3532 
3533         @Override
showContextMenuForChild(View originalView)3534         public boolean showContextMenuForChild(View originalView) {
3535             isShowContextMenuForChildCalled = true;
3536             return super.showContextMenuForChild(originalView);
3537         }
3538 
3539         @Override
showContextMenuForChild(View originalView, float x, float y)3540         public boolean showContextMenuForChild(View originalView, float x, float y) {
3541             isShowContextMenuForChildCalledWithXYCoords = true;
3542             return super.showContextMenuForChild(originalView, x, y);
3543         }
3544 
3545         @Override
isInTouchMode()3546         public boolean isInTouchMode() {
3547             super.isInTouchMode();
3548             return false;
3549         }
3550 
3551         @Override
focusableViewAvailable(View v)3552         public void focusableViewAvailable(View v) {
3553             isFocusableViewAvailable = true;
3554             super.focusableViewAvailable(v);
3555         }
3556 
3557         @Override
focusSearch(View focused, int direction)3558         public View focusSearch(View focused, int direction) {
3559             if (returnActualFocusSearchResult) {
3560                 return super.focusSearch(focused, direction);
3561             } else {
3562                 super.focusSearch(focused, direction);
3563                 return focused;
3564             }
3565         }
3566 
3567         @Override
requestDisallowInterceptTouchEvent(boolean disallowIntercept)3568         public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
3569             isRequestDisallowInterceptTouchEventCalled = true;
3570             super.requestDisallowInterceptTouchEvent(disallowIntercept);
3571         }
3572 
3573         @Override
requestTransparentRegion(View child)3574         public void requestTransparentRegion(View child) {
3575             isRequestTransparentRegionCalled = true;
3576             super.requestTransparentRegion(child);
3577         }
3578 
3579         @Override
debug(int depth)3580         public void debug(int depth) {
3581             debugDepth = depth;
3582             super.debug(depth);
3583         }
3584 
3585         @Override
requestLayout()3586         public void requestLayout() {
3587             isRequestLayoutCalled = true;
3588             super.requestLayout();
3589         }
3590 
3591         @Override
setStaticTransformationsEnabled(boolean enabled)3592         public void setStaticTransformationsEnabled(boolean enabled) {
3593             super.setStaticTransformationsEnabled(enabled);
3594         }
3595 
3596         @Override
resetRtlProperties()3597         public void resetRtlProperties() {
3598             super.resetRtlProperties();
3599             resetRtlPropertiesCount++;
3600         }
3601 
3602         @Override
resetResolvedLayoutDirection()3603         public void resetResolvedLayoutDirection() {
3604             super.resetResolvedLayoutDirection();
3605             resetResolvedLayoutDirectionCount++;
3606         }
3607 
3608         @Override
resetResolvedTextDirection()3609         public void resetResolvedTextDirection() {
3610             super.resetResolvedTextDirection();
3611             resetResolvedTextDirectionCount++;
3612         }
3613 
3614         @Override
resetResolvedTextAlignment()3615         public void resetResolvedTextAlignment() {
3616             super.resetResolvedTextAlignment();
3617             resetResolvedTextAlignmentCount++;
3618         }
3619 
3620         @Override
resetResolvedPadding()3621         public void resetResolvedPadding() {
3622             super.resetResolvedPadding();
3623             resetResolvedPaddingCount++;
3624         }
3625 
3626         @Override
resetResolvedDrawables()3627         protected void resetResolvedDrawables() {
3628             super.resetResolvedDrawables();
3629             resetResolvedDrawablesCount++;
3630         }
3631 
3632         @Override
onDescendantInvalidated(@onNull View child, @NonNull View target)3633         public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
3634             isOnDescendantInvalidatedCalled = true;
3635             super.onDescendantInvalidated(child, target);
3636         }
3637 
3638         @Override
setChildrenDrawnWithCacheEnabled(boolean enabled)3639         public void setChildrenDrawnWithCacheEnabled(boolean enabled) {
3640             super.setChildrenDrawnWithCacheEnabled(enabled);
3641         }
3642 
3643         @Override
isChildrenDrawnWithCacheEnabled()3644         public boolean isChildrenDrawnWithCacheEnabled() {
3645             return super.isChildrenDrawnWithCacheEnabled();
3646         }
3647     }
3648 
3649     static class MockView2 extends View {
3650 
MockView2(Context context)3651         public MockView2(Context context) {
3652             super(context);
3653         }
3654 
MockView2(Context context, AttributeSet attrs)3655         public MockView2(Context context, AttributeSet attrs) {
3656             super(context, attrs);
3657         }
3658 
MockView2(Context context, AttributeSet attrs, int defStyle)3659         public MockView2(Context context, AttributeSet attrs, int defStyle) {
3660             super(context, attrs, defStyle);
3661         }
3662 
3663         @Override
resetRtlProperties()3664         public void resetRtlProperties() {
3665             super.resetRtlProperties();
3666             resetRtlPropertiesCount++;
3667         }
3668 
3669         @Override
resetResolvedLayoutDirection()3670         public void resetResolvedLayoutDirection() {
3671             super.resetResolvedLayoutDirection();
3672             resetResolvedLayoutDirectionCount++;
3673         }
3674 
3675         @Override
resetResolvedTextDirection()3676         public void resetResolvedTextDirection() {
3677             super.resetResolvedTextDirection();
3678             resetResolvedTextDirectionCount++;
3679         }
3680 
3681         @Override
resetResolvedTextAlignment()3682         public void resetResolvedTextAlignment() {
3683             super.resetResolvedTextAlignment();
3684             resetResolvedTextAlignmentCount++;
3685         }
3686 
3687         @Override
resetResolvedPadding()3688         public void resetResolvedPadding() {
3689             super.resetResolvedPadding();
3690             resetResolvedPaddingCount++;
3691         }
3692 
3693         @Override
resetResolvedDrawables()3694         protected void resetResolvedDrawables() {
3695             super.resetResolvedDrawables();
3696             resetResolvedDrawablesCount++;
3697         }
3698     }
3699 
3700     static final class TemporaryDetachingMockView extends View {
3701         private int mDispatchStartTemporaryDetachCount = 0;
3702         private int mDispatchFinishTemporaryDetachCount = 0;
3703         private int mOnStartTemporaryDetachCount = 0;
3704         private int mOnFinishTemporaryDetachCount = 0;
3705 
TemporaryDetachingMockView(Context context)3706         public TemporaryDetachingMockView(Context context) {
3707             super(context);
3708         }
3709 
3710         @Override
dispatchStartTemporaryDetach()3711         public void dispatchStartTemporaryDetach() {
3712             super.dispatchStartTemporaryDetach();
3713             mDispatchStartTemporaryDetachCount += 1;
3714         }
3715 
3716         @Override
dispatchFinishTemporaryDetach()3717         public void dispatchFinishTemporaryDetach() {
3718             super.dispatchFinishTemporaryDetach();
3719             mDispatchFinishTemporaryDetachCount += 1;
3720         }
3721 
3722         @Override
onStartTemporaryDetach()3723         public void onStartTemporaryDetach() {
3724             super.onStartTemporaryDetach();
3725             mOnStartTemporaryDetachCount += 1;
3726         }
3727 
3728         @Override
onFinishTemporaryDetach()3729         public void onFinishTemporaryDetach() {
3730             super.onFinishTemporaryDetach();
3731             mOnFinishTemporaryDetachCount += 1;
3732         }
3733 
getDispatchStartTemporaryDetachCount()3734         public int getDispatchStartTemporaryDetachCount() {
3735             return mDispatchStartTemporaryDetachCount;
3736         }
3737 
getDispatchFinishTemporaryDetachCount()3738         public int getDispatchFinishTemporaryDetachCount() {
3739             return mDispatchFinishTemporaryDetachCount;
3740         }
3741 
getOnStartTemporaryDetachCount()3742         public int getOnStartTemporaryDetachCount() {
3743             return mOnStartTemporaryDetachCount;
3744         }
3745 
getOnFinishTemporaryDetachCount()3746         public int getOnFinishTemporaryDetachCount() {
3747             return mOnFinishTemporaryDetachCount;
3748         }
3749     }
3750 
3751     public static class ScrollTestView extends ViewGroup {
ScrollTestView(Context context)3752         public ScrollTestView(Context context) {
3753             super(context);
3754         }
3755 
3756         @Override
onLayout(boolean changed, int l, int t, int r, int b)3757         protected void onLayout(boolean changed, int l, int t, int r, int b) {}
3758 
3759         @Override
awakenScrollBars()3760         public boolean awakenScrollBars() {
3761             return super.awakenScrollBars();
3762         }
3763 
3764         @Override
computeHorizontalScrollRange()3765         public int computeHorizontalScrollRange() {
3766             return super.computeHorizontalScrollRange();
3767         }
3768 
3769         @Override
computeHorizontalScrollExtent()3770         public int computeHorizontalScrollExtent() {
3771             return super.computeHorizontalScrollExtent();
3772         }
3773 
3774         @Override
computeVerticalScrollRange()3775         public int computeVerticalScrollRange() {
3776             return super.computeVerticalScrollRange();
3777         }
3778 
3779         @Override
computeVerticalScrollExtent()3780         public int computeVerticalScrollExtent() {
3781             return super.computeVerticalScrollExtent();
3782         }
3783 
3784         @Override
getHorizontalScrollbarHeight()3785         protected int getHorizontalScrollbarHeight() {
3786             return super.getHorizontalScrollbarHeight();
3787         }
3788     }
3789 
3790     @Override
setResult(int resultCode)3791     public void setResult(int resultCode) {
3792         synchronized (mSync) {
3793             mSync.mHasNotify = true;
3794             mSync.notify();
3795             mResultCode = resultCode;
3796         }
3797     }
3798 }
3799