1 /*
2  * Copyright (C) 2008 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 org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertSame;
24 import static org.junit.Assert.assertTrue;
25 import static org.mockito.Matchers.any;
26 import static org.mockito.Matchers.anyInt;
27 import static org.mockito.Mockito.doReturn;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.reset;
31 import static org.mockito.Mockito.times;
32 import static org.mockito.Mockito.verify;
33 
34 import android.app.Instrumentation;
35 import android.app.Presentation;
36 import android.content.Context;
37 import android.content.res.Configuration;
38 import android.content.res.TypedArray;
39 import android.graphics.Color;
40 import android.graphics.PixelFormat;
41 import android.graphics.drawable.ColorDrawable;
42 import android.graphics.drawable.Drawable;
43 import android.hardware.display.DisplayManager;
44 import android.hardware.display.VirtualDisplay;
45 import android.media.AudioManager;
46 import android.net.Uri;
47 import android.os.Bundle;
48 import android.os.Handler;
49 import android.os.SystemClock;
50 import android.support.test.InstrumentationRegistry;
51 import android.support.test.annotation.UiThreadTest;
52 import android.support.test.filters.MediumTest;
53 import android.support.test.rule.ActivityTestRule;
54 import android.support.test.runner.AndroidJUnit4;
55 import android.util.DisplayMetrics;
56 import android.util.Log;
57 import android.view.ContextThemeWrapper;
58 import android.view.Display;
59 import android.view.Gravity;
60 import android.view.InputDevice;
61 import android.view.InputQueue;
62 import android.view.KeyEvent;
63 import android.view.LayoutInflater;
64 import android.view.MotionEvent;
65 import android.view.Surface;
66 import android.view.SurfaceHolder;
67 import android.view.SurfaceView;
68 import android.view.View;
69 import android.view.ViewGroup;
70 import android.view.Window;
71 import android.view.WindowManager;
72 import android.widget.Button;
73 import android.widget.TextView;
74 
75 import com.android.compatibility.common.util.PollingCheck;
76 
77 import org.junit.After;
78 import org.junit.Before;
79 import org.junit.Rule;
80 import org.junit.Test;
81 import org.junit.runner.RunWith;
82 
83 import java.util.concurrent.Semaphore;
84 import java.util.concurrent.TimeUnit;
85 
86 @MediumTest
87 @RunWith(AndroidJUnit4.class)
88 public class WindowTest {
89     private static final String TAG = "WindowTest";
90     private static final int VIEWGROUP_LAYOUT_HEIGHT = 100;
91     private static final int VIEWGROUP_LAYOUT_WIDTH = 200;
92 
93     private Instrumentation mInstrumentation;
94     private WindowCtsActivity mActivity;
95     private Window mWindow;
96     private Window.Callback mWindowCallback;
97     private SurfaceView mSurfaceView;
98 
99     // for testing setLocalFocus
100     private ProjectedPresentation mPresentation;
101     private VirtualDisplay mVirtualDisplay;
102 
103     @Rule
104     public ActivityTestRule<WindowCtsActivity> mActivityRule =
105             new ActivityTestRule<>(WindowCtsActivity.class);
106 
107     @Before
setup()108     public void setup() {
109         mInstrumentation = InstrumentationRegistry.getInstrumentation();
110         mActivity = mActivityRule.getActivity();
111         mWindow = mActivity.getWindow();
112 
113 
114         mWindowCallback = mock(Window.Callback.class);
115         doReturn(true).when(mWindowCallback).dispatchKeyEvent(any());
116         doReturn(true).when(mWindowCallback).dispatchTouchEvent(any());
117         doReturn(true).when(mWindowCallback).dispatchTrackballEvent(any());
118         doReturn(true).when(mWindowCallback).dispatchGenericMotionEvent(any());
119         doReturn(true).when(mWindowCallback).dispatchPopulateAccessibilityEvent(any());
120         doReturn(true).when(mWindowCallback).onMenuItemSelected(anyInt(), any());
121     }
122 
123     @After
teardown()124     public void teardown() {
125         if (mActivity != null) {
126             mActivity.setFlagFalse();
127         }
128     }
129 
130     @UiThreadTest
131     @Test
testConstructor()132     public void testConstructor() {
133         mWindow = new MockWindow(mActivity);
134         assertSame(mActivity, mWindow.getContext());
135     }
136 
137     /**
138      * Test flags related methods:
139      * 1. addFlags: add the given flag to WindowManager.LayoutParams.flags, if add more than one
140      *    in sequence, flags will be set to formerFlag | latterFlag.
141      * 2. setFlags: _1. set the flags of the window.
142      *              _2. test invocation of Window.Callback#onWindowAttributesChanged.
143      * 3. clearFlags: clear the flag bits as specified in flags.
144      */
145     @Test
testOpFlags()146     public void testOpFlags() {
147         mWindow = new MockWindow(mActivity);
148         final WindowManager.LayoutParams attrs = mWindow.getAttributes();
149         assertEquals(0, attrs.flags);
150 
151         mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
152         assertEquals(WindowManager.LayoutParams.FLAG_FULLSCREEN, attrs.flags);
153 
154         mWindow.addFlags(WindowManager.LayoutParams.FLAG_DITHER);
155         assertEquals(WindowManager.LayoutParams.FLAG_FULLSCREEN
156                 | WindowManager.LayoutParams.FLAG_DITHER, attrs.flags);
157 
158         mWindow.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
159         assertEquals(WindowManager.LayoutParams.FLAG_DITHER, attrs.flags);
160         mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DITHER);
161         assertEquals(0, attrs.flags);
162 
163         mWindow.setCallback(mWindowCallback);
164         verify(mWindowCallback, never()).onWindowAttributesChanged(any());
165         // mask == flag, no bit of flag need to be modified.
166         mWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
167                 WindowManager.LayoutParams.FLAG_FULLSCREEN);
168         assertEquals(WindowManager.LayoutParams.FLAG_FULLSCREEN, attrs.flags);
169 
170         // Test if the callback method is called by system
171         verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs);
172         mWindow.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
173         mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DITHER);
174     }
175 
176     @Test
testFindViewById()177     public void testFindViewById() {
178         TextView v = mWindow.findViewById(R.id.listview_window);
179         assertNotNull(v);
180         assertEquals(R.id.listview_window, v.getId());
181     }
182 
183     @Test
testRequireViewById()184     public void testRequireViewById() {
185         TextView v = mWindow.requireViewById(R.id.listview_window);
186         assertNotNull(v);
187         assertEquals(R.id.listview_window, v.getId());
188     }
189 
190     @Test(expected = IllegalArgumentException.class)
testRequireViewByIdNoId()191     public void testRequireViewByIdNoId() {
192         TextView v = mWindow.requireViewById(View.NO_ID);
193     }
194 
195     @Test(expected = IllegalArgumentException.class)
testRequireViewByIdInvalid()196     public void testRequireViewByIdInvalid() {
197         TextView v = mWindow.requireViewById(R.id.view); // not present in layout
198     }
199 
200     /**
201      * getAttributes: Retrieve the current window attributes associated with this panel.
202      *    Return is 1.the existing window attributes object.
203      *              2.a freshly created one if there is none.
204      * setAttributes: Specify custom window attributes.
205      *    Here we just set some parameters to test if it can set, and the window is just
206      *    available in this method. But it's not proper to setAttributes arbitrarily.
207      * setCallback: Set the Callback interface for this window. In Window.java,
208      *    there is just one method, onWindowAttributesChanged, used.
209      * getCallback: Return the current Callback interface for this window.
210      */
211     @Test
testAccessAttributes()212     public void testAccessAttributes() {
213         mWindow = new MockWindow(mActivity);
214 
215         // default attributes
216         WindowManager.LayoutParams attr = mWindow.getAttributes();
217         assertEquals(WindowManager.LayoutParams.MATCH_PARENT, attr.width);
218         assertEquals(WindowManager.LayoutParams.MATCH_PARENT, attr.height);
219         assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION, attr.type);
220         assertEquals(PixelFormat.OPAQUE, attr.format);
221 
222         int width = 200;
223         int height = 300;
224         WindowManager.LayoutParams param = new WindowManager.LayoutParams(width, height,
225                 WindowManager.LayoutParams.TYPE_BASE_APPLICATION,
226                 WindowManager.LayoutParams.FLAG_DITHER, PixelFormat.RGBA_8888);
227         mWindow.setCallback(mWindowCallback);
228         assertSame(mWindowCallback, mWindow.getCallback());
229         verify(mWindowCallback, never()).onWindowAttributesChanged(any());
230         mWindow.setAttributes(param);
231         attr = mWindow.getAttributes();
232         assertEquals(width, attr.width);
233         assertEquals(height, attr.height);
234         assertEquals(WindowManager.LayoutParams.TYPE_BASE_APPLICATION, attr.type);
235         assertEquals(PixelFormat.RGBA_8888, attr.format);
236         assertEquals(WindowManager.LayoutParams.FLAG_DITHER, attr.flags);
237         verify(mWindowCallback, times(1)).onWindowAttributesChanged(attr);
238     }
239 
240     /**
241      * If not set container, the DecorWindow operates as a top-level window, the mHasChildren of
242      * container is false;
243      * Otherwise, it will display itself meanwhile container's mHasChildren is true.
244      */
245     @Test
testAccessContainer()246     public void testAccessContainer() {
247         mWindow = new MockWindow(mActivity);
248         assertNull(mWindow.getContainer());
249         assertFalse(mWindow.hasChildren());
250 
251         MockWindow container = new MockWindow(mActivity);
252         mWindow.setContainer(container);
253         assertSame(container, mWindow.getContainer());
254         assertTrue(container.hasChildren());
255     }
256 
257     /**
258      * addContentView: add an additional content view to the screen.
259      *    1.Added after any existing ones in the screen.
260      *    2.Existing views are NOT removed.
261      * getLayoutInflater: Quick access to the {@link LayoutInflater} instance that this Window
262      *    retrieved from its Context.
263      */
264     @UiThreadTest
265     @Test
testAddContentView()266     public void testAddContentView() {
267         final ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(VIEWGROUP_LAYOUT_WIDTH,
268                 VIEWGROUP_LAYOUT_HEIGHT);
269         // The LayoutInflater instance will be inflated to a view and used by
270         // addContentView,
271         // id of this view should be same with inflated id.
272         final LayoutInflater inflater = mActivity.getLayoutInflater();
273         TextView addedView = (TextView) mWindow.findViewById(R.id.listview_addwindow);
274         assertNull(addedView);
275         mWindow.addContentView(inflater.inflate(R.layout.windowstub_addlayout, null), lp);
276         TextView view = (TextView) mWindow.findViewById(R.id.listview_window);
277         addedView = (TextView) mWindow.findViewById(R.id.listview_addwindow);
278         assertNotNull(view);
279         assertNotNull(addedView);
280         assertEquals(R.id.listview_window, view.getId());
281         assertEquals(R.id.listview_addwindow, addedView.getId());
282     }
283 
284     /**
285      * getCurrentFocus: Return the view in this Window that currently has focus, or null if
286      *                  there are none.
287      * The test will be:
288      * 1. Set focus view to null, get current focus, it should be null
289      * 2. Set listview_window as focus view, get it and compare.
290      */
291     @UiThreadTest
292     @Test
testGetCurrentFocus()293     public void testGetCurrentFocus() {
294         TextView v = (TextView) mWindow.findViewById(R.id.listview_window);
295         v.clearFocus();
296         assertNull(mWindow.getCurrentFocus());
297 
298         v.setFocusable(true);
299         assertTrue(v.isFocusable());
300         assertTrue(v.requestFocus());
301         View focus = mWindow.getCurrentFocus();
302         assertNotNull(focus);
303         assertEquals(R.id.listview_window, focus.getId());
304     }
305 
306     /**
307      * 1. getDecorView() retrieves the top-level window decor view, which contains the standard
308      *    window frame/decorations and the client's content inside of that, we should check the
309      *    primary components of this view which have no relationship concrete realization of Window.
310      *    Therefore we should check if the size of this view equals to the screen size and the
311      *    ontext is same as Window's context.
312      * 2. Return null if decor view is not created, else the same with detDecorView.
313      */
314     @Test
testDecorView()315     public void testDecorView() {
316         mInstrumentation.waitForIdleSync();
317         View decor = mWindow.getDecorView();
318         assertNotNull(decor);
319         verifyDecorView(decor);
320 
321         decor = mWindow.peekDecorView();
322         if (decor != null) {
323             verifyDecorView(decor);
324         }
325     }
326 
verifyDecorView(View decor)327     private void verifyDecorView(View decor) {
328         DisplayMetrics dm = new DisplayMetrics();
329         mActivity.getWindowManager().getDefaultDisplay().getMetrics(dm);
330         int screenWidth = dm.widthPixels;
331         int screenHeight = dm.heightPixels;
332         assertTrue(decor.getWidth() >= screenWidth);
333         assertTrue(decor.getHeight() >= screenHeight);
334         assertTrue(decor.getContext() instanceof ContextThemeWrapper);
335     }
336 
337     /**
338      * setVolumeControlStream: Suggests an audio stream whose volume should be changed by
339      *    the hardware volume controls.
340      * getVolumeControlStream: Gets the suggested audio stream whose volume should be changed by
341      *    the harwdare volume controls.
342      */
343     @Test
testAccessVolumeControlStream()344     public void testAccessVolumeControlStream() {
345         // Default value is AudioManager.USE_DEFAULT_STREAM_TYPE, see javadoc of
346         // {@link Activity#setVolumeControlStream}.
347         assertEquals(AudioManager.USE_DEFAULT_STREAM_TYPE, mWindow.getVolumeControlStream());
348         mWindow.setVolumeControlStream(AudioManager.STREAM_MUSIC);
349         assertEquals(AudioManager.STREAM_MUSIC, mWindow.getVolumeControlStream());
350     }
351 
352     /**
353      * setWindowManager: Set the window manager for use by this Window.
354      * getWindowManager: Return the window manager allowing this Window to display its own
355      *    windows.
356      */
357     @Test
testAccessWindowManager()358     public void testAccessWindowManager() {
359         mWindow = new MockWindow(mActivity);
360         WindowManager expected = (WindowManager) mActivity.getSystemService(
361                 Context.WINDOW_SERVICE);
362         assertNull(mWindow.getWindowManager());
363         mWindow.setWindowManager(expected, null,
364                 mActivity.getApplicationInfo().loadLabel(mActivity.getPackageManager()).toString());
365         // No way to compare the expected and actual directly, they are
366         // different object
367         assertNotNull(mWindow.getWindowManager());
368     }
369 
370     /**
371      * Return the {@link android.R.styleable#Window} attributes from this
372      * window's theme. It's invisible.
373      */
374     @Test
testGetWindowStyle()375     public void testGetWindowStyle() {
376         mWindow = new MockWindow(mActivity);
377         final TypedArray windowStyle = mWindow.getWindowStyle();
378         // the windowStyle is obtained from
379         // com.android.internal.R.styleable.Window whose details
380         // are invisible for user.
381         assertNotNull(windowStyle);
382     }
383 
384     @Test
testIsActive()385     public void testIsActive() {
386         MockWindow window = new MockWindow(mActivity);
387         assertFalse(window.isActive());
388 
389         window.makeActive();
390         assertTrue(window.isActive());
391         assertTrue(window.mIsOnActiveCalled);
392     }
393 
394     /**
395      * isFloating: Return whether this window is being displayed with a floating style
396      * (based on the {@link android.R.attr#windowIsFloating} attribute in the style/theme).
397      */
398     @Test
testIsFloating()399     public void testIsFloating() {
400         // Default system theme defined by themes.xml, the windowIsFloating is set false.
401         assertFalse(mWindow.isFloating());
402     }
403 
404     /**
405      * Change the background of this window to a custom Drawable.
406      * Setting the background to null will make the window be opaque(No way to get the window
407      *  attribute of PixelFormat to check if the window is opaque). To make the window
408      * transparent, you can use an empty drawable(eg. ColorDrawable with the color 0).
409      */
410     @Test
testSetBackgroundDrawable()411     public void testSetBackgroundDrawable() throws Throwable {
412         // DecorView holds the background
413         View decor = mWindow.getDecorView();
414         if (!mWindow.hasFeature(Window.FEATURE_SWIPE_TO_DISMISS)) {
415             assertEquals(PixelFormat.OPAQUE, decor.getBackground().getOpacity());
416         }
417         // setBackgroundDrawableResource(int resId) has the same
418         // functionality with setBackgroundDrawable(Drawable drawable), just different in
419         // parameter.
420         mActivityRule.runOnUiThread(() -> mWindow.setBackgroundDrawableResource(R.drawable.faces));
421         mInstrumentation.waitForIdleSync();
422 
423         mActivityRule.runOnUiThread(() -> {
424             ColorDrawable drawable = new ColorDrawable(0);
425             mWindow.setBackgroundDrawable(drawable);
426         });
427         mInstrumentation.waitForIdleSync();
428         decor = mWindow.getDecorView();
429         // Color 0 with one alpha bit
430         assertEquals(PixelFormat.TRANSPARENT, decor.getBackground().getOpacity());
431 
432         mActivityRule.runOnUiThread(() -> mWindow.setBackgroundDrawable(null));
433         mInstrumentation.waitForIdleSync();
434         decor = mWindow.getDecorView();
435         assertNull(decor.getBackground());
436     }
437 
438     /**
439      * setContentView(int): set the screen content from a layout resource.
440      * setContentView(View): set the screen content to an explicit view.
441      * setContentView(View, LayoutParams): Set the screen content to an explicit view.
442      *
443      * Note that calling this function "locks in" various characteristics
444      * of the window that can not, from this point forward, be changed: the
445      * features that have been requested with {@link #requestFeature(int)},
446      * and certain window flags as described in {@link #setFlags(int, int)}.
447      *   This functionality point is hard to test:
448      *   1. can't get the features requested because the getter is protected final.
449      *   2. certain window flags are not clear to concrete one.
450      */
451     @Test
testSetContentView()452     public void testSetContentView() throws Throwable {
453         final ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(VIEWGROUP_LAYOUT_WIDTH,
454                 VIEWGROUP_LAYOUT_HEIGHT);
455         final LayoutInflater inflate = mActivity.getLayoutInflater();
456 
457         mActivityRule.runOnUiThread(() -> {
458             TextView view;
459             View setView;
460             // Test setContentView(int layoutResID)
461             mWindow.setContentView(R.layout.windowstub_layout);
462             view = (TextView) mWindow.findViewById(R.id.listview_window);
463             assertNotNull(view);
464             assertEquals(R.id.listview_window, view.getId());
465 
466             // Test setContentView(View view)
467             setView = inflate.inflate(R.layout.windowstub_addlayout, null);
468             mWindow.setContentView(setView);
469             view = (TextView) mWindow.findViewById(R.id.listview_addwindow);
470             assertNotNull(view);
471             assertEquals(R.id.listview_addwindow, view.getId());
472 
473             // Test setContentView(View view, ViewGroup.LayoutParams params)
474             setView = inflate.inflate(R.layout.windowstub_layout, null);
475             mWindow.setContentView(setView, lp);
476             assertEquals(VIEWGROUP_LAYOUT_WIDTH, setView.getLayoutParams().width);
477             assertEquals(VIEWGROUP_LAYOUT_HEIGHT, setView.getLayoutParams().height);
478             view = (TextView) mWindow.findViewById(R.id.listview_window);
479             assertNotNull(view);
480             assertEquals(R.id.listview_window, view.getId());
481         });
482         mInstrumentation.waitForIdleSync();
483     }
484 
485     @Test
testSetTitle()486     public void testSetTitle() throws Throwable {
487         final String title = "Android Window Test";
488         mActivityRule.runOnUiThread(() -> {
489             mWindow.setTitle(title);
490             mWindow.setTitleColor(Color.BLUE);
491         });
492         mInstrumentation.waitForIdleSync();
493         // No way to get title and title color
494     }
495 
496     /**
497      * takeKeyEvents: Request that key events come to this activity. Use this if your activity
498      * has no views with focus, but the activity still wants a chance to process key events.
499      */
500     @Test
testTakeKeyEvents()501     public void testTakeKeyEvents() throws Throwable {
502         mActivityRule.runOnUiThread(() -> {
503             View v = mWindow.findViewById(R.id.listview_window);
504             v.clearFocus();
505             assertNull(mWindow.getCurrentFocus());
506             mWindow.takeKeyEvents(false);
507         });
508         mInstrumentation.waitForIdleSync();
509     }
510 
511     /**
512      * setDefaultWindowFormat: Set the format of window, as per the PixelFormat types. This
513      *    is the format that will be used unless the client specifies in explicit format with
514      *    setFormat().
515      * setFormat: Set the format of window, as per the PixelFormat types.
516      *            param format: The new window format (see PixelFormat).  Use
517      *                          PixelFormat.UNKNOWN to allow the Window to select
518      *                          the format.
519      */
520     @Test
testSetDefaultWindowFormat()521     public void testSetDefaultWindowFormat() {
522         MockWindow window = new MockWindow(mActivity);
523 
524         // mHaveWindowFormat will be true after set PixelFormat.OPAQUE and
525         // setDefaultWindowFormat is invalid
526         window.setFormat(PixelFormat.OPAQUE);
527         window.setCallback(mWindowCallback);
528         verify(mWindowCallback, never()).onWindowAttributesChanged(any());
529         window.setDefaultWindowFormat(PixelFormat.JPEG);
530         assertEquals(PixelFormat.OPAQUE, window.getAttributes().format);
531         verify(mWindowCallback, never()).onWindowAttributesChanged(any());
532 
533         // mHaveWindowFormat will be false after set PixelFormat.UNKNOWN and
534         // setDefaultWindowFormat is valid
535         window.setFormat(PixelFormat.UNKNOWN);
536         reset(mWindowCallback);
537         window.setDefaultWindowFormat(PixelFormat.JPEG);
538         assertEquals(PixelFormat.JPEG, window.getAttributes().format);
539         verify(mWindowCallback, times(1)).onWindowAttributesChanged(window.getAttributes());
540     }
541 
542     /**
543      * Set the gravity of the window
544      */
545     @Test
testSetGravity()546     public void testSetGravity() {
547         mWindow = new MockWindow(mActivity);
548         WindowManager.LayoutParams attrs = mWindow.getAttributes();
549         assertEquals(0, attrs.gravity);
550 
551         mWindow.setCallback(mWindowCallback);
552         verify(mWindowCallback, never()).onWindowAttributesChanged(any());
553         mWindow.setGravity(Gravity.TOP);
554         attrs = mWindow.getAttributes();
555         assertEquals(Gravity.TOP, attrs.gravity);
556         verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs);
557     }
558 
559     /**
560      * Set the width and height layout parameters of the window.
561      *    1.The default for both of these is MATCH_PARENT;
562      *    2.You can change them to WRAP_CONTENT to make a window that is not full-screen.
563      */
564     @Test
testSetLayout()565     public void testSetLayout() {
566         mWindow = new MockWindow(mActivity);
567         WindowManager.LayoutParams attrs = mWindow.getAttributes();
568         assertEquals(WindowManager.LayoutParams.MATCH_PARENT, attrs.width);
569         assertEquals(WindowManager.LayoutParams.MATCH_PARENT, attrs.height);
570 
571         mWindow.setCallback(mWindowCallback);
572         verify(mWindowCallback, never()).onWindowAttributesChanged(any());
573         mWindow.setLayout(WindowManager.LayoutParams.WRAP_CONTENT,
574                 WindowManager.LayoutParams.WRAP_CONTENT);
575         attrs = mWindow.getAttributes();
576         assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, attrs.width);
577         assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, attrs.height);
578         verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs);
579     }
580 
581     /**
582      * Set the type of the window, as per the WindowManager.LayoutParams types.
583      */
584     @Test
testSetType()585     public void testSetType() {
586         mWindow = new MockWindow(mActivity);
587         WindowManager.LayoutParams attrs = mWindow.getAttributes();
588         assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION, attrs.type);
589 
590         mWindow.setCallback(mWindowCallback);
591         verify(mWindowCallback, never()).onWindowAttributesChanged(any());
592         mWindow.setType(WindowManager.LayoutParams.TYPE_BASE_APPLICATION);
593         attrs = mWindow.getAttributes();
594         assertEquals(WindowManager.LayoutParams.TYPE_BASE_APPLICATION, attrs.type);
595         verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs);
596     }
597 
598     /**
599      * Specify an explicit soft input mode to use for the window, as per
600      * WindowManager.LayoutParams#softInputMode.
601      *    1.Providing "unspecified" here will NOT override the input mode the window.
602      *    2.Providing "unspecified" here will override the input mode the window.
603      */
604     @Test
testSetSoftInputMode()605     public void testSetSoftInputMode() {
606         mWindow = new MockWindow(mActivity);
607         assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED,
608                 mWindow.getAttributes().softInputMode);
609         mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
610         assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE,
611                 mWindow.getAttributes().softInputMode);
612         mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED);
613         assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE,
614                 mWindow.getAttributes().softInputMode);
615     }
616 
617     /**
618      * Specify custom animations to use for the window, as per
619      * WindowManager.LayoutParams#windowAnimations.
620      *    1.Providing 0 here will NOT override the animations the window(But here we can't check
621      *    it because the getter is in WindowManagerService and is private)
622      *    2.Providing 0 here will override the animations the window.
623      */
624     @Test
testSetWindowAnimations()625     public void testSetWindowAnimations() {
626         mWindow = new MockWindow(mActivity);
627 
628         mWindow.setCallback(mWindowCallback);
629         verify(mWindowCallback, never()).onWindowAttributesChanged(any());
630         mWindow.setWindowAnimations(R.anim.alpha);
631         WindowManager.LayoutParams attrs = mWindow.getAttributes();
632         assertEquals(R.anim.alpha, attrs.windowAnimations);
633         verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs);
634     }
635 
636     /**
637      * Test setLocalFocus together with injectInputEvent.
638      */
639     @Test
testSetLocalFocus()640     public void testSetLocalFocus() throws Throwable {
641         mActivityRule.runOnUiThread(() -> mSurfaceView = new SurfaceView(mActivity));
642         mInstrumentation.waitForIdleSync();
643 
644         final Semaphore waitingSemaphore = new Semaphore(0);
645         mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
646             @Override
647             public void surfaceCreated(SurfaceHolder holder) {
648             }
649 
650             @Override
651             public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
652                 destroyPresentation();
653                 createPresentation(holder.getSurface(), width, height);
654                 waitingSemaphore.release();
655             }
656 
657             @Override
658             public void surfaceDestroyed(SurfaceHolder holder) {
659                 destroyPresentation();
660             }
661         });
662         mActivityRule.runOnUiThread(() -> mWindow.setContentView(mSurfaceView));
663         mInstrumentation.waitForIdleSync();
664         assertTrue(waitingSemaphore.tryAcquire(5, TimeUnit.SECONDS));
665         assertNotNull(mVirtualDisplay);
666         assertNotNull(mPresentation);
667 
668         PollingCheck.waitFor(() -> (mPresentation.button1 != null)
669                 && (mPresentation.button2 != null) && (mPresentation.button3 != null)
670                 && mPresentation.ready);
671         assertTrue(mPresentation.button1.isFocusable() && mPresentation.button2.isFocusable() &&
672                 mPresentation.button3.isFocusable());
673         // currently it is only for debugging
674         View.OnFocusChangeListener listener = (View v, boolean hasFocus) ->
675                 Log.d(TAG, "view " + v + " focus " + hasFocus);
676 
677         // check key event focus
678         mPresentation.button1.setOnFocusChangeListener(listener);
679         mPresentation.button2.setOnFocusChangeListener(listener);
680         mPresentation.button3.setOnFocusChangeListener(listener);
681         final Window presentationWindow = mPresentation.getWindow();
682         presentationWindow.setLocalFocus(true, false);
683         PollingCheck.waitFor(() -> mPresentation.button1.hasWindowFocus());
684         checkPresentationButtonFocus(true, false, false);
685         assertFalse(mPresentation.button1.isInTouchMode());
686         injectKeyEvent(presentationWindow, KeyEvent.KEYCODE_TAB);
687         checkPresentationButtonFocus(false, true, false);
688         injectKeyEvent(presentationWindow, KeyEvent.KEYCODE_TAB);
689         checkPresentationButtonFocus(false, false, true);
690 
691         // check touch input injection
692         presentationWindow.setLocalFocus(true, true);
693         PollingCheck.waitFor(() -> mPresentation.button1.isInTouchMode());
694         View.OnClickListener clickListener = (View v) -> {
695             Log.d(TAG, "onClick " + v);
696             if (v == mPresentation.button1) {
697                 waitingSemaphore.release();
698             }
699         };
700         mPresentation.button1.setOnClickListener(clickListener);
701         mPresentation.button2.setOnClickListener(clickListener);
702         mPresentation.button3.setOnClickListener(clickListener);
703         injectTouchEvent(presentationWindow, mPresentation.button1.getX() +
704                 mPresentation.button1.getWidth() / 2,
705                 mPresentation.button1.getY() + mPresentation.button1.getHeight() / 2);
706         assertTrue(waitingSemaphore.tryAcquire(5, TimeUnit.SECONDS));
707 
708         destroyPresentation();
709     }
710 
checkPresentationButtonFocus(final boolean button1Focused, final boolean button2Focused, final boolean button3Focused)711     private void checkPresentationButtonFocus(final boolean button1Focused,
712             final boolean button2Focused, final boolean button3Focused) {
713         PollingCheck.waitFor(() -> (mPresentation.button1.isFocused() == button1Focused) &&
714                         (mPresentation.button2.isFocused() == button2Focused) &&
715                         (mPresentation.button3.isFocused() == button3Focused));
716     }
717 
injectKeyEvent(Window window, int keyCode)718     private void injectKeyEvent(Window window, int keyCode) {
719         KeyEvent downEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
720         window.injectInputEvent(downEvent);
721         KeyEvent upEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
722         window.injectInputEvent(upEvent);
723     }
724 
injectTouchEvent(Window window, float x, float y)725     private void injectTouchEvent(Window window, float x, float y) {
726         Log.d(TAG, "injectTouchEvent " + x + "," + y);
727         long downTime = SystemClock.uptimeMillis();
728         MotionEvent downEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN,
729                 x, y, 0);
730         downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
731         window.injectInputEvent(downEvent);
732         long upTime = SystemClock.uptimeMillis();
733         MotionEvent upEvent = MotionEvent.obtain(downTime, upTime, MotionEvent.ACTION_UP,
734                 x, y, 0);
735         upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
736         window.injectInputEvent(upEvent);
737     }
738 
createPresentation(final Surface surface, final int width, final int height)739     private void createPresentation(final Surface surface, final int width,
740             final int height) {
741         DisplayManager displayManager =
742                 (DisplayManager) mActivity.getSystemService(Context.DISPLAY_SERVICE);
743         mVirtualDisplay = displayManager.createVirtualDisplay("localFocusTest",
744                 width, height, 300, surface, 0);
745         mPresentation = new ProjectedPresentation(mActivity, mVirtualDisplay.getDisplay());
746         mPresentation.show();
747     }
748 
destroyPresentation()749     private void destroyPresentation() {
750         if (mPresentation != null) {
751             mPresentation.dismiss();
752         }
753         if (mVirtualDisplay != null) {
754             mVirtualDisplay.release();
755         }
756     }
757 
758     private class ProjectedPresentation extends Presentation {
759         public Button button1 = null;
760         public Button button2 = null;
761         public Button button3 = null;
762         public volatile boolean ready = false;
763 
ProjectedPresentation(Context outerContext, Display display)764         public ProjectedPresentation(Context outerContext, Display display) {
765             super(outerContext, display);
766             getWindow().setType(WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
767             getWindow().addFlags(WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE);
768         }
769 
770         @Override
onCreate(Bundle savedInstanceState)771         protected void onCreate(Bundle savedInstanceState) {
772             super.onCreate(savedInstanceState);
773             setContentView(R.layout.windowstub_presentation);
774             button1 = (Button) findViewById(R.id.presentation_button1);
775             button2 = (Button) findViewById(R.id.presentation_button2);
776             button3 = (Button) findViewById(R.id.presentation_button3);
777         }
778 
779         @Override
show()780         public void show() {
781             super.show();
782             new Handler().post(() -> ready = true);
783         }
784     }
785 
786     public class MockWindow extends Window {
787         public boolean mIsOnConfigurationChangedCalled = false;
788         public boolean mIsOnActiveCalled = false;
789 
MockWindow(Context context)790         public MockWindow(Context context) {
791             super(context);
792         }
793 
isFloating()794         public boolean isFloating() {
795             return false;
796         }
797 
setContentView(int layoutResID)798         public void setContentView(int layoutResID) {
799         }
800 
setContentView(View view)801         public void setContentView(View view) {
802         }
803 
setContentView(View view, ViewGroup.LayoutParams params)804         public void setContentView(View view, ViewGroup.LayoutParams params) {
805         }
806 
addContentView(View view, ViewGroup.LayoutParams params)807         public void addContentView(View view, ViewGroup.LayoutParams params) {
808         }
809 
clearContentView()810         public void clearContentView() {
811         }
812 
getCurrentFocus()813         public View getCurrentFocus() {
814             return null;
815         }
816 
getLayoutInflater()817         public LayoutInflater getLayoutInflater() {
818             return null;
819         }
820 
setTitle(CharSequence title)821         public void setTitle(CharSequence title) {
822         }
823 
setTitleColor(int textColor)824         public void setTitleColor(int textColor) {
825         }
826 
openPanel(int featureId, KeyEvent event)827         public void openPanel(int featureId, KeyEvent event) {
828         }
829 
closePanel(int featureId)830         public void closePanel(int featureId) {
831         }
832 
togglePanel(int featureId, KeyEvent event)833         public void togglePanel(int featureId, KeyEvent event) {
834         }
835 
invalidatePanelMenu(int featureId)836         public void invalidatePanelMenu(int featureId) {
837         }
838 
performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags)839         public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
840             return true;
841         }
842 
performPanelIdentifierAction(int featureId, int id, int flags)843         public boolean performPanelIdentifierAction(int featureId, int id, int flags) {
844             return true;
845         }
846 
closeAllPanels()847         public void closeAllPanels() {
848         }
849 
performContextMenuIdentifierAction(int id, int flags)850         public boolean performContextMenuIdentifierAction(int id, int flags) {
851             return true;
852         }
853 
onConfigurationChanged(Configuration newConfig)854         public void onConfigurationChanged(Configuration newConfig) {
855             mIsOnConfigurationChangedCalled = true;
856         }
857 
setBackgroundDrawable(Drawable drawable)858         public void setBackgroundDrawable(Drawable drawable) {
859         }
860 
setFeatureDrawableResource(int featureId, int resId)861         public void setFeatureDrawableResource(int featureId, int resId) {
862         }
863 
setFeatureDrawableUri(int featureId, Uri uri)864         public void setFeatureDrawableUri(int featureId, Uri uri) {
865         }
866 
setFeatureDrawable(int featureId, Drawable drawable)867         public void setFeatureDrawable(int featureId, Drawable drawable) {
868         }
869 
setFeatureDrawableAlpha(int featureId, int alpha)870         public void setFeatureDrawableAlpha(int featureId, int alpha) {
871         }
872 
setFeatureInt(int featureId, int value)873         public void setFeatureInt(int featureId, int value) {
874         }
875 
takeKeyEvents(boolean get)876         public void takeKeyEvents(boolean get) {
877         }
878 
superDispatchKeyEvent(KeyEvent event)879         public boolean superDispatchKeyEvent(KeyEvent event) {
880             return true;
881         }
882 
superDispatchKeyShortcutEvent(KeyEvent event)883         public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
884             return false;
885         }
886 
superDispatchTouchEvent(MotionEvent event)887         public boolean superDispatchTouchEvent(MotionEvent event) {
888             return true;
889         }
890 
superDispatchTrackballEvent(MotionEvent event)891         public boolean superDispatchTrackballEvent(MotionEvent event) {
892             return true;
893         }
894 
superDispatchGenericMotionEvent(MotionEvent event)895         public boolean superDispatchGenericMotionEvent(MotionEvent event) {
896             return true;
897         }
898 
getDecorView()899         public View getDecorView() {
900             return null;
901         }
902 
alwaysReadCloseOnTouchAttr()903         public void alwaysReadCloseOnTouchAttr() {
904         }
905 
peekDecorView()906         public View peekDecorView() {
907             return null;
908         }
909 
saveHierarchyState()910         public Bundle saveHierarchyState() {
911             return null;
912         }
913 
restoreHierarchyState(Bundle savedInstanceState)914         public void restoreHierarchyState(Bundle savedInstanceState) {
915         }
916 
onActive()917         protected void onActive() {
918             mIsOnActiveCalled = true;
919         }
920 
setChildDrawable(int featureId, Drawable drawable)921         public void setChildDrawable(int featureId, Drawable drawable) {
922 
923         }
924 
setChildInt(int featureId, int value)925         public void setChildInt(int featureId, int value) {
926         }
927 
isShortcutKey(int keyCode, KeyEvent event)928         public boolean isShortcutKey(int keyCode, KeyEvent event) {
929             return false;
930         }
931 
setVolumeControlStream(int streamType)932         public void setVolumeControlStream(int streamType) {
933         }
934 
getVolumeControlStream()935         public int getVolumeControlStream() {
936             return 0;
937         }
938 
setDefaultWindowFormatFake(int format)939         public void setDefaultWindowFormatFake(int format) {
940             super.setDefaultWindowFormat(format);
941         }
942 
943         @Override
setDefaultWindowFormat(int format)944         public void setDefaultWindowFormat(int format) {
945             super.setDefaultWindowFormat(format);
946         }
947 
948         @Override
takeSurface(SurfaceHolder.Callback2 callback)949         public void takeSurface(SurfaceHolder.Callback2 callback) {
950         }
951 
952         @Override
takeInputQueue(InputQueue.Callback callback)953         public void takeInputQueue(InputQueue.Callback callback) {
954         }
955 
956         @Override
setStatusBarColor(int color)957         public void setStatusBarColor(int color) {
958         }
959 
960         @Override
getStatusBarColor()961         public int getStatusBarColor() {
962             return 0;
963         }
964 
965         @Override
setNavigationBarColor(int color)966         public void setNavigationBarColor(int color) {
967         }
968 
969         @Override
setDecorCaptionShade(int decorCaptionShade)970         public void setDecorCaptionShade(int decorCaptionShade) {
971 
972         }
973 
974         @Override
setResizingCaptionDrawable(Drawable drawable)975         public void setResizingCaptionDrawable(Drawable drawable) {
976 
977         }
978 
979         @Override
getNavigationBarColor()980         public int getNavigationBarColor() {
981             return 0;
982         }
983 
984         @Override
onMultiWindowModeChanged()985         public void onMultiWindowModeChanged() {
986         }
987 
988         @Override
onPictureInPictureModeChanged(boolean isInPictureInPictureMode)989         public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
990         }
991 
992         @Override
reportActivityRelaunched()993         public void reportActivityRelaunched() {
994         }
995     }
996 }
997