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