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