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