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