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