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