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