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