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