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.view.cts; 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.support.test.InstrumentationRegistry; 51 import android.support.test.annotation.UiThreadTest; 52 import android.support.test.filters.MediumTest; 53 import android.support.test.rule.ActivityTestRule; 54 import android.support.test.runner.AndroidJUnit4; 55 import android.util.DisplayMetrics; 56 import android.util.Log; 57 import android.view.ContextThemeWrapper; 58 import android.view.Display; 59 import android.view.Gravity; 60 import android.view.InputDevice; 61 import android.view.InputQueue; 62 import android.view.KeyEvent; 63 import android.view.LayoutInflater; 64 import android.view.MotionEvent; 65 import android.view.Surface; 66 import android.view.SurfaceHolder; 67 import android.view.SurfaceView; 68 import android.view.View; 69 import android.view.ViewGroup; 70 import android.view.Window; 71 import android.view.WindowManager; 72 import android.widget.Button; 73 import android.widget.TextView; 74 75 import com.android.compatibility.common.util.PollingCheck; 76 77 import org.junit.After; 78 import org.junit.Before; 79 import org.junit.Rule; 80 import org.junit.Test; 81 import org.junit.runner.RunWith; 82 83 import java.util.concurrent.Semaphore; 84 import java.util.concurrent.TimeUnit; 85 86 @MediumTest 87 @RunWith(AndroidJUnit4.class) 88 public class WindowTest { 89 private static final String TAG = "WindowTest"; 90 private static final int VIEWGROUP_LAYOUT_HEIGHT = 100; 91 private static final int VIEWGROUP_LAYOUT_WIDTH = 200; 92 93 private Instrumentation mInstrumentation; 94 private WindowCtsActivity mActivity; 95 private Window mWindow; 96 private Window.Callback mWindowCallback; 97 private SurfaceView mSurfaceView; 98 99 // for testing setLocalFocus 100 private ProjectedPresentation mPresentation; 101 private VirtualDisplay mVirtualDisplay; 102 103 @Rule 104 public ActivityTestRule<WindowCtsActivity> mActivityRule = 105 new ActivityTestRule<>(WindowCtsActivity.class); 106 107 @Before setup()108 public void setup() { 109 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 110 mActivity = mActivityRule.getActivity(); 111 mWindow = mActivity.getWindow(); 112 113 114 mWindowCallback = mock(Window.Callback.class); 115 doReturn(true).when(mWindowCallback).dispatchKeyEvent(any()); 116 doReturn(true).when(mWindowCallback).dispatchTouchEvent(any()); 117 doReturn(true).when(mWindowCallback).dispatchTrackballEvent(any()); 118 doReturn(true).when(mWindowCallback).dispatchGenericMotionEvent(any()); 119 doReturn(true).when(mWindowCallback).dispatchPopulateAccessibilityEvent(any()); 120 doReturn(true).when(mWindowCallback).onMenuItemSelected(anyInt(), any()); 121 } 122 123 @After teardown()124 public void teardown() { 125 if (mActivity != null) { 126 mActivity.setFlagFalse(); 127 } 128 } 129 130 @UiThreadTest 131 @Test testConstructor()132 public void testConstructor() { 133 mWindow = new MockWindow(mActivity); 134 assertSame(mActivity, mWindow.getContext()); 135 } 136 137 /** 138 * Test flags related methods: 139 * 1. addFlags: add the given flag to WindowManager.LayoutParams.flags, if add more than one 140 * in sequence, flags will be set to formerFlag | latterFlag. 141 * 2. setFlags: _1. set the flags of the window. 142 * _2. test invocation of Window.Callback#onWindowAttributesChanged. 143 * 3. clearFlags: clear the flag bits as specified in flags. 144 */ 145 @Test testOpFlags()146 public void testOpFlags() { 147 mWindow = new MockWindow(mActivity); 148 final WindowManager.LayoutParams attrs = mWindow.getAttributes(); 149 assertEquals(0, attrs.flags); 150 151 mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 152 assertEquals(WindowManager.LayoutParams.FLAG_FULLSCREEN, attrs.flags); 153 154 mWindow.addFlags(WindowManager.LayoutParams.FLAG_DITHER); 155 assertEquals(WindowManager.LayoutParams.FLAG_FULLSCREEN 156 | WindowManager.LayoutParams.FLAG_DITHER, attrs.flags); 157 158 mWindow.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 159 assertEquals(WindowManager.LayoutParams.FLAG_DITHER, attrs.flags); 160 mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DITHER); 161 assertEquals(0, attrs.flags); 162 163 mWindow.setCallback(mWindowCallback); 164 verify(mWindowCallback, never()).onWindowAttributesChanged(any()); 165 // mask == flag, no bit of flag need to be modified. 166 mWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 167 WindowManager.LayoutParams.FLAG_FULLSCREEN); 168 assertEquals(WindowManager.LayoutParams.FLAG_FULLSCREEN, attrs.flags); 169 170 // Test if the callback method is called by system 171 verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs); 172 mWindow.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 173 mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DITHER); 174 } 175 176 @Test testFindViewById()177 public void testFindViewById() { 178 TextView v = mWindow.findViewById(R.id.listview_window); 179 assertNotNull(v); 180 assertEquals(R.id.listview_window, v.getId()); 181 } 182 183 @Test testRequireViewById()184 public void testRequireViewById() { 185 TextView v = mWindow.requireViewById(R.id.listview_window); 186 assertNotNull(v); 187 assertEquals(R.id.listview_window, v.getId()); 188 } 189 190 @Test(expected = IllegalArgumentException.class) testRequireViewByIdNoId()191 public void testRequireViewByIdNoId() { 192 TextView v = mWindow.requireViewById(View.NO_ID); 193 } 194 195 @Test(expected = IllegalArgumentException.class) testRequireViewByIdInvalid()196 public void testRequireViewByIdInvalid() { 197 TextView v = mWindow.requireViewById(R.id.view); // not present in layout 198 } 199 200 /** 201 * getAttributes: Retrieve the current window attributes associated with this panel. 202 * Return is 1.the existing window attributes object. 203 * 2.a freshly created one if there is none. 204 * setAttributes: Specify custom window attributes. 205 * Here we just set some parameters to test if it can set, and the window is just 206 * available in this method. But it's not proper to setAttributes arbitrarily. 207 * setCallback: Set the Callback interface for this window. In Window.java, 208 * there is just one method, onWindowAttributesChanged, used. 209 * getCallback: Return the current Callback interface for this window. 210 */ 211 @Test testAccessAttributes()212 public void testAccessAttributes() { 213 mWindow = new MockWindow(mActivity); 214 215 // default attributes 216 WindowManager.LayoutParams attr = mWindow.getAttributes(); 217 assertEquals(WindowManager.LayoutParams.MATCH_PARENT, attr.width); 218 assertEquals(WindowManager.LayoutParams.MATCH_PARENT, attr.height); 219 assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION, attr.type); 220 assertEquals(PixelFormat.OPAQUE, attr.format); 221 222 int width = 200; 223 int height = 300; 224 WindowManager.LayoutParams param = new WindowManager.LayoutParams(width, height, 225 WindowManager.LayoutParams.TYPE_BASE_APPLICATION, 226 WindowManager.LayoutParams.FLAG_DITHER, PixelFormat.RGBA_8888); 227 mWindow.setCallback(mWindowCallback); 228 assertSame(mWindowCallback, mWindow.getCallback()); 229 verify(mWindowCallback, never()).onWindowAttributesChanged(any()); 230 mWindow.setAttributes(param); 231 attr = mWindow.getAttributes(); 232 assertEquals(width, attr.width); 233 assertEquals(height, attr.height); 234 assertEquals(WindowManager.LayoutParams.TYPE_BASE_APPLICATION, attr.type); 235 assertEquals(PixelFormat.RGBA_8888, attr.format); 236 assertEquals(WindowManager.LayoutParams.FLAG_DITHER, attr.flags); 237 verify(mWindowCallback, times(1)).onWindowAttributesChanged(attr); 238 } 239 240 /** 241 * If not set container, the DecorWindow operates as a top-level window, the mHasChildren of 242 * container is false; 243 * Otherwise, it will display itself meanwhile container's mHasChildren is true. 244 */ 245 @Test testAccessContainer()246 public void testAccessContainer() { 247 mWindow = new MockWindow(mActivity); 248 assertNull(mWindow.getContainer()); 249 assertFalse(mWindow.hasChildren()); 250 251 MockWindow container = new MockWindow(mActivity); 252 mWindow.setContainer(container); 253 assertSame(container, mWindow.getContainer()); 254 assertTrue(container.hasChildren()); 255 } 256 257 /** 258 * addContentView: add an additional content view to the screen. 259 * 1.Added after any existing ones in the screen. 260 * 2.Existing views are NOT removed. 261 * getLayoutInflater: Quick access to the {@link LayoutInflater} instance that this Window 262 * retrieved from its Context. 263 */ 264 @UiThreadTest 265 @Test testAddContentView()266 public void testAddContentView() { 267 final ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(VIEWGROUP_LAYOUT_WIDTH, 268 VIEWGROUP_LAYOUT_HEIGHT); 269 // The LayoutInflater instance will be inflated to a view and used by 270 // addContentView, 271 // id of this view should be same with inflated id. 272 final LayoutInflater inflater = mActivity.getLayoutInflater(); 273 TextView addedView = (TextView) mWindow.findViewById(R.id.listview_addwindow); 274 assertNull(addedView); 275 mWindow.addContentView(inflater.inflate(R.layout.windowstub_addlayout, null), lp); 276 TextView view = (TextView) mWindow.findViewById(R.id.listview_window); 277 addedView = (TextView) mWindow.findViewById(R.id.listview_addwindow); 278 assertNotNull(view); 279 assertNotNull(addedView); 280 assertEquals(R.id.listview_window, view.getId()); 281 assertEquals(R.id.listview_addwindow, addedView.getId()); 282 } 283 284 /** 285 * getCurrentFocus: Return the view in this Window that currently has focus, or null if 286 * there are none. 287 * The test will be: 288 * 1. Set focus view to null, get current focus, it should be null 289 * 2. Set listview_window as focus view, get it and compare. 290 */ 291 @UiThreadTest 292 @Test testGetCurrentFocus()293 public void testGetCurrentFocus() { 294 TextView v = (TextView) mWindow.findViewById(R.id.listview_window); 295 v.clearFocus(); 296 assertNull(mWindow.getCurrentFocus()); 297 298 v.setFocusable(true); 299 assertTrue(v.isFocusable()); 300 assertTrue(v.requestFocus()); 301 View focus = mWindow.getCurrentFocus(); 302 assertNotNull(focus); 303 assertEquals(R.id.listview_window, focus.getId()); 304 } 305 306 /** 307 * 1. getDecorView() retrieves the top-level window decor view, which contains the standard 308 * window frame/decorations and the client's content inside of that, we should check the 309 * primary components of this view which have no relationship concrete realization of Window. 310 * Therefore we should check if the size of this view equals to the screen size and the 311 * ontext is same as Window's context. 312 * 2. Return null if decor view is not created, else the same with detDecorView. 313 */ 314 @Test testDecorView()315 public void testDecorView() { 316 mInstrumentation.waitForIdleSync(); 317 View decor = mWindow.getDecorView(); 318 assertNotNull(decor); 319 verifyDecorView(decor); 320 321 decor = mWindow.peekDecorView(); 322 if (decor != null) { 323 verifyDecorView(decor); 324 } 325 } 326 verifyDecorView(View decor)327 private void verifyDecorView(View decor) { 328 DisplayMetrics dm = new DisplayMetrics(); 329 mActivity.getWindowManager().getDefaultDisplay().getMetrics(dm); 330 int screenWidth = dm.widthPixels; 331 int screenHeight = dm.heightPixels; 332 assertTrue(decor.getWidth() >= screenWidth); 333 assertTrue(decor.getHeight() >= screenHeight); 334 assertTrue(decor.getContext() instanceof ContextThemeWrapper); 335 } 336 337 /** 338 * setVolumeControlStream: Suggests an audio stream whose volume should be changed by 339 * the hardware volume controls. 340 * getVolumeControlStream: Gets the suggested audio stream whose volume should be changed by 341 * the harwdare volume controls. 342 */ 343 @Test testAccessVolumeControlStream()344 public void testAccessVolumeControlStream() { 345 // Default value is AudioManager.USE_DEFAULT_STREAM_TYPE, see javadoc of 346 // {@link Activity#setVolumeControlStream}. 347 assertEquals(AudioManager.USE_DEFAULT_STREAM_TYPE, mWindow.getVolumeControlStream()); 348 mWindow.setVolumeControlStream(AudioManager.STREAM_MUSIC); 349 assertEquals(AudioManager.STREAM_MUSIC, mWindow.getVolumeControlStream()); 350 } 351 352 /** 353 * setWindowManager: Set the window manager for use by this Window. 354 * getWindowManager: Return the window manager allowing this Window to display its own 355 * windows. 356 */ 357 @Test testAccessWindowManager()358 public void testAccessWindowManager() { 359 mWindow = new MockWindow(mActivity); 360 WindowManager expected = (WindowManager) mActivity.getSystemService( 361 Context.WINDOW_SERVICE); 362 assertNull(mWindow.getWindowManager()); 363 mWindow.setWindowManager(expected, null, 364 mActivity.getApplicationInfo().loadLabel(mActivity.getPackageManager()).toString()); 365 // No way to compare the expected and actual directly, they are 366 // different object 367 assertNotNull(mWindow.getWindowManager()); 368 } 369 370 /** 371 * Return the {@link android.R.styleable#Window} attributes from this 372 * window's theme. It's invisible. 373 */ 374 @Test testGetWindowStyle()375 public void testGetWindowStyle() { 376 mWindow = new MockWindow(mActivity); 377 final TypedArray windowStyle = mWindow.getWindowStyle(); 378 // the windowStyle is obtained from 379 // com.android.internal.R.styleable.Window whose details 380 // are invisible for user. 381 assertNotNull(windowStyle); 382 } 383 384 @Test testIsActive()385 public void testIsActive() { 386 MockWindow window = new MockWindow(mActivity); 387 assertFalse(window.isActive()); 388 389 window.makeActive(); 390 assertTrue(window.isActive()); 391 assertTrue(window.mIsOnActiveCalled); 392 } 393 394 /** 395 * isFloating: Return whether this window is being displayed with a floating style 396 * (based on the {@link android.R.attr#windowIsFloating} attribute in the style/theme). 397 */ 398 @Test testIsFloating()399 public void testIsFloating() { 400 // Default system theme defined by themes.xml, the windowIsFloating is set false. 401 assertFalse(mWindow.isFloating()); 402 } 403 404 /** 405 * Change the background of this window to a custom Drawable. 406 * Setting the background to null will make the window be opaque(No way to get the window 407 * attribute of PixelFormat to check if the window is opaque). To make the window 408 * transparent, you can use an empty drawable(eg. ColorDrawable with the color 0). 409 */ 410 @Test testSetBackgroundDrawable()411 public void testSetBackgroundDrawable() throws Throwable { 412 // DecorView holds the background 413 View decor = mWindow.getDecorView(); 414 if (!mWindow.hasFeature(Window.FEATURE_SWIPE_TO_DISMISS)) { 415 assertEquals(PixelFormat.OPAQUE, decor.getBackground().getOpacity()); 416 } 417 // setBackgroundDrawableResource(int resId) has the same 418 // functionality with setBackgroundDrawable(Drawable drawable), just different in 419 // parameter. 420 mActivityRule.runOnUiThread(() -> mWindow.setBackgroundDrawableResource(R.drawable.faces)); 421 mInstrumentation.waitForIdleSync(); 422 423 mActivityRule.runOnUiThread(() -> { 424 ColorDrawable drawable = new ColorDrawable(0); 425 mWindow.setBackgroundDrawable(drawable); 426 }); 427 mInstrumentation.waitForIdleSync(); 428 decor = mWindow.getDecorView(); 429 // Color 0 with one alpha bit 430 assertEquals(PixelFormat.TRANSPARENT, decor.getBackground().getOpacity()); 431 432 mActivityRule.runOnUiThread(() -> mWindow.setBackgroundDrawable(null)); 433 mInstrumentation.waitForIdleSync(); 434 decor = mWindow.getDecorView(); 435 assertNull(decor.getBackground()); 436 } 437 438 /** 439 * setContentView(int): set the screen content from a layout resource. 440 * setContentView(View): set the screen content to an explicit view. 441 * setContentView(View, LayoutParams): Set the screen content to an explicit view. 442 * 443 * Note that calling this function "locks in" various characteristics 444 * of the window that can not, from this point forward, be changed: the 445 * features that have been requested with {@link #requestFeature(int)}, 446 * and certain window flags as described in {@link #setFlags(int, int)}. 447 * This functionality point is hard to test: 448 * 1. can't get the features requested because the getter is protected final. 449 * 2. certain window flags are not clear to concrete one. 450 */ 451 @Test testSetContentView()452 public void testSetContentView() throws Throwable { 453 final ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(VIEWGROUP_LAYOUT_WIDTH, 454 VIEWGROUP_LAYOUT_HEIGHT); 455 final LayoutInflater inflate = mActivity.getLayoutInflater(); 456 457 mActivityRule.runOnUiThread(() -> { 458 TextView view; 459 View setView; 460 // Test setContentView(int layoutResID) 461 mWindow.setContentView(R.layout.windowstub_layout); 462 view = (TextView) mWindow.findViewById(R.id.listview_window); 463 assertNotNull(view); 464 assertEquals(R.id.listview_window, view.getId()); 465 466 // Test setContentView(View view) 467 setView = inflate.inflate(R.layout.windowstub_addlayout, null); 468 mWindow.setContentView(setView); 469 view = (TextView) mWindow.findViewById(R.id.listview_addwindow); 470 assertNotNull(view); 471 assertEquals(R.id.listview_addwindow, view.getId()); 472 473 // Test setContentView(View view, ViewGroup.LayoutParams params) 474 setView = inflate.inflate(R.layout.windowstub_layout, null); 475 mWindow.setContentView(setView, lp); 476 assertEquals(VIEWGROUP_LAYOUT_WIDTH, setView.getLayoutParams().width); 477 assertEquals(VIEWGROUP_LAYOUT_HEIGHT, setView.getLayoutParams().height); 478 view = (TextView) mWindow.findViewById(R.id.listview_window); 479 assertNotNull(view); 480 assertEquals(R.id.listview_window, view.getId()); 481 }); 482 mInstrumentation.waitForIdleSync(); 483 } 484 485 @Test testSetTitle()486 public void testSetTitle() throws Throwable { 487 final String title = "Android Window Test"; 488 mActivityRule.runOnUiThread(() -> { 489 mWindow.setTitle(title); 490 mWindow.setTitleColor(Color.BLUE); 491 }); 492 mInstrumentation.waitForIdleSync(); 493 // No way to get title and title color 494 } 495 496 /** 497 * takeKeyEvents: Request that key events come to this activity. Use this if your activity 498 * has no views with focus, but the activity still wants a chance to process key events. 499 */ 500 @Test testTakeKeyEvents()501 public void testTakeKeyEvents() throws Throwable { 502 mActivityRule.runOnUiThread(() -> { 503 View v = mWindow.findViewById(R.id.listview_window); 504 v.clearFocus(); 505 assertNull(mWindow.getCurrentFocus()); 506 mWindow.takeKeyEvents(false); 507 }); 508 mInstrumentation.waitForIdleSync(); 509 } 510 511 /** 512 * setDefaultWindowFormat: Set the format of window, as per the PixelFormat types. This 513 * is the format that will be used unless the client specifies in explicit format with 514 * setFormat(). 515 * setFormat: Set the format of window, as per the PixelFormat types. 516 * param format: The new window format (see PixelFormat). Use 517 * PixelFormat.UNKNOWN to allow the Window to select 518 * the format. 519 */ 520 @Test testSetDefaultWindowFormat()521 public void testSetDefaultWindowFormat() { 522 MockWindow window = new MockWindow(mActivity); 523 524 // mHaveWindowFormat will be true after set PixelFormat.OPAQUE and 525 // setDefaultWindowFormat is invalid 526 window.setFormat(PixelFormat.OPAQUE); 527 window.setCallback(mWindowCallback); 528 verify(mWindowCallback, never()).onWindowAttributesChanged(any()); 529 window.setDefaultWindowFormat(PixelFormat.JPEG); 530 assertEquals(PixelFormat.OPAQUE, window.getAttributes().format); 531 verify(mWindowCallback, never()).onWindowAttributesChanged(any()); 532 533 // mHaveWindowFormat will be false after set PixelFormat.UNKNOWN and 534 // setDefaultWindowFormat is valid 535 window.setFormat(PixelFormat.UNKNOWN); 536 reset(mWindowCallback); 537 window.setDefaultWindowFormat(PixelFormat.JPEG); 538 assertEquals(PixelFormat.JPEG, window.getAttributes().format); 539 verify(mWindowCallback, times(1)).onWindowAttributesChanged(window.getAttributes()); 540 } 541 542 /** 543 * Set the gravity of the window 544 */ 545 @Test testSetGravity()546 public void testSetGravity() { 547 mWindow = new MockWindow(mActivity); 548 WindowManager.LayoutParams attrs = mWindow.getAttributes(); 549 assertEquals(0, attrs.gravity); 550 551 mWindow.setCallback(mWindowCallback); 552 verify(mWindowCallback, never()).onWindowAttributesChanged(any()); 553 mWindow.setGravity(Gravity.TOP); 554 attrs = mWindow.getAttributes(); 555 assertEquals(Gravity.TOP, attrs.gravity); 556 verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs); 557 } 558 559 /** 560 * Set the width and height layout parameters of the window. 561 * 1.The default for both of these is MATCH_PARENT; 562 * 2.You can change them to WRAP_CONTENT to make a window that is not full-screen. 563 */ 564 @Test testSetLayout()565 public void testSetLayout() { 566 mWindow = new MockWindow(mActivity); 567 WindowManager.LayoutParams attrs = mWindow.getAttributes(); 568 assertEquals(WindowManager.LayoutParams.MATCH_PARENT, attrs.width); 569 assertEquals(WindowManager.LayoutParams.MATCH_PARENT, attrs.height); 570 571 mWindow.setCallback(mWindowCallback); 572 verify(mWindowCallback, never()).onWindowAttributesChanged(any()); 573 mWindow.setLayout(WindowManager.LayoutParams.WRAP_CONTENT, 574 WindowManager.LayoutParams.WRAP_CONTENT); 575 attrs = mWindow.getAttributes(); 576 assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, attrs.width); 577 assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, attrs.height); 578 verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs); 579 } 580 581 /** 582 * Set the type of the window, as per the WindowManager.LayoutParams types. 583 */ 584 @Test testSetType()585 public void testSetType() { 586 mWindow = new MockWindow(mActivity); 587 WindowManager.LayoutParams attrs = mWindow.getAttributes(); 588 assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION, attrs.type); 589 590 mWindow.setCallback(mWindowCallback); 591 verify(mWindowCallback, never()).onWindowAttributesChanged(any()); 592 mWindow.setType(WindowManager.LayoutParams.TYPE_BASE_APPLICATION); 593 attrs = mWindow.getAttributes(); 594 assertEquals(WindowManager.LayoutParams.TYPE_BASE_APPLICATION, attrs.type); 595 verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs); 596 } 597 598 /** 599 * Specify an explicit soft input mode to use for the window, as per 600 * WindowManager.LayoutParams#softInputMode. 601 * 1.Providing "unspecified" here will NOT override the input mode the window. 602 * 2.Providing "unspecified" here will override the input mode the window. 603 */ 604 @Test testSetSoftInputMode()605 public void testSetSoftInputMode() { 606 mWindow = new MockWindow(mActivity); 607 assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED, 608 mWindow.getAttributes().softInputMode); 609 mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); 610 assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE, 611 mWindow.getAttributes().softInputMode); 612 mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); 613 assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE, 614 mWindow.getAttributes().softInputMode); 615 } 616 617 /** 618 * Specify custom animations to use for the window, as per 619 * WindowManager.LayoutParams#windowAnimations. 620 * 1.Providing 0 here will NOT override the animations the window(But here we can't check 621 * it because the getter is in WindowManagerService and is private) 622 * 2.Providing 0 here will override the animations the window. 623 */ 624 @Test testSetWindowAnimations()625 public void testSetWindowAnimations() { 626 mWindow = new MockWindow(mActivity); 627 628 mWindow.setCallback(mWindowCallback); 629 verify(mWindowCallback, never()).onWindowAttributesChanged(any()); 630 mWindow.setWindowAnimations(R.anim.alpha); 631 WindowManager.LayoutParams attrs = mWindow.getAttributes(); 632 assertEquals(R.anim.alpha, attrs.windowAnimations); 633 verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs); 634 } 635 636 /** 637 * Test setLocalFocus together with injectInputEvent. 638 */ 639 @Test testSetLocalFocus()640 public void testSetLocalFocus() throws Throwable { 641 mActivityRule.runOnUiThread(() -> mSurfaceView = new SurfaceView(mActivity)); 642 mInstrumentation.waitForIdleSync(); 643 644 final Semaphore waitingSemaphore = new Semaphore(0); 645 mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { 646 @Override 647 public void surfaceCreated(SurfaceHolder holder) { 648 } 649 650 @Override 651 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 652 destroyPresentation(); 653 createPresentation(holder.getSurface(), width, height); 654 waitingSemaphore.release(); 655 } 656 657 @Override 658 public void surfaceDestroyed(SurfaceHolder holder) { 659 destroyPresentation(); 660 } 661 }); 662 mActivityRule.runOnUiThread(() -> mWindow.setContentView(mSurfaceView)); 663 mInstrumentation.waitForIdleSync(); 664 assertTrue(waitingSemaphore.tryAcquire(5, TimeUnit.SECONDS)); 665 assertNotNull(mVirtualDisplay); 666 assertNotNull(mPresentation); 667 668 PollingCheck.waitFor(() -> (mPresentation.button1 != null) 669 && (mPresentation.button2 != null) && (mPresentation.button3 != null) 670 && mPresentation.ready); 671 assertTrue(mPresentation.button1.isFocusable() && mPresentation.button2.isFocusable() && 672 mPresentation.button3.isFocusable()); 673 // currently it is only for debugging 674 View.OnFocusChangeListener listener = (View v, boolean hasFocus) -> 675 Log.d(TAG, "view " + v + " focus " + hasFocus); 676 677 // check key event focus 678 mPresentation.button1.setOnFocusChangeListener(listener); 679 mPresentation.button2.setOnFocusChangeListener(listener); 680 mPresentation.button3.setOnFocusChangeListener(listener); 681 final Window presentationWindow = mPresentation.getWindow(); 682 presentationWindow.setLocalFocus(true, false); 683 PollingCheck.waitFor(() -> mPresentation.button1.hasWindowFocus()); 684 checkPresentationButtonFocus(true, false, false); 685 assertFalse(mPresentation.button1.isInTouchMode()); 686 injectKeyEvent(presentationWindow, KeyEvent.KEYCODE_TAB); 687 checkPresentationButtonFocus(false, true, false); 688 injectKeyEvent(presentationWindow, KeyEvent.KEYCODE_TAB); 689 checkPresentationButtonFocus(false, false, true); 690 691 // check touch input injection 692 presentationWindow.setLocalFocus(true, true); 693 PollingCheck.waitFor(() -> mPresentation.button1.isInTouchMode()); 694 View.OnClickListener clickListener = (View v) -> { 695 Log.d(TAG, "onClick " + v); 696 if (v == mPresentation.button1) { 697 waitingSemaphore.release(); 698 } 699 }; 700 mPresentation.button1.setOnClickListener(clickListener); 701 mPresentation.button2.setOnClickListener(clickListener); 702 mPresentation.button3.setOnClickListener(clickListener); 703 injectTouchEvent(presentationWindow, mPresentation.button1.getX() + 704 mPresentation.button1.getWidth() / 2, 705 mPresentation.button1.getY() + mPresentation.button1.getHeight() / 2); 706 assertTrue(waitingSemaphore.tryAcquire(5, TimeUnit.SECONDS)); 707 708 destroyPresentation(); 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 @Override onMultiWindowModeChanged()985 public void onMultiWindowModeChanged() { 986 } 987 988 @Override onPictureInPictureModeChanged(boolean isInPictureInPictureMode)989 public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { 990 } 991 992 @Override reportActivityRelaunched()993 public void reportActivityRelaunched() { 994 } 995 } 996 } 997