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