1 /*
2  * Copyright (C) 2015 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 
17 package androidx.appcompat.app;
18 
19 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
20 
21 import android.app.Dialog;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.database.Cursor;
25 import android.graphics.drawable.Drawable;
26 import android.os.Bundle;
27 import android.os.Message;
28 import android.util.TypedValue;
29 import android.view.ContextThemeWrapper;
30 import android.view.KeyEvent;
31 import android.view.View;
32 import android.widget.AdapterView;
33 import android.widget.Button;
34 import android.widget.ListAdapter;
35 import android.widget.ListView;
36 
37 import androidx.annotation.ArrayRes;
38 import androidx.annotation.AttrRes;
39 import androidx.annotation.DrawableRes;
40 import androidx.annotation.RestrictTo;
41 import androidx.annotation.StringRes;
42 import androidx.annotation.StyleRes;
43 import androidx.appcompat.R;
44 
45 import org.jspecify.annotations.NonNull;
46 import org.jspecify.annotations.Nullable;
47 
48 /**
49  * A subclass of Dialog that can display one, two or three buttons. If you only want to
50  * display a String in this dialog box, use the setMessage() method.  If you
51  * want to display a more complex view, look up the FrameLayout called "custom"
52  * and add your view to it:
53  *
54  * <pre>
55  * FrameLayout fl = findViewById(android.R.id.custom);
56  * fl.addView(myView, new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
57  * </pre>
58  *
59  * <p>The AlertDialog class takes care of automatically setting
60  * {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM
61  * android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM} for you based on whether
62  * any views in the dialog return true from {@link View#onCheckIsTextEditor()
63  * View.onCheckIsTextEditor()}.  Generally you want this set for a Dialog
64  * without text editors, so that it will be placed on top of the current
65  * input method UI.  You can modify this behavior by forcing the flag to your
66  * desired mode after calling {@link #onCreate}.
67  *
68  * <div class="special reference">
69  * <h3>Developer Guides</h3>
70  * <p>For more information about creating dialogs, read the
71  * <a href="{@docRoot}guide/topics/ui/dialogs.html">Dialogs</a> developer guide.</p>
72  * </div>
73  */
74 public class AlertDialog extends AppCompatDialog implements DialogInterface {
75 
76     final AlertController mAlert;
77 
78     /**
79      * No layout hint.
80      */
81     static final int LAYOUT_HINT_NONE = 0;
82 
83     /**
84      * Hint layout to the side.
85      */
86     static final int LAYOUT_HINT_SIDE = 1;
87 
AlertDialog(@onNull Context context)88     protected AlertDialog(@NonNull Context context) {
89         this(context, 0);
90     }
91 
92     /**
93      * Construct an AlertDialog that uses an explicit theme.  The actual style
94      * that an AlertDialog uses is a private implementation, however you can
95      * here supply either the name of an attribute in the theme from which
96      * to get the dialog's style (such as {@link R.attr#alertDialogTheme}.
97      */
AlertDialog(@onNull Context context, @StyleRes int themeResId)98     protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
99         super(context, resolveDialogTheme(context, themeResId));
100         mAlert = new AlertController(getContext(), this, getWindow());
101     }
102 
AlertDialog(@onNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener)103     protected AlertDialog(@NonNull Context context, boolean cancelable,
104             @Nullable OnCancelListener cancelListener) {
105         this(context, 0);
106         setCancelable(cancelable);
107         setOnCancelListener(cancelListener);
108     }
109 
resolveDialogTheme(@onNull Context context, @StyleRes int resid)110     static int resolveDialogTheme(@NonNull Context context, @StyleRes int resid) {
111         // Check to see if this resourceId has a valid package ID.
112         if (((resid >>> 24) & 0x000000ff) >= 0x00000001) {   // start of real resource IDs.
113             return resid;
114         } else {
115             TypedValue outValue = new TypedValue();
116             context.getTheme().resolveAttribute(R.attr.alertDialogTheme, outValue, true);
117             return outValue.resourceId;
118         }
119     }
120 
121     /**
122      * Gets one of the buttons used in the dialog. Returns null if the specified
123      * button does not exist or the dialog has not yet been fully created (for
124      * example, via {@link #show()} or {@link #create()}).
125      *
126      * @param whichButton The identifier of the button that should be returned.
127      *                    For example, this can be
128      *                    {@link DialogInterface#BUTTON_POSITIVE}.
129      * @return The button from the dialog, or null if a button does not exist.
130      */
getButton(int whichButton)131     public Button getButton(int whichButton) {
132         return mAlert.getButton(whichButton);
133     }
134 
135     /**
136      * Gets the list view used in the dialog.
137      *
138      * @return The {@link ListView} from the dialog.
139      */
getListView()140     public ListView getListView() {
141         return mAlert.getListView();
142     }
143 
144     @Override
setTitle(CharSequence title)145     public void setTitle(CharSequence title) {
146         super.setTitle(title);
147         mAlert.setTitle(title);
148     }
149 
150     /**
151      * This method has no effect if called after {@link #show()}.
152      *
153      * @see Builder#setCustomTitle(View)
154      */
setCustomTitle(View customTitleView)155     public void setCustomTitle(View customTitleView) {
156         mAlert.setCustomTitle(customTitleView);
157     }
158 
159     /**
160      * Sets the message to display.
161      *
162      * @param message The message to display in the dialog.
163      */
setMessage(CharSequence message)164     public void setMessage(CharSequence message) {
165         mAlert.setMessage(message);
166     }
167 
168     /**
169      * Set the view to display in the dialog. This method has no effect if called
170      * after {@link #show()}.
171      */
setView(View view)172     public void setView(View view) {
173         mAlert.setView(view);
174     }
175 
176     /**
177      * Set the view to display in the dialog, specifying the spacing to appear around that
178      * view.  This method has no effect if called after {@link #show()}.
179      *
180      * @param view              The view to show in the content area of the dialog
181      * @param viewSpacingLeft   Extra space to appear to the left of {@code view}
182      * @param viewSpacingTop    Extra space to appear above {@code view}
183      * @param viewSpacingRight  Extra space to appear to the right of {@code view}
184      * @param viewSpacingBottom Extra space to appear below {@code view}
185      */
setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight, int viewSpacingBottom)186     public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,
187             int viewSpacingBottom) {
188         mAlert.setView(view, viewSpacingLeft, viewSpacingTop, viewSpacingRight, viewSpacingBottom);
189     }
190 
191     /**
192      * Internal api to allow hinting for the best button panel layout.
193      */
194     @RestrictTo(LIBRARY_GROUP_PREFIX)
setButtonPanelLayoutHint(int layoutHint)195     void setButtonPanelLayoutHint(int layoutHint) {
196         mAlert.setButtonPanelLayoutHint(layoutHint);
197     }
198 
199     /**
200      * Sets a message to be sent when a button is pressed. This method has no effect if called
201      * after {@link #show()}.
202      *
203      * @param whichButton Which button to set the message for, can be one of
204      *                    {@link DialogInterface#BUTTON_POSITIVE},
205      *                    {@link DialogInterface#BUTTON_NEGATIVE}, or
206      *                    {@link DialogInterface#BUTTON_NEUTRAL}
207      * @param text        The text to display in positive button.
208      * @param msg         The {@link Message} to be sent when clicked.
209      */
setButton(int whichButton, CharSequence text, Message msg)210     public void setButton(int whichButton, CharSequence text, Message msg) {
211         mAlert.setButton(whichButton, text, null, msg, null);
212     }
213 
214     /**
215      * Sets a listener to be invoked when the positive button of the dialog is pressed. This method
216      * has no effect if called after {@link #show()}.
217      *
218      * @param whichButton Which button to set the listener on, can be one of
219      *                    {@link DialogInterface#BUTTON_POSITIVE},
220      *                    {@link DialogInterface#BUTTON_NEGATIVE}, or
221      *                    {@link DialogInterface#BUTTON_NEUTRAL}
222      * @param text        The text to display in positive button.
223      * @param listener    The {@link DialogInterface.OnClickListener} to use.
224      */
setButton(int whichButton, CharSequence text, OnClickListener listener)225     public void setButton(int whichButton, CharSequence text, OnClickListener listener) {
226         mAlert.setButton(whichButton, text, listener, null, null);
227     }
228 
229     /**
230      * Sets an icon to be displayed along with the button text and a listener to be invoked when
231      * the positive button of the dialog is pressed. This method has no effect if called after
232      * {@link #show()}.
233      *
234      * @param whichButton Which button to set the listener on, can be one of
235      *                    {@link DialogInterface#BUTTON_POSITIVE},
236      *                    {@link DialogInterface#BUTTON_NEGATIVE}, or
237      *                    {@link DialogInterface#BUTTON_NEUTRAL}
238      * @param text        The text to display in positive button.
239      * @param listener    The {@link DialogInterface.OnClickListener} to use.
240      * @param icon        The {@link Drawable} to be set as an icon for the button.
241      */
setButton(int whichButton, CharSequence text, Drawable icon, OnClickListener listener)242     public void setButton(int whichButton, CharSequence text, Drawable icon,
243             OnClickListener listener) {
244         mAlert.setButton(whichButton, text, listener, null,  icon);
245     }
246 
247     /**
248      * Set resId to 0 if you don't want an icon.
249      * @param resId the resourceId of the drawable to use as the icon or 0
250      * if you don't want an icon.
251      */
setIcon(int resId)252     public void setIcon(int resId) {
253         mAlert.setIcon(resId);
254     }
255 
256     /**
257      * Set the {@link Drawable} to be used in the title.
258      *
259      * @param icon Drawable to use as the icon or null if you don't want an icon.
260      */
setIcon(Drawable icon)261     public void setIcon(Drawable icon) {
262         mAlert.setIcon(icon);
263     }
264 
265     /**
266      * Sets an icon as supplied by a theme attribute. e.g. android.R.attr.alertDialogIcon
267      *
268      * @param attrId ID of a theme attribute that points to a drawable resource.
269      */
setIconAttribute(int attrId)270     public void setIconAttribute(int attrId) {
271         TypedValue out = new TypedValue();
272         getContext().getTheme().resolveAttribute(attrId, out, true);
273         mAlert.setIcon(out.resourceId);
274     }
275 
276     @Override
onCreate(Bundle savedInstanceState)277     protected void onCreate(Bundle savedInstanceState) {
278         super.onCreate(savedInstanceState);
279         mAlert.installContent();
280     }
281 
282     @Override
onKeyDown(int keyCode, KeyEvent event)283     public boolean onKeyDown(int keyCode, KeyEvent event) {
284         if (mAlert.onKeyDown(keyCode, event)) {
285             return true;
286         }
287         return super.onKeyDown(keyCode, event);
288     }
289 
290     @Override
onKeyUp(int keyCode, KeyEvent event)291     public boolean onKeyUp(int keyCode, KeyEvent event) {
292         if (mAlert.onKeyUp(keyCode, event)) {
293             return true;
294         }
295         return super.onKeyUp(keyCode, event);
296     }
297 
298     public static class Builder {
299         private final AlertController.AlertParams P;
300         private final int mTheme;
301 
302         /**
303          * Creates a builder for an alert dialog that uses the default alert
304          * dialog theme.
305          * <p>
306          * The default alert dialog theme is defined by
307          * {@link android.R.attr#alertDialogTheme} within the parent
308          * {@code context}'s theme.
309          *
310          * @param context the parent context
311          */
Builder(@onNull Context context)312         public Builder(@NonNull Context context) {
313             this(context, resolveDialogTheme(context, 0));
314         }
315 
316         /**
317          * Creates a builder for an alert dialog that uses an explicit theme
318          * resource.
319          * <p>
320          * The specified theme resource ({@code themeResId}) is applied on top
321          * of the parent {@code context}'s theme. It may be specified as a
322          * style resource containing a fully-populated theme, such as
323          * {@link R.style#Theme_AppCompat_Dialog}, to replace all
324          * attributes in the parent {@code context}'s theme including primary
325          * and accent colors.
326          * <p>
327          * To preserve attributes such as primary and accent colors, the
328          * {@code themeResId} may instead be specified as an overlay theme such
329          * as {@link R.style#ThemeOverlay_AppCompat_Dialog}. This will
330          * override only the window attributes necessary to style the alert
331          * window as a dialog.
332          * <p>
333          * Alternatively, the {@code themeResId} may be specified as {@code 0}
334          * to use the parent {@code context}'s resolved value for
335          * {@link android.R.attr#alertDialogTheme}.
336          *
337          * @param context the parent context
338          * @param themeResId the resource ID of the theme against which to inflate
339          *                   this dialog, or {@code 0} to use the parent
340          *                   {@code context}'s default alert dialog theme
341          */
Builder(@onNull Context context, @StyleRes int themeResId)342         public Builder(@NonNull Context context, @StyleRes int themeResId) {
343             P = new AlertController.AlertParams(new ContextThemeWrapper(
344                     context, resolveDialogTheme(context, themeResId)));
345             mTheme = themeResId;
346         }
347 
348         /**
349          * Returns a {@link Context} with the appropriate theme for dialogs created by this Builder.
350          * Applications should use this Context for obtaining LayoutInflaters for inflating views
351          * that will be used in the resulting dialogs, as it will cause views to be inflated with
352          * the correct theme.
353          *
354          * @return A Context for built Dialogs.
355          */
getContext()356         public @NonNull Context getContext() {
357             return P.mContext;
358         }
359 
360         /**
361          * Set the title using the given resource id.
362          *
363          * @return This Builder object to allow for chaining of calls to set methods
364          */
setTitle(@tringRes int titleId)365         public Builder setTitle(@StringRes int titleId) {
366             P.mTitle = P.mContext.getText(titleId);
367             return this;
368         }
369 
370         /**
371          * Set the title displayed in the {@link Dialog}.
372          *
373          * @return This Builder object to allow for chaining of calls to set methods
374          */
setTitle(@ullable CharSequence title)375         public Builder setTitle(@Nullable CharSequence title) {
376             P.mTitle = title;
377             return this;
378         }
379 
380         /**
381          * Set the title using the custom view {@code customTitleView}.
382          * <p>
383          * The methods {@link #setTitle(int)} and {@link #setIcon(int)} should
384          * be sufficient for most titles, but this is provided if the title
385          * needs more customization. Using this will replace the title and icon
386          * set via the other methods.
387          * <p>
388          * <strong>Note:</strong> To ensure consistent styling, the custom view
389          * should be inflated or constructed using the alert dialog's themed
390          * context obtained via {@link #getContext()}.
391          *
392          * @param customTitleView the custom view to use as the title
393          * @return this Builder object to allow for chaining of calls to set
394          *         methods
395          */
setCustomTitle(@ullable View customTitleView)396         public Builder setCustomTitle(@Nullable View customTitleView) {
397             P.mCustomTitleView = customTitleView;
398             return this;
399         }
400 
401         /**
402          * Set the message to display using the given resource id.
403          *
404          * @return This Builder object to allow for chaining of calls to set methods
405          */
setMessage(@tringRes int messageId)406         public Builder setMessage(@StringRes int messageId) {
407             P.mMessage = P.mContext.getText(messageId);
408             return this;
409         }
410 
411         /**
412          * Set the message to display.
413          *
414          * @return This Builder object to allow for chaining of calls to set methods
415          */
setMessage(@ullable CharSequence message)416         public Builder setMessage(@Nullable CharSequence message) {
417             P.mMessage = message;
418             return this;
419         }
420 
421         /**
422          * Set the resource id of the {@link Drawable} to be used in the title.
423          * <p>
424          * Takes precedence over values set using {@link #setIcon(Drawable)}.
425          *
426          * @return This Builder object to allow for chaining of calls to set methods
427          */
setIcon(@rawableRes int iconId)428         public Builder setIcon(@DrawableRes int iconId) {
429             P.mIconId = iconId;
430             return this;
431         }
432 
433         /**
434          * Set the {@link Drawable} to be used in the title.
435          * <p>
436          * <strong>Note:</strong> To ensure consistent styling, the drawable
437          * should be inflated or constructed using the alert dialog's themed
438          * context obtained via {@link #getContext()}.
439          *
440          * @return this Builder object to allow for chaining of calls to set
441          *         methods
442          */
setIcon(@ullable Drawable icon)443         public Builder setIcon(@Nullable Drawable icon) {
444             P.mIcon = icon;
445             return this;
446         }
447 
448         /**
449          * Set an icon as supplied by a theme attribute. e.g.
450          * {@link android.R.attr#alertDialogIcon}.
451          * <p>
452          * Takes precedence over values set using {@link #setIcon(int)} or
453          * {@link #setIcon(Drawable)}.
454          *
455          * @param attrId ID of a theme attribute that points to a drawable resource.
456          */
setIconAttribute(@ttrRes int attrId)457         public Builder setIconAttribute(@AttrRes int attrId) {
458             TypedValue out = new TypedValue();
459             P.mContext.getTheme().resolveAttribute(attrId, out, true);
460             P.mIconId = out.resourceId;
461             return this;
462         }
463 
464         /**
465          * Set a listener to be invoked when the positive button of the dialog is pressed.
466          * @param textId The resource id of the text to display in the positive button
467          * @param listener The {@link DialogInterface.OnClickListener} to use.
468          *
469          * @return This Builder object to allow for chaining of calls to set methods
470          */
setPositiveButton(@tringRes int textId, final OnClickListener listener)471         public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {
472             P.mPositiveButtonText = P.mContext.getText(textId);
473             P.mPositiveButtonListener = listener;
474             return this;
475         }
476 
477         /**
478          * Set a listener to be invoked when the positive button of the dialog is pressed.
479          * @param text The text to display in the positive button
480          * @param listener The {@link DialogInterface.OnClickListener} to use.
481          *
482          * @return This Builder object to allow for chaining of calls to set methods
483          */
setPositiveButton(CharSequence text, final OnClickListener listener)484         public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {
485             P.mPositiveButtonText = text;
486             P.mPositiveButtonListener = listener;
487             return this;
488         }
489 
490         /**
491          * Set an icon to be displayed for the positive button.
492          * @param icon The icon to be displayed
493          * @return This Builder object to allow for chaining of calls to set methods
494          */
setPositiveButtonIcon(Drawable icon)495         public Builder setPositiveButtonIcon(Drawable icon) {
496             P.mPositiveButtonIcon = icon;
497             return this;
498         }
499 
500         /**
501          * Set a listener to be invoked when the negative button of the dialog is pressed.
502          * @param textId The resource id of the text to display in the negative button
503          * @param listener The {@link DialogInterface.OnClickListener} to use.
504          *
505          * @return This Builder object to allow for chaining of calls to set methods
506          */
setNegativeButton(@tringRes int textId, final OnClickListener listener)507         public Builder setNegativeButton(@StringRes int textId, final OnClickListener listener) {
508             P.mNegativeButtonText = P.mContext.getText(textId);
509             P.mNegativeButtonListener = listener;
510             return this;
511         }
512 
513         /**
514          * Set a listener to be invoked when the negative button of the dialog is pressed.
515          * @param text The text to display in the negative button
516          * @param listener The {@link DialogInterface.OnClickListener} to use.
517          *
518          * @return This Builder object to allow for chaining of calls to set methods
519          */
setNegativeButton(CharSequence text, final OnClickListener listener)520         public Builder setNegativeButton(CharSequence text, final OnClickListener listener) {
521             P.mNegativeButtonText = text;
522             P.mNegativeButtonListener = listener;
523             return this;
524         }
525 
526         /**
527          * Set an icon to be displayed for the negative button.
528          * @param icon The icon to be displayed
529          * @return This Builder object to allow for chaining of calls to set methods
530          */
setNegativeButtonIcon(Drawable icon)531         public Builder setNegativeButtonIcon(Drawable icon) {
532             P.mNegativeButtonIcon = icon;
533             return this;
534         }
535 
536         /**
537          * Set a listener to be invoked when the neutral button of the dialog is pressed.
538          * @param textId The resource id of the text to display in the neutral button
539          * @param listener The {@link DialogInterface.OnClickListener} to use.
540          *
541          * @return This Builder object to allow for chaining of calls to set methods
542          */
setNeutralButton(@tringRes int textId, final OnClickListener listener)543         public Builder setNeutralButton(@StringRes int textId, final OnClickListener listener) {
544             P.mNeutralButtonText = P.mContext.getText(textId);
545             P.mNeutralButtonListener = listener;
546             return this;
547         }
548 
549         /**
550          * Set a listener to be invoked when the neutral button of the dialog is pressed.
551          * @param text The text to display in the neutral button
552          * @param listener The {@link DialogInterface.OnClickListener} to use.
553          *
554          * @return This Builder object to allow for chaining of calls to set methods
555          */
setNeutralButton(CharSequence text, final OnClickListener listener)556         public Builder setNeutralButton(CharSequence text, final OnClickListener listener) {
557             P.mNeutralButtonText = text;
558             P.mNeutralButtonListener = listener;
559             return this;
560         }
561 
562         /**
563          * Set an icon to be displayed for the neutral button.
564          * @param icon The icon to be displayed
565          * @return This Builder object to allow for chaining of calls to set methods
566          */
setNeutralButtonIcon(Drawable icon)567         public Builder setNeutralButtonIcon(Drawable icon) {
568             P.mNeutralButtonIcon = icon;
569             return this;
570         }
571 
572         /**
573          * Sets whether the dialog is cancelable or not.  Default is true.
574          *
575          * @return This Builder object to allow for chaining of calls to set methods
576          */
setCancelable(boolean cancelable)577         public Builder setCancelable(boolean cancelable) {
578             P.mCancelable = cancelable;
579             return this;
580         }
581 
582         /**
583          * Sets the callback that will be called if the dialog is canceled.
584          *
585          * <p>Even in a cancelable dialog, the dialog may be dismissed for reasons other than
586          * being canceled or one of the supplied choices being selected.
587          * If you are interested in listening for all cases where the dialog is dismissed
588          * and not just when it is canceled, see
589          * {@link #setOnDismissListener(android.content.DialogInterface.OnDismissListener)
590          * setOnDismissListener}.</p>
591          *
592          * @return This Builder object to allow for chaining of calls to set methods
593          * @see #setCancelable(boolean)
594          * @see #setOnDismissListener(android.content.DialogInterface.OnDismissListener)
595          *
596          * @return This Builder object to allow for chaining of calls to set methods
597          */
setOnCancelListener(OnCancelListener onCancelListener)598         public Builder setOnCancelListener(OnCancelListener onCancelListener) {
599             P.mOnCancelListener = onCancelListener;
600             return this;
601         }
602 
603         /**
604          * Sets the callback that will be called when the dialog is dismissed for any reason.
605          *
606          * @return This Builder object to allow for chaining of calls to set methods
607          */
setOnDismissListener(OnDismissListener onDismissListener)608         public Builder setOnDismissListener(OnDismissListener onDismissListener) {
609             P.mOnDismissListener = onDismissListener;
610             return this;
611         }
612 
613         /**
614          * Sets the callback that will be called if a key is dispatched to the dialog.
615          *
616          * @return This Builder object to allow for chaining of calls to set methods
617          */
setOnKeyListener(OnKeyListener onKeyListener)618         public Builder setOnKeyListener(OnKeyListener onKeyListener) {
619             P.mOnKeyListener = onKeyListener;
620             return this;
621         }
622 
623         /**
624          * Set a list of items to be displayed in the dialog as the content, you will be notified of the
625          * selected item via the supplied listener. This should be an array type i.e. R.array.foo
626          *
627          * @return This Builder object to allow for chaining of calls to set methods
628          */
setItems(@rrayRes int itemsId, final OnClickListener listener)629         public Builder setItems(@ArrayRes int itemsId, final OnClickListener listener) {
630             P.mItems = P.mContext.getResources().getTextArray(itemsId);
631             P.mOnClickListener = listener;
632             return this;
633         }
634 
635         /**
636          * Set a list of items to be displayed in the dialog as the content, you will be notified of the
637          * selected item via the supplied listener.
638          *
639          * @return This Builder object to allow for chaining of calls to set methods
640          */
setItems(CharSequence[] items, final OnClickListener listener)641         public Builder setItems(CharSequence[] items, final OnClickListener listener) {
642             P.mItems = items;
643             P.mOnClickListener = listener;
644             return this;
645         }
646 
647         /**
648          * Set a list of items, which are supplied by the given {@link ListAdapter}, to be
649          * displayed in the dialog as the content, you will be notified of the
650          * selected item via the supplied listener.
651          *
652          * @param adapter The {@link ListAdapter} to supply the list of items
653          * @param listener The listener that will be called when an item is clicked.
654          *
655          * @return This Builder object to allow for chaining of calls to set methods
656          */
setAdapter(final ListAdapter adapter, final OnClickListener listener)657         public Builder setAdapter(final ListAdapter adapter, final OnClickListener listener) {
658             P.mAdapter = adapter;
659             P.mOnClickListener = listener;
660             return this;
661         }
662 
663         /**
664          * Set a list of items, which are supplied by the given {@link Cursor}, to be
665          * displayed in the dialog as the content, you will be notified of the
666          * selected item via the supplied listener.
667          *
668          * @param cursor The {@link Cursor} to supply the list of items
669          * @param listener The listener that will be called when an item is clicked.
670          * @param labelColumn The column name on the cursor containing the string to display
671          *          in the label.
672          *
673          * @return This Builder object to allow for chaining of calls to set methods
674          */
setCursor(final Cursor cursor, final OnClickListener listener, String labelColumn)675         public Builder setCursor(final Cursor cursor, final OnClickListener listener,
676                 String labelColumn) {
677             P.mCursor = cursor;
678             P.mLabelColumn = labelColumn;
679             P.mOnClickListener = listener;
680             return this;
681         }
682 
683         /**
684          * Set a list of items to be displayed in the dialog as the content,
685          * you will be notified of the selected item via the supplied listener.
686          * This should be an array type, e.g. R.array.foo. The list will have
687          * a check mark displayed to the right of the text for each checked
688          * item. Clicking on an item in the list will not dismiss the dialog.
689          * Clicking on a button will dismiss the dialog.
690          *
691          * @param itemsId the resource id of an array i.e. R.array.foo
692          * @param checkedItems specifies which items are checked. It should be null in which case no
693          *        items are checked. If non null it must be exactly the same length as the array of
694          *        items.
695          * @param listener notified when an item on the list is clicked. The dialog will not be
696          *        dismissed when an item is clicked. It will only be dismissed if clicked on a
697          *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
698          *
699          * @return This Builder object to allow for chaining of calls to set methods
700          */
setMultiChoiceItems(@rrayRes int itemsId, boolean[] checkedItems, final OnMultiChoiceClickListener listener)701         public Builder setMultiChoiceItems(@ArrayRes int itemsId, boolean[] checkedItems,
702                 final OnMultiChoiceClickListener listener) {
703             P.mItems = P.mContext.getResources().getTextArray(itemsId);
704             P.mOnCheckboxClickListener = listener;
705             P.mCheckedItems = checkedItems;
706             P.mIsMultiChoice = true;
707             return this;
708         }
709 
710         /**
711          * Set a list of items to be displayed in the dialog as the content,
712          * you will be notified of the selected item via the supplied listener.
713          * The list will have a check mark displayed to the right of the text
714          * for each checked item. Clicking on an item in the list will not
715          * dismiss the dialog. Clicking on a button will dismiss the dialog.
716          *
717          * @param items the text of the items to be displayed in the list.
718          * @param checkedItems specifies which items are checked. It should be null in which case no
719          *        items are checked. If non null it must be exactly the same length as the array of
720          *        items.
721          * @param listener notified when an item on the list is clicked. The dialog will not be
722          *        dismissed when an item is clicked. It will only be dismissed if clicked on a
723          *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
724          *
725          * @return This Builder object to allow for chaining of calls to set methods
726          */
setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems, final OnMultiChoiceClickListener listener)727         public Builder setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems,
728                 final OnMultiChoiceClickListener listener) {
729             P.mItems = items;
730             P.mOnCheckboxClickListener = listener;
731             P.mCheckedItems = checkedItems;
732             P.mIsMultiChoice = true;
733             return this;
734         }
735 
736         /**
737          * Set a list of items to be displayed in the dialog as the content,
738          * you will be notified of the selected item via the supplied listener.
739          * The list will have a check mark displayed to the right of the text
740          * for each checked item. Clicking on an item in the list will not
741          * dismiss the dialog. Clicking on a button will dismiss the dialog.
742          *
743          * @param cursor the cursor used to provide the items.
744          * @param isCheckedColumn specifies the column name on the cursor to use to determine
745          *        whether a checkbox is checked or not. It must return an integer value where 1
746          *        means checked and 0 means unchecked.
747          * @param labelColumn The column name on the cursor containing the string to display in the
748          *        label.
749          * @param listener notified when an item on the list is clicked. The dialog will not be
750          *        dismissed when an item is clicked. It will only be dismissed if clicked on a
751          *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
752          *
753          * @return This Builder object to allow for chaining of calls to set methods
754          */
setMultiChoiceItems(Cursor cursor, String isCheckedColumn, String labelColumn, final OnMultiChoiceClickListener listener)755         public Builder setMultiChoiceItems(Cursor cursor, String isCheckedColumn, String labelColumn,
756                 final OnMultiChoiceClickListener listener) {
757             P.mCursor = cursor;
758             P.mOnCheckboxClickListener = listener;
759             P.mIsCheckedColumn = isCheckedColumn;
760             P.mLabelColumn = labelColumn;
761             P.mIsMultiChoice = true;
762             return this;
763         }
764 
765         /**
766          * Set a list of items to be displayed in the dialog as the content, you will be notified of
767          * the selected item via the supplied listener. This should be an array type i.e.
768          * R.array.foo The list will have a check mark displayed to the right of the text for the
769          * checked item. Clicking on an item in the list will not dismiss the dialog. Clicking on a
770          * button will dismiss the dialog.
771          *
772          * @param itemsId the resource id of an array i.e. R.array.foo
773          * @param checkedItem specifies which item is checked. If -1 no items are checked.
774          * @param listener notified when an item on the list is clicked. The dialog will not be
775          *        dismissed when an item is clicked. It will only be dismissed if clicked on a
776          *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
777          *
778          * @return This Builder object to allow for chaining of calls to set methods
779          */
setSingleChoiceItems(@rrayRes int itemsId, int checkedItem, final OnClickListener listener)780         public Builder setSingleChoiceItems(@ArrayRes int itemsId, int checkedItem,
781                 final OnClickListener listener) {
782             P.mItems = P.mContext.getResources().getTextArray(itemsId);
783             P.mOnClickListener = listener;
784             P.mCheckedItem = checkedItem;
785             P.mIsSingleChoice = true;
786             return this;
787         }
788 
789         /**
790          * Set a list of items to be displayed in the dialog as the content, you will be notified of
791          * the selected item via the supplied listener. The list will have a check mark displayed to
792          * the right of the text for the checked item. Clicking on an item in the list will not
793          * dismiss the dialog. Clicking on a button will dismiss the dialog.
794          *
795          * @param cursor the cursor to retrieve the items from.
796          * @param checkedItem specifies which item is checked. If -1 no items are checked.
797          * @param labelColumn The column name on the cursor containing the string to display in the
798          *        label.
799          * @param listener notified when an item on the list is clicked. The dialog will not be
800          *        dismissed when an item is clicked. It will only be dismissed if clicked on a
801          *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
802          *
803          * @return This Builder object to allow for chaining of calls to set methods
804          */
setSingleChoiceItems(Cursor cursor, int checkedItem, String labelColumn, final OnClickListener listener)805         public Builder setSingleChoiceItems(Cursor cursor, int checkedItem, String labelColumn,
806                 final OnClickListener listener) {
807             P.mCursor = cursor;
808             P.mOnClickListener = listener;
809             P.mCheckedItem = checkedItem;
810             P.mLabelColumn = labelColumn;
811             P.mIsSingleChoice = true;
812             return this;
813         }
814 
815         /**
816          * Set a list of items to be displayed in the dialog as the content, you will be notified of
817          * the selected item via the supplied listener. The list will have a check mark displayed to
818          * the right of the text for the checked item. Clicking on an item in the list will not
819          * dismiss the dialog. Clicking on a button will dismiss the dialog.
820          *
821          * @param items the items to be displayed.
822          * @param checkedItem specifies which item is checked. If -1 no items are checked.
823          * @param listener notified when an item on the list is clicked. The dialog will not be
824          *        dismissed when an item is clicked. It will only be dismissed if clicked on a
825          *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
826          *
827          * @return This Builder object to allow for chaining of calls to set methods
828          */
setSingleChoiceItems(CharSequence[] items, int checkedItem, final OnClickListener listener)829         public Builder setSingleChoiceItems(CharSequence[] items, int checkedItem, final OnClickListener listener) {
830             P.mItems = items;
831             P.mOnClickListener = listener;
832             P.mCheckedItem = checkedItem;
833             P.mIsSingleChoice = true;
834             return this;
835         }
836 
837         /**
838          * Set a list of items to be displayed in the dialog as the content, you will be notified of
839          * the selected item via the supplied listener. The list will have a check mark displayed to
840          * the right of the text for the checked item. Clicking on an item in the list will not
841          * dismiss the dialog. Clicking on a button will dismiss the dialog.
842          *
843          * @param adapter The {@link ListAdapter} to supply the list of items
844          * @param checkedItem specifies which item is checked. If -1 no items are checked.
845          * @param listener notified when an item on the list is clicked. The dialog will not be
846          *        dismissed when an item is clicked. It will only be dismissed if clicked on a
847          *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
848          *
849          * @return This Builder object to allow for chaining of calls to set methods
850          */
setSingleChoiceItems(ListAdapter adapter, int checkedItem, final OnClickListener listener)851         public Builder setSingleChoiceItems(ListAdapter adapter, int checkedItem, final OnClickListener listener) {
852             P.mAdapter = adapter;
853             P.mOnClickListener = listener;
854             P.mCheckedItem = checkedItem;
855             P.mIsSingleChoice = true;
856             return this;
857         }
858 
859         /**
860          * Sets a listener to be invoked when an item in the list is selected.
861          *
862          * @param listener the listener to be invoked
863          * @return this Builder object to allow for chaining of calls to set methods
864          * @see AdapterView#setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)
865          */
setOnItemSelectedListener(final AdapterView.OnItemSelectedListener listener)866         public Builder setOnItemSelectedListener(final AdapterView.OnItemSelectedListener listener) {
867             P.mOnItemSelectedListener = listener;
868             return this;
869         }
870 
871         /**
872          * Inflates a custom view resource inside the dialog, below the message and above the
873          * buttons. If none of the other parts of the dialog are made visible, the custom view
874          * occupies the entire view. Because the view is inflated inside the parent dialog's
875          * view, take care not to define conflicting resource IDs.
876          *
877          * @param layoutResId Resource ID to be inflated.
878          * @return this Builder object to allow for chaining of calls to set
879          *         methods
880          */
setView(int layoutResId)881         public Builder setView(int layoutResId) {
882             P.mView = null;
883             P.mViewLayoutResId = layoutResId;
884             P.mViewSpacingSpecified = false;
885             return this;
886         }
887 
888         /**
889          * Sets a custom view to be the contents of the alert dialog.
890          * <p>
891          * When using a pre-Holo theme, if the supplied view is an instance of
892          * a {@link ListView} then the light background will be used.
893          * <p>
894          * <strong>Note:</strong> To ensure consistent styling, the custom view
895          * should be inflated or constructed using the alert dialog's themed
896          * context obtained via {@link #getContext()}.
897          *
898          * @param view the view to use as the contents of the alert dialog
899          * @return this Builder object to allow for chaining of calls to set
900          *         methods
901          */
setView(View view)902         public Builder setView(View view) {
903             P.mView = view;
904             P.mViewLayoutResId = 0;
905             P.mViewSpacingSpecified = false;
906             return this;
907         }
908 
909         /**
910          * Set a custom view to be the contents of the Dialog, specifying the
911          * spacing to appear around that view. If the supplied view is an
912          * instance of a {@link ListView} the light background will be used.
913          *
914          * @param view              The view to use as the contents of the Dialog.
915          * @param viewSpacingLeft   Spacing between the left edge of the view and
916          *                          the dialog frame
917          * @param viewSpacingTop    Spacing between the top edge of the view and
918          *                          the dialog frame
919          * @param viewSpacingRight  Spacing between the right edge of the view
920          *                          and the dialog frame
921          * @param viewSpacingBottom Spacing between the bottom edge of the view
922          *                          and the dialog frame
923          * @return This Builder object to allow for chaining of calls to set
924          * methods
925          *
926          *
927          * This is currently hidden because it seems like people should just
928          * be able to put padding around the view.
929          * @deprecated This method has been deprecated.
930          */
931         @RestrictTo(LIBRARY_GROUP_PREFIX)
932         @Deprecated
setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight, int viewSpacingBottom)933         public Builder setView(View view, int viewSpacingLeft, int viewSpacingTop,
934                 int viewSpacingRight, int viewSpacingBottom) {
935             P.mView = view;
936             P.mViewLayoutResId = 0;
937             P.mViewSpacingSpecified = true;
938             P.mViewSpacingLeft = viewSpacingLeft;
939             P.mViewSpacingTop = viewSpacingTop;
940             P.mViewSpacingRight = viewSpacingRight;
941             P.mViewSpacingBottom = viewSpacingBottom;
942             return this;
943         }
944 
945         /**
946          * Sets the Dialog to use the inverse background, regardless of what the
947          * contents is.
948          *
949          * @param useInverseBackground Whether to use the inverse background
950          * @return This Builder object to allow for chaining of calls to set methods
951          * @deprecated This flag is only used for pre-Material themes. Instead,
952          *             specify the window background using on the alert dialog
953          *             theme.
954          */
955         @Deprecated
setInverseBackgroundForced(boolean useInverseBackground)956         public Builder setInverseBackgroundForced(boolean useInverseBackground) {
957             P.mForceInverseBackground = useInverseBackground;
958             return this;
959         }
960 
961         /**
962          */
963         @RestrictTo(LIBRARY_GROUP_PREFIX)
setRecycleOnMeasureEnabled(boolean enabled)964         public Builder setRecycleOnMeasureEnabled(boolean enabled) {
965             P.mRecycleOnMeasure = enabled;
966             return this;
967         }
968 
969 
970         /**
971          * Creates an {@link AlertDialog} with the arguments supplied to this
972          * builder.
973          * <p>
974          * Calling this method does not display the dialog. If no additional
975          * processing is needed, {@link #show()} may be called instead to both
976          * create and display the dialog.
977          */
create()978         public @NonNull AlertDialog create() {
979             // We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
980             // so we always have to re-set the theme
981             final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
982             P.apply(dialog.mAlert);
983             dialog.setCancelable(P.mCancelable);
984             if (P.mCancelable) {
985                 dialog.setCanceledOnTouchOutside(true);
986             }
987             dialog.setOnCancelListener(P.mOnCancelListener);
988             dialog.setOnDismissListener(P.mOnDismissListener);
989             if (P.mOnKeyListener != null) {
990                 dialog.setOnKeyListener(P.mOnKeyListener);
991             }
992             return dialog;
993         }
994 
995         /**
996          * Creates an {@link AlertDialog} with the arguments supplied to this
997          * builder and immediately displays the dialog.
998          * <p>
999          * Calling this method is functionally identical to:
1000          * <pre>
1001          *     AlertDialog dialog = builder.create();
1002          *     dialog.show();
1003          * </pre>
1004          */
show()1005         public AlertDialog show() {
1006             final AlertDialog dialog = create();
1007             dialog.show();
1008             return dialog;
1009         }
1010     }
1011 
1012 }
1013