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 package android.app.cts; 17 18 import static org.junit.Assert.assertEquals; 19 import static org.junit.Assert.assertFalse; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.assertSame; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 import static org.junit.Assume.assumeFalse; 26 27 import android.app.Dialog; 28 import android.app.Instrumentation; 29 import android.app.stubs.DialogStubActivity; 30 import android.app.stubs.R; 31 import android.app.stubs.TestDialog; 32 import android.content.Context; 33 import android.content.DialogInterface; 34 import android.content.DialogInterface.OnCancelListener; 35 import android.content.DialogInterface.OnDismissListener; 36 import android.content.DialogInterface.OnKeyListener; 37 import android.content.pm.PackageManager; 38 import android.content.res.Resources; 39 import android.content.res.TypedArray; 40 import android.graphics.Canvas; 41 import android.graphics.ColorFilter; 42 import android.graphics.drawable.Drawable; 43 import android.net.Uri; 44 import android.os.Handler; 45 import android.os.HandlerThread; 46 import android.os.Looper; 47 import android.os.Message; 48 import android.os.SystemClock; 49 import android.view.InputDevice; 50 import android.view.KeyEvent; 51 import android.view.LayoutInflater; 52 import android.view.MotionEvent; 53 import android.view.View; 54 import android.view.ViewConfiguration; 55 import android.view.ViewGroup; 56 import android.view.Window; 57 import android.view.WindowManager; 58 import android.widget.LinearLayout; 59 60 import androidx.test.annotation.UiThreadTest; 61 import androidx.test.core.app.ActivityScenario; 62 import androidx.test.ext.junit.runners.AndroidJUnit4; 63 import androidx.test.platform.app.InstrumentationRegistry; 64 65 import com.android.compatibility.common.util.PollingCheck; 66 import com.android.compatibility.common.util.UserHelper; 67 import com.android.compatibility.common.util.WindowUtil; 68 69 import org.junit.After; 70 import org.junit.Before; 71 import org.junit.Test; 72 import org.junit.runner.RunWith; 73 74 import java.lang.ref.WeakReference; 75 76 @RunWith(AndroidJUnit4.class) 77 public class DialogTest { 78 79 /** 80 * please refer to Dialog 81 */ 82 private static final int DISMISS = 0x43; 83 private static final int CANCEL = 0x44; 84 85 private boolean mCalledCallback; 86 private boolean mIsKey0Listened; 87 private boolean mIsKey1Listened; 88 private boolean mOnCancelListenerCalled; 89 90 private Instrumentation mInstrumentation; 91 private Context mContext; 92 private ActivityScenario<DialogStubActivity> mScenario; 93 private DialogStubActivity mActivity; 94 95 private final UserHelper mUserHelper = new UserHelper(); 96 97 @Before setup()98 public void setup() throws Throwable { 99 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 100 mContext = mInstrumentation.getContext(); 101 CtsAppTestUtils.turnScreenOn(mInstrumentation, mContext); 102 mInstrumentation.waitForIdleSync(); 103 } 104 105 @After tearDown()106 public void tearDown() { 107 if (mScenario != null) { 108 mScenario.close(); 109 mScenario = null; 110 } 111 } 112 startDialogActivity(int dialogNumber)113 private void startDialogActivity(int dialogNumber) { 114 mScenario = DialogStubActivity.startDialogActivity( 115 mInstrumentation.getTargetContext(), dialogNumber); 116 mScenario.onActivity(activity -> { 117 mActivity = activity; 118 }); 119 WindowUtil.waitForFocus(mActivity.getDialog().getWindow()); 120 } 121 122 @UiThreadTest 123 @Test testConstructor()124 public void testConstructor() { 125 new Dialog(mContext); 126 Dialog d = new Dialog(mContext, 0); 127 // According to javadoc of constructors, it will set theme to system default theme, 128 // when we set no theme id or set it theme id to 0. 129 // But CTS can no assert dialog theme equals system internal theme. 130 131 d = new Dialog(mContext, R.style.TextAppearance); 132 TypedArray ta = 133 d.getContext().getTheme().obtainStyledAttributes(R.styleable.TextAppearance); 134 assertTextAppearanceStyle(ta); 135 136 final Window w = d.getWindow(); 137 ta = w.getContext().getTheme().obtainStyledAttributes(R.styleable.TextAppearance); 138 assertTextAppearanceStyle(ta); 139 } 140 141 @Test testConstructor_protectedCancellable()142 public void testConstructor_protectedCancellable() { 143 startDialogActivity(DialogStubActivity.TEST_PROTECTED_CANCELABLE); 144 mActivity.onCancelListenerCalled = false; 145 sendKeys(KeyEvent.KEYCODE_BACK); 146 PollingCheck.waitFor(() -> mActivity.onCancelListenerCalled); 147 } 148 149 @Test testConstructor_protectedNotCancellable()150 public void testConstructor_protectedNotCancellable() { 151 startDialogActivity(DialogStubActivity.TEST_PROTECTED_NOT_CANCELABLE); 152 mActivity.onCancelListenerCalled = false; 153 sendKeys(KeyEvent.KEYCODE_BACK); 154 assertFalse(mActivity.onCancelListenerCalled); 155 } 156 157 @Test testConstructor_protectedCancellableEsc()158 public void testConstructor_protectedCancellableEsc() { 159 startDialogActivity(DialogStubActivity.TEST_PROTECTED_CANCELABLE); 160 mActivity.onCancelListenerCalled = false; 161 sendKeys(KeyEvent.KEYCODE_ESCAPE); 162 PollingCheck.waitFor(() -> { 163 return mActivity.onCancelListenerCalled; }); 164 } 165 166 @Test testConstructor_protectedNotCancellableEsc()167 public void testConstructor_protectedNotCancellableEsc() { 168 startDialogActivity(DialogStubActivity.TEST_PROTECTED_NOT_CANCELABLE); 169 mActivity.onCancelListenerCalled = false; 170 sendKeys(KeyEvent.KEYCODE_ESCAPE); 171 assertFalse(mActivity.onCancelListenerCalled); 172 } 173 assertTextAppearanceStyle(TypedArray ta)174 private void assertTextAppearanceStyle(TypedArray ta) { 175 final int defValue = -1; 176 // get Theme and assert 177 final Resources.Theme expected = mContext.getResources().newTheme(); 178 expected.setTo(mContext.getTheme()); 179 expected.applyStyle(R.style.TextAppearance, true); 180 TypedArray expectedTa = expected.obtainStyledAttributes(R.styleable.TextAppearance); 181 assertEquals(expectedTa.getIndexCount(), ta.getIndexCount()); 182 assertEquals(expectedTa.getColor(R.styleable.TextAppearance_textColor, defValue), 183 ta.getColor(R.styleable.TextAppearance_textColor, defValue)); 184 assertEquals(expectedTa.getColor(R.styleable.TextAppearance_textColorHint, defValue), 185 ta.getColor(R.styleable.TextAppearance_textColorHint, defValue)); 186 assertEquals(expectedTa.getColor(R.styleable.TextAppearance_textColorLink, defValue), 187 ta.getColor(R.styleable.TextAppearance_textColorLink, defValue)); 188 assertEquals(expectedTa.getColor(R.styleable.TextAppearance_textColorHighlight, defValue), 189 ta.getColor(R.styleable.TextAppearance_textColorHighlight, defValue)); 190 assertEquals(expectedTa.getDimension(R.styleable.TextAppearance_textSize, defValue), 191 ta.getDimension(R.styleable.TextAppearance_textSize, defValue), Float.MIN_VALUE); 192 assertEquals(expectedTa.getInt(R.styleable.TextAppearance_textStyle, defValue), 193 ta.getInt(R.styleable.TextAppearance_textStyle, defValue)); 194 } 195 196 @Test testOnStartCreateStop()197 public void testOnStartCreateStop(){ 198 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 199 final TestDialog d = (TestDialog) mActivity.getDialog(); 200 201 assertTrue(d.isOnStartCalled); 202 assertTrue(d.isOnCreateCalled); 203 204 assertFalse(d.isOnStopCalled); 205 sendKeys(KeyEvent.KEYCODE_BACK); 206 PollingCheck.waitFor(() -> { 207 return d.isOnStopCalled; }); 208 } 209 210 @Test testOnStartCreateStopEsc()211 public void testOnStartCreateStopEsc(){ 212 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 213 final TestDialog d = (TestDialog) mActivity.getDialog(); 214 215 assertTrue(d.isOnStartCalled); 216 assertTrue(d.isOnCreateCalled); 217 218 assertFalse(d.isOnStopCalled); 219 sendKeys(KeyEvent.KEYCODE_ESCAPE); 220 PollingCheck.waitFor(() -> d.isOnStopCalled); 221 } 222 223 @Test testAccessOwnerActivity()224 public void testAccessOwnerActivity() throws Throwable { 225 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 226 Dialog d = mActivity.getDialog(); 227 assertNotNull(d); 228 assertSame(mActivity, d.getOwnerActivity()); 229 d.setVolumeControlStream(d.getVolumeControlStream() + 1); 230 assertEquals(d.getOwnerActivity().getVolumeControlStream() + 1, d.getVolumeControlStream()); 231 232 try { 233 d.setOwnerActivity(null); 234 fail("Should throw NullPointerException"); 235 } catch (NullPointerException e) { 236 // expected 237 } 238 239 mScenario.onActivity(activity -> { 240 Dialog dialog = new Dialog(mContext); 241 assertNull(dialog.getOwnerActivity()); 242 }); 243 mInstrumentation.waitForIdleSync(); 244 } 245 246 @Test testShow()247 public void testShow() throws Throwable { 248 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 249 final Dialog d = mActivity.getDialog(); 250 final View decor = d.getWindow().getDecorView(); 251 252 mScenario.onActivity(activity -> { 253 d.hide(); 254 }); 255 mInstrumentation.waitForIdleSync(); 256 257 assertEquals(View.GONE, decor.getVisibility()); 258 assertFalse(d.isShowing()); 259 260 mScenario.onActivity(activity -> { 261 d.show(); 262 }); 263 mInstrumentation.waitForIdleSync(); 264 265 assertEquals(View.VISIBLE, decor.getVisibility()); 266 assertTrue(d.isShowing()); 267 dialogDismiss(d); 268 assertFalse(d.isShowing()); 269 } 270 271 @Test testOnSaveInstanceState()272 public void testOnSaveInstanceState() throws InterruptedException { 273 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 274 final TestDialog d = (TestDialog) mActivity.getDialog(); 275 276 d.onSaveInstanceStateObserver.startObserving(); 277 TestDialog.onRestoreInstanceStateObserver.startObserving(); 278 mScenario.recreate(); 279 d.onSaveInstanceStateObserver.await(); 280 TestDialog.onRestoreInstanceStateObserver.await(); 281 } 282 283 @Test testGetCurrentFocus()284 public void testGetCurrentFocus() throws Throwable { 285 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 286 final TestDialog d = (TestDialog) mActivity.getDialog(); 287 assertNull(d.getCurrentFocus()); 288 289 mScenario.onActivity(activity -> { 290 d.takeKeyEvents(true); 291 d.setContentView(R.layout.alert_dialog_text_entry); 292 }); 293 mInstrumentation.waitForIdleSync(); 294 295 sendKeys(KeyEvent.KEYCODE_0); 296 // When mWindow is not null getCurrentFocus is the view in dialog 297 assertEquals(d.getWindow().getCurrentFocus(), d.getCurrentFocus()); 298 } 299 300 @Test testSetContentView()301 public void testSetContentView() throws Throwable { 302 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 303 final Dialog d = mActivity.getDialog(); 304 assertNotNull(d); 305 306 // set content view to a four elements layout 307 mScenario.onActivity(activity -> { 308 d.setContentView(R.layout.alert_dialog_text_entry); 309 }); 310 mInstrumentation.waitForIdleSync(); 311 312 // check if four elements are right there 313 assertNotNull(d.findViewById(R.id.username_view)); 314 assertNotNull(d.findViewById(R.id.username_edit)); 315 assertNotNull(d.findViewById(R.id.password_view)); 316 assertNotNull(d.findViewById(R.id.password_edit)); 317 318 final LayoutInflater inflate1 = d.getLayoutInflater(); 319 320 // set content view to a two elements layout 321 mScenario.onActivity(activity -> { 322 d.setContentView(inflate1.inflate(R.layout.alert_dialog_text_entry_2, null)); 323 }); 324 mInstrumentation.waitForIdleSync(); 325 326 // check if only two elements are right there 327 assertNotNull(d.findViewById(R.id.username_view)); 328 assertNotNull(d.findViewById(R.id.username_edit)); 329 assertNull(d.findViewById(R.id.password_view)); 330 assertNull(d.findViewById(R.id.password_edit)); 331 332 final WindowManager.LayoutParams lp = d.getWindow().getAttributes(); 333 final LayoutInflater inflate2 = mActivity.getLayoutInflater(); 334 335 // set content view to a four elements layout 336 mScenario.onActivity(activity -> { 337 d.setContentView(inflate2.inflate(R.layout.alert_dialog_text_entry, null), lp); 338 }); 339 mInstrumentation.waitForIdleSync(); 340 341 // check if four elements are right there 342 assertNotNull(d.findViewById(R.id.username_view)); 343 assertNotNull(d.findViewById(R.id.username_edit)); 344 assertNotNull(d.findViewById(R.id.password_view)); 345 assertNotNull(d.findViewById(R.id.password_edit)); 346 347 final WindowManager.LayoutParams lp2 = d.getWindow().getAttributes(); 348 final LayoutInflater inflate3 = mActivity.getLayoutInflater(); 349 lp2.height = ViewGroup.LayoutParams.WRAP_CONTENT; 350 lp2.width = ViewGroup.LayoutParams.WRAP_CONTENT; 351 352 // add a check box view 353 mScenario.onActivity(activity -> { 354 d.addContentView(inflate3.inflate(R.layout.checkbox_layout, null), lp2); 355 }); 356 mInstrumentation.waitForIdleSync(); 357 358 // check if four elements are right there, and new add view there. 359 assertNotNull(d.findViewById(R.id.check_box)); 360 assertNotNull(d.findViewById(R.id.username_view)); 361 assertNotNull(d.findViewById(R.id.username_edit)); 362 assertNotNull(d.findViewById(R.id.password_view)); 363 assertNotNull(d.findViewById(R.id.password_edit)); 364 } 365 366 @Test testRequireViewById()367 public void testRequireViewById() throws Throwable { 368 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 369 final Dialog d = mActivity.getDialog(); 370 assertNotNull(d); 371 372 // set content view to a four elements layout 373 mScenario.onActivity(activity -> { 374 d.setContentView(R.layout.alert_dialog_text_entry); 375 }); 376 mInstrumentation.waitForIdleSync(); 377 378 // check if four elements are right there 379 assertNotNull(d.requireViewById(R.id.username_view)); 380 assertNotNull(d.requireViewById(R.id.username_edit)); 381 assertNotNull(d.requireViewById(R.id.password_view)); 382 assertNotNull(d.requireViewById(R.id.password_edit)); 383 try { 384 d.requireViewById(R.id.check_box); // not present 385 fail("should not get here, check_box should not be found"); 386 } catch (IllegalArgumentException e) { 387 // expected 388 } 389 try { 390 d.requireViewById(View.NO_ID); // invalid 391 fail("should not get here, NO_ID should not be found"); 392 } catch (IllegalArgumentException e) { 393 // expected 394 } 395 } 396 397 398 @Test testSetTitle()399 public void testSetTitle() { 400 final String expectedTitle = "Test Dialog Without theme"; 401 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 402 403 assertNotNull(mActivity.getDialog()); 404 mActivity.setUpTitle(expectedTitle); 405 mInstrumentation.waitForIdleSync(); 406 407 final Dialog d = mActivity.getDialog(); 408 assertEquals(expectedTitle, (String) d.getWindow().getAttributes().getTitle()); 409 410 mActivity.setUpTitle(R.string.hello_android); 411 mInstrumentation.waitForIdleSync(); 412 assertEquals(mActivity.getResources().getString(R.string.hello_android), 413 (String) d.getWindow().getAttributes().getTitle()); 414 } 415 416 @Test testOnKeyDownKeyUp()417 public void testOnKeyDownKeyUp() { 418 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 419 final TestDialog d = (TestDialog) mActivity.getDialog(); 420 assertFalse(d.isOnKeyDownCalled); 421 assertFalse(d.isOnKeyUpCalled); 422 423 // send key 0 down and up events, onKeyDown return false 424 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_0); 425 assertTrue(d.isOnKeyDownCalled); 426 assertTrue(d.isOnKeyUpCalled); 427 assertEquals(KeyEvent.KEYCODE_0, d.keyDownCode); 428 assertFalse(d.onKeyDownReturn); 429 430 // send key back down and up events, onKeyDown return true 431 mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK); 432 assertEquals(KeyEvent.KEYCODE_BACK, d.keyDownCode); 433 assertTrue(d.onKeyDownReturn); 434 } 435 436 @Test testOnKeyMultiple()437 public void testOnKeyMultiple() { 438 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 439 final TestDialog d = (TestDialog) mActivity.getDialog(); 440 441 assertNull(d.keyMultipleEvent); 442 d.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_UNKNOWN)); 443 assertTrue(d.isOnKeyMultipleCalled); 444 assertFalse(d.onKeyMultipleReturn); 445 assertEquals(KeyEvent.KEYCODE_UNKNOWN, d.keyMultipleEvent.getKeyCode()); 446 assertEquals(KeyEvent.ACTION_MULTIPLE, d.keyMultipleEvent.getAction()); 447 } 448 sendTouchEvent(long downTime, int action, float x, float y)449 private MotionEvent sendTouchEvent(long downTime, int action, float x, float y) { 450 long eventTime = downTime; 451 if (action != MotionEvent.ACTION_DOWN) { 452 eventTime += 1; 453 } 454 MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0); 455 event.setSource(InputDevice.SOURCE_TOUCHSCREEN); 456 mUserHelper.injectDisplayIdIfNeeded(event); 457 mInstrumentation.getUiAutomation().injectInputEvent(event, true); 458 mInstrumentation.waitForIdleSync(); 459 return event; 460 } 461 462 @Test testTouchEvent()463 public void testTouchEvent() { 464 // Watch activities cover the entire screen, so there is no way to touch outside. 465 assumeFalse(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); 466 467 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 468 final TestDialog d = (TestDialog) mActivity.getDialog(); 469 470 int dialogLocation[] = new int[2]; 471 d.getWindow().getDecorView().getRootView().getLocationOnScreen(dialogLocation); 472 473 final int touchSlop = ViewConfiguration.get(mActivity).getScaledWindowTouchSlop(); 474 final int x = dialogLocation[0]; 475 final int y = dialogLocation[1] - (touchSlop + 1); 476 477 assertNull(d.onTouchEvent); 478 assertNull(d.touchEvent); 479 assertFalse(d.isOnTouchEventCalled); 480 481 // Tap outside the dialog window. Expect the event to be ignored 482 // because closeOnTouchOutside is false. 483 d.setCanceledOnTouchOutside(false); 484 485 long downTime = SystemClock.uptimeMillis(); 486 487 sendTouchEvent(downTime, MotionEvent.ACTION_DOWN, x, y).recycle(); 488 MotionEvent touchMotionEvent = sendTouchEvent(downTime, MotionEvent.ACTION_UP, x, y); 489 490 assertMotionEventEquals(touchMotionEvent, d.touchEvent); 491 assertTrue(d.isOnTouchEventCalled); 492 assertMotionEventEquals(touchMotionEvent, d.onTouchEvent); 493 d.isOnTouchEventCalled = false; 494 assertTrue(d.isShowing()); 495 touchMotionEvent.recycle(); 496 497 // Send a touch event outside the dialog window. Expect the dialog to be dismissed 498 // because closeOnTouchOutside is true. 499 d.setCanceledOnTouchOutside(true); 500 downTime = SystemClock.uptimeMillis(); 501 502 sendTouchEvent(downTime, MotionEvent.ACTION_DOWN, x, y).recycle(); 503 touchMotionEvent = sendTouchEvent(downTime, MotionEvent.ACTION_UP, x, y); 504 505 assertMotionEventEquals(touchMotionEvent, d.touchEvent); 506 assertTrue(d.isOnTouchEventCalled); 507 assertMotionEventEquals(touchMotionEvent, d.onTouchEvent); 508 assertFalse(d.isShowing()); 509 touchMotionEvent.recycle(); 510 } 511 512 @Test testTrackballEvent()513 public void testTrackballEvent() { 514 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 515 final TestDialog d = (TestDialog) mActivity.getDialog(); 516 long eventTime = SystemClock.uptimeMillis(); 517 final MotionEvent trackBallEvent = MotionEvent.obtain(eventTime, eventTime, 518 MotionEvent.ACTION_DOWN, 0.0f, 0.0f, 0); 519 mUserHelper.injectDisplayIdIfNeeded(trackBallEvent); 520 521 assertNull(d.trackballEvent); 522 assertNull(d.onTrackballEvent); 523 524 assertFalse(d.isOnTrackballEventCalled); 525 mInstrumentation.sendTrackballEventSync(trackBallEvent); 526 assertTrue(d.isOnTrackballEventCalled); 527 assertMotionEventEquals(trackBallEvent, d.trackballEvent); 528 assertMotionEventEquals(trackBallEvent, d.onTrackballEvent); 529 530 } 531 assertMotionEventEquals(final MotionEvent expected, final MotionEvent actual)532 private void assertMotionEventEquals(final MotionEvent expected, final MotionEvent actual) { 533 assertNotNull(actual); 534 assertEquals(expected.getDownTime(), actual.getDownTime()); 535 assertEquals(expected.getEventTime(), actual.getEventTime()); 536 assertEquals(expected.getAction(), actual.getAction()); 537 assertEquals(expected.getMetaState(), actual.getMetaState()); 538 assertEquals(expected.getSize(), actual.getSize(), Float.MIN_VALUE); 539 // As MotionEvent doc says the value of X and Y coordinate may have 540 // a fraction for input devices that are sub-pixel precise, 541 // so we won't assert them here. 542 } 543 544 @Test testOnWindowAttributesChanged()545 public void testOnWindowAttributesChanged() throws Throwable { 546 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 547 final TestDialog d = (TestDialog) mActivity.getDialog(); 548 549 assertTrue(d.isOnWindowAttributesChangedCalled); 550 d.isOnWindowAttributesChangedCalled = false; 551 552 final WindowManager.LayoutParams lp = d.getWindow().getAttributes(); 553 lp.setTitle("test OnWindowAttributesChanged"); 554 mScenario.onActivity(activity -> { 555 d.getWindow().setAttributes(lp); 556 }); 557 mInstrumentation.waitForIdleSync(); 558 559 assertTrue(d.isOnWindowAttributesChangedCalled); 560 assertSame(lp, d.getWindow().getAttributes()); 561 } 562 563 @Test testOnContentChanged()564 public void testOnContentChanged() throws Throwable { 565 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 566 final TestDialog d = (TestDialog) mActivity.getDialog(); 567 assertNotNull(d); 568 569 assertFalse(d.isOnContentChangedCalled); 570 571 mScenario.onActivity(activity -> { 572 d.setContentView(R.layout.alert_dialog_text_entry); 573 }); 574 mInstrumentation.waitForIdleSync(); 575 576 assertTrue(d.isOnContentChangedCalled); 577 } 578 579 @Test testOnWindowFocusChanged()580 public void testOnWindowFocusChanged() throws Throwable { 581 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 582 final TestDialog d = (TestDialog) mActivity.getDialog(); 583 assertTrue(d.isOnWindowFocusChangedCalled); 584 d.isOnWindowFocusChangedCalled = false; 585 586 // show a new dialog, the new dialog get focus 587 mScenario.onActivity(activity -> { 588 mActivity.showDialog(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 589 }); 590 591 PollingCheck.waitFor(() -> d.isOnWindowFocusChangedCalled); 592 } 593 594 @Test testDispatchKeyEvent()595 public void testDispatchKeyEvent() { 596 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 597 final TestDialog d = (TestDialog) mActivity.getDialog(); 598 599 sendKeys(KeyEvent.KEYCODE_0); 600 assertFalse(d.dispatchKeyEventResult); 601 assertEquals(KeyEvent.KEYCODE_0, d.keyEvent.getKeyCode()); 602 603 d.setOnKeyListener(new OnKeyListener() { 604 public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { 605 if (KeyEvent.ACTION_DOWN == event.getAction()) { 606 if (KeyEvent.KEYCODE_0 == keyCode) { 607 mIsKey0Listened = true; 608 return true; 609 } 610 611 if (KeyEvent.KEYCODE_1 == keyCode) { 612 mIsKey1Listened = true; 613 return true; 614 } 615 } 616 617 return false; 618 } 619 }); 620 621 mIsKey1Listened = false; 622 sendKeys(KeyEvent.KEYCODE_1); 623 assertTrue(mIsKey1Listened); 624 625 mIsKey0Listened = false; 626 sendKeys(KeyEvent.KEYCODE_0); 627 assertTrue(mIsKey0Listened); 628 } 629 630 /* 631 * Test point 632 * 1. registerForContextMenu() will OnCreateContextMenuListener on the view to this activity, 633 * so onCreateContextMenu() will be called when it is time to show the context menu. 634 * 2. Close context menu will make onPanelClosed to be called, 635 * and onPanelClosed will calls through to the new onPanelClosed method. 636 * 3. unregisterForContextMenu() will remove the OnCreateContextMenuListener on the view, 637 * so onCreateContextMenu() will not be called when try to open context menu. 638 * 4. Selected a item of context menu will make onMenuItemSelected() to be called, 639 * and onMenuItemSelected will calls through to the new onContextItemSelected method. 640 * 5. onContextMenuClosed is called whenever the context menu is being closed (either by 641 * the user canceling the menu with the back/menu button, or when an item is selected). 642 */ 643 @Test testContextMenu()644 public void testContextMenu() throws Throwable { 645 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 646 final TestDialog d = (TestDialog) mActivity.getDialog(); 647 final LinearLayout parent = new LinearLayout(mContext); 648 final MockView v = new MockView(mContext); 649 parent.addView(v); 650 assertFalse(v.isShowContextMenuCalled); 651 // Register for context menu and open it 652 mScenario.onActivity(activity -> { 653 d.addContentView(parent, new LinearLayout.LayoutParams( 654 ViewGroup.LayoutParams.MATCH_PARENT, 655 ViewGroup.LayoutParams.WRAP_CONTENT)); 656 d.registerForContextMenu(v); 657 d.openContextMenu(v); 658 }); 659 PollingCheck.waitFor(d::contextMenuHasWindowFocus); 660 PollingCheck.waitFor(() -> v.isShowContextMenuCalled); 661 PollingCheck.waitFor(() -> d.isOnCreateContextMenuCalled); 662 663 assertFalse(d.isOnPanelClosedCalled); 664 assertFalse(d.isOnContextMenuClosedCalled); 665 // Close context menu 666 d.isOnWindowFocusChangedCalled = false; 667 sendKeys(KeyEvent.KEYCODE_BACK); 668 PollingCheck.waitFor(() -> d.isOnPanelClosedCalled); 669 // Wait for window focus change after pressing back 670 PollingCheck.waitFor(() -> d.isOnWindowFocusChangedCalled); 671 // Here isOnContextMenuClosedCalled should be true, see bug 1716918. 672 assertFalse(d.isOnContextMenuClosedCalled); 673 674 v.isShowContextMenuCalled = false; 675 d.isOnCreateContextMenuCalled = false; 676 // Unregister for context menu, and try to open it 677 mScenario.onActivity(activity -> { 678 d.unregisterForContextMenu(v); 679 }); 680 681 mScenario.onActivity(activity -> { 682 d.openContextMenu(v); 683 }); 684 685 assertTrue(v.isShowContextMenuCalled); 686 assertFalse(d.isOnCreateContextMenuCalled); 687 688 // Register for context menu and open it again 689 v.isShowContextMenuCalled = false; 690 d.isOnCreateContextMenuCalled = false; 691 mScenario.onActivity(activity -> { 692 d.registerForContextMenu(v); 693 d.openContextMenu(v); 694 }); 695 PollingCheck.waitFor(() -> d.isOnCreateContextMenuCalled); 696 PollingCheck.waitFor(() -> v.isShowContextMenuCalled); 697 PollingCheck.waitFor(d::contextMenuHasWindowFocus); 698 699 assertFalse(d.isOnContextItemSelectedCalled); 700 assertFalse(d.isOnMenuItemSelectedCalled); 701 d.isOnPanelClosedCalled = false; 702 assertFalse(d.isOnContextMenuClosedCalled); 703 // select a context menu item 704 d.selectContextMenuItem(); 705 assertTrue(d.isOnMenuItemSelectedCalled); 706 // Here isOnContextItemSelectedCalled should be true, see bug 1716918. 707 assertFalse(d.isOnContextItemSelectedCalled); 708 PollingCheck.waitFor(() -> d.isOnPanelClosedCalled); 709 // Here isOnContextMenuClosedCalled should be true, see bug 1716918. 710 assertFalse(d.isOnContextMenuClosedCalled); 711 } 712 713 @Test testOnSearchRequested()714 public void testOnSearchRequested() { 715 } 716 717 @Test testTakeKeyEvents()718 public void testTakeKeyEvents() throws Throwable { 719 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 720 final TestDialog d = (TestDialog) mActivity.getDialog(); 721 final View v = d.getWindow().getDecorView(); 722 assertNull(d.getCurrentFocus()); 723 takeKeyEvents(d, true); 724 assertTrue(v.isFocusable()); 725 sendKeys(KeyEvent.KEYCODE_0); 726 assertEquals(KeyEvent.KEYCODE_0, d.keyEvent.getKeyCode()); 727 d.keyEvent = null; 728 729 takeKeyEvents(d, false); 730 assertNull(d.getCurrentFocus()); 731 assertFalse(v.isFocusable()); 732 sendKeys(KeyEvent.KEYCODE_0); 733 // d.keyEvent should be null 734 } 735 takeKeyEvents(final Dialog d, final boolean get)736 private void takeKeyEvents(final Dialog d, final boolean get) throws Throwable { 737 mScenario.onActivity(activity -> { 738 d.takeKeyEvents(get); 739 }); 740 } 741 742 @Test testRequestWindowFeature()743 public void testRequestWindowFeature() { 744 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 745 // called requestWindowFeature at TestDialog onCreate method 746 assertTrue(((TestDialog) mActivity.getDialog()).isRequestWindowFeature); 747 } 748 749 @Test testSetFeatureDrawableResource()750 public void testSetFeatureDrawableResource() throws Throwable { 751 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 752 mScenario.onActivity(activity -> { 753 mActivity.getDialog().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, 754 R.drawable.robot); 755 }); 756 mInstrumentation.waitForIdleSync(); 757 } 758 759 @Test testSetFeatureDrawableUri()760 public void testSetFeatureDrawableUri() throws Throwable { 761 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 762 mScenario.onActivity(activity -> { 763 mActivity.getDialog().setFeatureDrawableUri(Window.FEATURE_LEFT_ICON, 764 Uri.parse("http://www.google.com")); 765 }); 766 mInstrumentation.waitForIdleSync(); 767 } 768 769 @Test testSetFeatureDrawable()770 public void testSetFeatureDrawable() throws Throwable { 771 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 772 mScenario.onActivity(activity -> { 773 mActivity.getDialog().setFeatureDrawable(Window.FEATURE_LEFT_ICON, 774 new MockDrawable()); 775 }); 776 mInstrumentation.waitForIdleSync(); 777 } 778 779 @Test testSetFeatureDrawableAlpha()780 public void testSetFeatureDrawableAlpha() throws Throwable { 781 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 782 mScenario.onActivity(activity -> { 783 mActivity.getDialog().setFeatureDrawableAlpha(Window.FEATURE_LEFT_ICON, 0); 784 }); 785 mInstrumentation.waitForIdleSync(); 786 } 787 788 @Test testGetLayoutInflater()789 public void testGetLayoutInflater() { 790 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 791 final Dialog d = mActivity.getDialog(); 792 assertEquals(d.getWindow().getLayoutInflater(), d.getLayoutInflater()); 793 } 794 795 @Test testSetCancelable_true()796 public void testSetCancelable_true() { 797 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 798 final Dialog d = mActivity.getDialog(); 799 800 d.setCancelable(true); 801 assertTrue(d.isShowing()); 802 sendKeys(KeyEvent.KEYCODE_BACK); 803 PollingCheck.waitFor(() -> !d.isShowing()); 804 } 805 806 @Test testSetCancelable_false()807 public void testSetCancelable_false() { 808 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 809 final Dialog d = mActivity.getDialog(); 810 811 d.setCancelable(false); 812 assertTrue(d.isShowing()); 813 sendKeys(KeyEvent.KEYCODE_BACK); 814 assertTrue(d.isShowing()); 815 } 816 817 @Test testSetCancelableEsc_true()818 public void testSetCancelableEsc_true() { 819 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 820 final Dialog d = mActivity.getDialog(); 821 822 d.setCancelable(true); 823 assertTrue(d.isShowing()); 824 sendKeys(KeyEvent.KEYCODE_ESCAPE); 825 PollingCheck.waitFor(() -> !d.isShowing()); 826 } 827 828 @Test testSetCancelableEsc_false()829 public void testSetCancelableEsc_false() { 830 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 831 final Dialog d = mActivity.getDialog(); 832 833 d.setCanceledOnTouchOutside(false); 834 d.setCancelable(false); 835 assertTrue(d.isShowing()); 836 sendKeys(KeyEvent.KEYCODE_ESCAPE); 837 assertTrue(d.isShowing()); 838 } 839 840 841 @Test testSetCanceledOnTouchOutsideEsc_true()842 public void testSetCanceledOnTouchOutsideEsc_true() { 843 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 844 final Dialog d = mActivity.getDialog(); 845 846 d.setCanceledOnTouchOutside(true); 847 d.setCancelable(false); 848 assertTrue(d.isShowing()); 849 sendKeys(KeyEvent.KEYCODE_ESCAPE); 850 PollingCheck.waitFor(() -> !d.isShowing()); 851 } 852 853 /* 854 * Test point 855 * 1. Cancel the dialog. 856 * 2. Set a listener to be invoked when the dialog is canceled. 857 */ 858 @Test testCancel_listener()859 public void testCancel_listener() throws Throwable { 860 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 861 final Dialog d = mActivity.getDialog(); 862 863 assertTrue(d.isShowing()); 864 mOnCancelListenerCalled = false; 865 866 d.setOnCancelListener(new OnCancelListener() { 867 public void onCancel(DialogInterface dialog) { 868 mOnCancelListenerCalled = true; 869 } 870 }); 871 dialogCancel(d); 872 873 assertFalse(d.isShowing()); 874 assertTrue(mOnCancelListenerCalled); 875 } 876 877 @Test testCancel_noListener()878 public void testCancel_noListener() throws Throwable { 879 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 880 final Dialog d = mActivity.getDialog(); 881 882 assertTrue(d.isShowing()); 883 mOnCancelListenerCalled = false; 884 d.setOnCancelListener(null); 885 dialogCancel(d); 886 887 assertFalse(d.isShowing()); 888 assertFalse(mOnCancelListenerCalled); 889 } 890 891 @Test testSetCancelMessage()892 public void testSetCancelMessage() throws Exception { 893 mCalledCallback = false; 894 startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP); 895 final TestDialog d = (TestDialog) mActivity.getDialog(); 896 final HandlerThread ht = new HandlerThread("DialogTest"); 897 ht.start(); 898 899 d.setCancelMessage(new MockDismissCancelHandler(d, ht.getLooper()).obtainMessage(CANCEL, 900 new OnCancelListener() { 901 public void onCancel(DialogInterface dialog) { 902 mCalledCallback = true; 903 } 904 })); 905 assertTrue(d.isShowing()); 906 assertFalse(mCalledCallback); 907 sendKeys(KeyEvent.KEYCODE_BACK); 908 PollingCheck.waitFor(() -> mCalledCallback); 909 PollingCheck.waitFor(() -> !d.isShowing()); 910 911 ht.join(100); 912 } 913 914 /* 915 * Test point 916 * 1. Set a listener to be invoked when the dialog is dismissed. 917 * 2. set onDismissListener to null, it will not changed flag after dialog dismissed. 918 */ 919 @Test testSetOnDismissListener_listener()920 public void testSetOnDismissListener_listener() throws Throwable { 921 mCalledCallback = false; 922 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 923 final Dialog d = mActivity.getDialog(); 924 925 d.setOnDismissListener(new OnDismissListener() { 926 public void onDismiss(DialogInterface dialog) { 927 mCalledCallback = true; 928 } 929 }); 930 931 assertTrue(d.isShowing()); 932 assertFalse(mCalledCallback); 933 dialogDismiss(d); 934 assertTrue(mCalledCallback); 935 assertFalse(d.isShowing()); 936 } 937 938 @Test testSetOnDismissListener_noListener()939 public void testSetOnDismissListener_noListener() throws Throwable { 940 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 941 final Dialog d = mActivity.getDialog(); 942 assertTrue(d.isShowing()); 943 mCalledCallback = false; 944 d.setOnDismissListener(null); 945 dialogDismiss(d); 946 assertFalse(mCalledCallback); 947 assertFalse(d.isShowing()); 948 } 949 950 @Test testSetDismissMessage()951 public void testSetDismissMessage() throws Throwable { 952 mCalledCallback = false; 953 startDialogActivity(DialogStubActivity.TEST_DIALOG_WITHOUT_THEME); 954 final Dialog d = mActivity.getDialog(); 955 956 final HandlerThread ht = new HandlerThread("DialogTest"); 957 ht.start(); 958 959 d.setDismissMessage(new MockDismissCancelHandler(d, ht.getLooper()).obtainMessage(DISMISS, 960 new OnDismissListener() { 961 public void onDismiss(DialogInterface dialog) { 962 mCalledCallback = true; 963 } 964 })); 965 assertTrue(d.isShowing()); 966 assertFalse(mCalledCallback); 967 dialogDismiss(d); 968 ht.join(100); 969 assertTrue(mCalledCallback); 970 assertFalse(d.isShowing()); 971 } 972 dialogDismiss(final Dialog d)973 private void dialogDismiss(final Dialog d) throws Throwable { 974 mScenario.onActivity(activity -> { 975 d.dismiss(); 976 }); 977 mInstrumentation.waitForIdleSync(); 978 } 979 dialogCancel(final Dialog d)980 private void dialogCancel(final Dialog d) throws Throwable { 981 mScenario.onActivity(activity -> { 982 d.cancel(); 983 }); 984 mInstrumentation.waitForIdleSync(); 985 } 986 sendKeys(int keyCode)987 private void sendKeys(int keyCode) { 988 mInstrumentation.sendKeyDownUpSync(keyCode); 989 } 990 991 private static class MockDismissCancelHandler extends Handler { 992 private WeakReference<DialogInterface> mDialog; 993 MockDismissCancelHandler(Dialog dialog, Looper looper)994 public MockDismissCancelHandler(Dialog dialog, Looper looper) { 995 super(looper); 996 997 mDialog = new WeakReference<DialogInterface>(dialog); 998 } 999 1000 @Override handleMessage(Message msg)1001 public void handleMessage(Message msg) { 1002 switch (msg.what) { 1003 case DISMISS: 1004 ((OnDismissListener) msg.obj).onDismiss(mDialog.get()); 1005 break; 1006 case CANCEL: 1007 ((OnCancelListener) msg.obj).onCancel(mDialog.get()); 1008 break; 1009 } 1010 } 1011 } 1012 1013 private static class MockDrawable extends Drawable { 1014 @Override draw(Canvas canvas)1015 public void draw(Canvas canvas) { 1016 } 1017 1018 @Override getOpacity()1019 public int getOpacity() { 1020 return 0; 1021 } 1022 1023 @Override setAlpha(int alpha)1024 public void setAlpha(int alpha) { 1025 } 1026 1027 @Override setColorFilter(ColorFilter cf)1028 public void setColorFilter(ColorFilter cf) { 1029 } 1030 } 1031 1032 private static class MockView extends View { 1033 public boolean isShowContextMenuCalled; 1034 protected OnCreateContextMenuListener mOnCreateContextMenuListener; 1035 MockView(Context context)1036 public MockView(Context context) { 1037 super(context); 1038 } 1039 setOnCreateContextMenuListener(OnCreateContextMenuListener l)1040 public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) { 1041 super.setOnCreateContextMenuListener(l); 1042 mOnCreateContextMenuListener = l; 1043 } 1044 1045 @Override showContextMenu()1046 public boolean showContextMenu() { 1047 isShowContextMenuCalled = true; 1048 return super.showContextMenu(); 1049 } 1050 } 1051 } 1052