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