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