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