1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.car.ui;
17 
18 import android.app.AlertDialog;
19 import android.app.Dialog;
20 import android.content.Context;
21 import android.content.DialogInterface;
22 import android.database.Cursor;
23 import android.graphics.drawable.Drawable;
24 import android.text.InputFilter;
25 import android.text.TextWatcher;
26 import android.view.LayoutInflater;
27 import android.view.View;
28 import android.widget.AdapterView;
29 import android.widget.EditText;
30 import android.widget.ImageView;
31 import android.widget.ListAdapter;
32 import android.widget.TextView;
33 
34 import androidx.annotation.ArrayRes;
35 import androidx.annotation.AttrRes;
36 import androidx.annotation.DrawableRes;
37 import androidx.annotation.NonNull;
38 import androidx.annotation.StringRes;
39 import androidx.recyclerview.widget.LinearLayoutManager;
40 import androidx.recyclerview.widget.RecyclerView;
41 
42 import com.android.car.ui.recyclerview.CarUiListItemAdapter;
43 import com.android.car.ui.recyclerview.CarUiRadioButtonListItemAdapter;
44 
45 /**
46  * Wrapper for AlertDialog.Builder
47  */
48 public class AlertDialogBuilder {
49 
50     private AlertDialog.Builder mBuilder;
51     private Context mContext;
52     private boolean mPositiveButtonSet;
53     private boolean mNeutralButtonSet;
54     private boolean mNegativeButtonSet;
55     private CharSequence mTitle;
56     private CharSequence mSubtitle;
57     private Drawable mIcon;
58 
AlertDialogBuilder(Context context)59     public AlertDialogBuilder(Context context) {
60         // Resource id specified as 0 uses the parent contexts resolved value for alertDialogTheme.
61         this(context, /* themeResId= */0);
62     }
63 
AlertDialogBuilder(Context context, int themeResId)64     public AlertDialogBuilder(Context context, int themeResId) {
65         mBuilder = new AlertDialog.Builder(context, themeResId);
66         mContext = context;
67     }
68 
getContext()69     public Context getContext() {
70         return mBuilder.getContext();
71     }
72 
73     /**
74      * Set the title using the given resource id.
75      *
76      * @return This Builder object to allow for chaining of calls to set methods
77      */
setTitle(@tringRes int titleId)78     public AlertDialogBuilder setTitle(@StringRes int titleId) {
79         return setTitle(mContext.getText(titleId));
80     }
81 
82     /**
83      * Set the title displayed in the {@link Dialog}.
84      *
85      * @return This Builder object to allow for chaining of calls to set methods
86      */
setTitle(CharSequence title)87     public AlertDialogBuilder setTitle(CharSequence title) {
88         mTitle = title;
89         mBuilder.setTitle(title);
90         return this;
91     }
92 
93     /**
94      * Sets a subtitle to be displayed in the {@link Dialog}.
95      *
96      * @return This Builder object to allow for chaining of calls to set methods
97      */
setSubtitle(@tringRes int subtitle)98     public AlertDialogBuilder setSubtitle(@StringRes int subtitle) {
99         return setSubtitle(mContext.getString(subtitle));
100     }
101 
102     /**
103      * Sets a subtitle to be displayed in the {@link Dialog}.
104      *
105      * @return This Builder object to allow for chaining of calls to set methods
106      */
setSubtitle(CharSequence subtitle)107     public AlertDialogBuilder setSubtitle(CharSequence subtitle) {
108         mSubtitle = subtitle;
109         return this;
110     }
111 
112     /**
113      * Set the message to display using the given resource id.
114      *
115      * @return This Builder object to allow for chaining of calls to set methods
116      */
setMessage(@tringRes int messageId)117     public AlertDialogBuilder setMessage(@StringRes int messageId) {
118         mBuilder.setMessage(messageId);
119         return this;
120     }
121 
122     /**
123      * Set the message to display.
124      *
125      * @return This Builder object to allow for chaining of calls to set methods
126      */
setMessage(CharSequence message)127     public AlertDialogBuilder setMessage(CharSequence message) {
128         mBuilder.setMessage(message);
129         return this;
130     }
131 
132     /**
133      * Set the resource id of the {@link Drawable} to be used in the title.
134      * <p>
135      * Takes precedence over values set using {@link #setIcon(Drawable)}.
136      *
137      * @return This Builder object to allow for chaining of calls to set methods
138      */
setIcon(@rawableRes int iconId)139     public AlertDialogBuilder setIcon(@DrawableRes int iconId) {
140         return setIcon(mContext.getDrawable(iconId));
141     }
142 
143     /**
144      * Set the {@link Drawable} to be used in the title.
145      * <p>
146      * <strong>Note:</strong> To ensure consistent styling, the drawable
147      * should be inflated or constructed using the alert dialog's themed
148      * context obtained via {@link #getContext()}.
149      *
150      * @return this Builder object to allow for chaining of calls to set
151      * methods
152      */
setIcon(Drawable icon)153     public AlertDialogBuilder setIcon(Drawable icon) {
154         mIcon = icon;
155         mBuilder.setIcon(icon);
156         return this;
157     }
158 
159     /**
160      * Set an icon as supplied by a theme attribute. e.g.
161      * {@link android.R.attr#alertDialogIcon}.
162      * <p>
163      * Takes precedence over values set using {@link #setIcon(int)} or
164      * {@link #setIcon(Drawable)}.
165      *
166      * @param attrId ID of a theme attribute that points to a drawable resource.
167      */
setIconAttribute(@ttrRes int attrId)168     public AlertDialogBuilder setIconAttribute(@AttrRes int attrId) {
169         mBuilder.setIconAttribute(attrId);
170         return this;
171     }
172 
173     /**
174      * Set a listener to be invoked when the positive button of the dialog is pressed.
175      *
176      * @param textId The resource id of the text to display in the positive button
177      * @param listener The {@link DialogInterface.OnClickListener} to use.
178      * @return This Builder object to allow for chaining of calls to set methods
179      */
setPositiveButton(@tringRes int textId, final DialogInterface.OnClickListener listener)180     public AlertDialogBuilder setPositiveButton(@StringRes int textId,
181             final DialogInterface.OnClickListener listener) {
182         mBuilder.setPositiveButton(textId, listener);
183         mPositiveButtonSet = true;
184         return this;
185     }
186 
187     /**
188      * Set a listener to be invoked when the positive button of the dialog is pressed.
189      *
190      * @param text The text to display in the positive button
191      * @param listener The {@link DialogInterface.OnClickListener} to use.
192      * @return This Builder object to allow for chaining of calls to set methods
193      */
setPositiveButton(CharSequence text, final DialogInterface.OnClickListener listener)194     public AlertDialogBuilder setPositiveButton(CharSequence text,
195             final DialogInterface.OnClickListener listener) {
196         mBuilder.setPositiveButton(text, listener);
197         mPositiveButtonSet = true;
198         return this;
199     }
200 
201     /**
202      * Set a listener to be invoked when the negative button of the dialog is pressed.
203      *
204      * @param textId The resource id of the text to display in the negative button
205      * @param listener The {@link DialogInterface.OnClickListener} to use.
206      * @return This Builder object to allow for chaining of calls to set methods
207      */
setNegativeButton(@tringRes int textId, final DialogInterface.OnClickListener listener)208     public AlertDialogBuilder setNegativeButton(@StringRes int textId,
209             final DialogInterface.OnClickListener listener) {
210         mBuilder.setNegativeButton(textId, listener);
211         mNegativeButtonSet = true;
212         return this;
213     }
214 
215     /**
216      * Set a listener to be invoked when the negative button of the dialog is pressed.
217      *
218      * @param text The text to display in the negative button
219      * @param listener The {@link DialogInterface.OnClickListener} to use.
220      * @return This Builder object to allow for chaining of calls to set methods
221      */
setNegativeButton(CharSequence text, final DialogInterface.OnClickListener listener)222     public AlertDialogBuilder setNegativeButton(CharSequence text,
223             final DialogInterface.OnClickListener listener) {
224         mBuilder.setNegativeButton(text, listener);
225         mNegativeButtonSet = true;
226         return this;
227     }
228 
229     /**
230      * Set a listener to be invoked when the neutral button of the dialog is pressed.
231      *
232      * @param textId The resource id of the text to display in the neutral button
233      * @param listener The {@link DialogInterface.OnClickListener} to use.
234      * @return This Builder object to allow for chaining of calls to set methods
235      */
setNeutralButton(@tringRes int textId, final DialogInterface.OnClickListener listener)236     public AlertDialogBuilder setNeutralButton(@StringRes int textId,
237             final DialogInterface.OnClickListener listener) {
238         mBuilder.setNeutralButton(textId, listener);
239         mNeutralButtonSet = true;
240         return this;
241     }
242 
243     /**
244      * Set a listener to be invoked when the neutral button of the dialog is pressed.
245      *
246      * @param text The text to display in the neutral button
247      * @param listener The {@link DialogInterface.OnClickListener} to use.
248      * @return This Builder object to allow for chaining of calls to set methods
249      */
setNeutralButton(CharSequence text, final DialogInterface.OnClickListener listener)250     public AlertDialogBuilder setNeutralButton(CharSequence text,
251             final DialogInterface.OnClickListener listener) {
252         mBuilder.setNeutralButton(text, listener);
253         mNeutralButtonSet = true;
254         return this;
255     }
256 
257     /**
258      * Sets whether the dialog is cancelable or not.  Default is true.
259      *
260      * @return This Builder object to allow for chaining of calls to set methods
261      */
setCancelable(boolean cancelable)262     public AlertDialogBuilder setCancelable(boolean cancelable) {
263         mBuilder.setCancelable(cancelable);
264         return this;
265     }
266 
267     /**
268      * Sets the callback that will be called if the dialog is canceled.
269      *
270      * <p>Even in a cancelable dialog, the dialog may be dismissed for reasons other than
271      * being canceled or one of the supplied choices being selected.
272      * If you are interested in listening for all cases where the dialog is dismissed
273      * and not just when it is canceled, see
274      * {@link #setOnDismissListener(android.content.DialogInterface.OnDismissListener)
275      * setOnDismissListener}.</p>
276      *
277      * @return This Builder object to allow for chaining of calls to set methods
278      * @see #setCancelable(boolean)
279      * @see #setOnDismissListener(android.content.DialogInterface.OnDismissListener)
280      */
setOnCancelListener( DialogInterface.OnCancelListener onCancelListener)281     public AlertDialogBuilder setOnCancelListener(
282             DialogInterface.OnCancelListener onCancelListener) {
283         mBuilder.setOnCancelListener(onCancelListener);
284         return this;
285     }
286 
287     /**
288      * Sets the callback that will be called when the dialog is dismissed for any reason.
289      *
290      * @return This Builder object to allow for chaining of calls to set methods
291      */
setOnDismissListener( DialogInterface.OnDismissListener onDismissListener)292     public AlertDialogBuilder setOnDismissListener(
293             DialogInterface.OnDismissListener onDismissListener) {
294         mBuilder.setOnDismissListener(onDismissListener);
295         return this;
296     }
297 
298     /**
299      * Sets the callback that will be called if a key is dispatched to the dialog.
300      *
301      * @return This Builder object to allow for chaining of calls to set methods
302      */
setOnKeyListener(DialogInterface.OnKeyListener onKeyListener)303     public AlertDialogBuilder setOnKeyListener(DialogInterface.OnKeyListener onKeyListener) {
304         mBuilder.setOnKeyListener(onKeyListener);
305         return this;
306     }
307 
308     /**
309      * Set a list of items to be displayed in the dialog as the content, you will be notified of the
310      * selected item via the supplied listener. This should be an array type i.e. R.array.foo
311      *
312      * @return This Builder object to allow for chaining of calls to set methods
313      */
setItems(@rrayRes int itemsId, final DialogInterface.OnClickListener listener)314     public AlertDialogBuilder setItems(@ArrayRes int itemsId,
315             final DialogInterface.OnClickListener listener) {
316         mBuilder.setItems(itemsId, listener);
317         return this;
318     }
319 
320     /**
321      * Set a list of items to be displayed in the dialog as the content, you will be notified of the
322      * selected item via the supplied listener.
323      *
324      * @return This Builder object to allow for chaining of calls to set methods
325      */
setItems(CharSequence[] items, final DialogInterface.OnClickListener listener)326     public AlertDialogBuilder setItems(CharSequence[] items,
327             final DialogInterface.OnClickListener listener) {
328         mBuilder.setItems(items, listener);
329         return this;
330     }
331 
332     /**
333      * This was not supposed to be in the Chassis API because it allows custom views.
334      *
335      * @deprecated Use {@link #setAdapter(CarUiListItemAdapter)} instead.
336      */
337     @Deprecated
setAdapter(final ListAdapter adapter, final DialogInterface.OnClickListener listener)338     public AlertDialogBuilder setAdapter(final ListAdapter adapter,
339             final DialogInterface.OnClickListener listener) {
340         mBuilder.setAdapter(adapter, listener);
341         return this;
342     }
343 
344     /**
345      * Display all the {@link com.android.car.ui.recyclerview.CarUiListItem CarUiListItems} in a
346      * {@link CarUiListItemAdapter}. You should set click listeners on the CarUiListItems as
347      * opposed to a callback in this function.
348      */
setAdapter(final CarUiListItemAdapter adapter)349     public AlertDialogBuilder setAdapter(final CarUiListItemAdapter adapter) {
350         setCustomList(adapter);
351         return this;
352     }
353 
setCustomList(@onNull CarUiListItemAdapter adapter)354     private void setCustomList(@NonNull CarUiListItemAdapter adapter) {
355         View customList = LayoutInflater.from(mContext).inflate(
356                 R.layout.car_ui_alert_dialog_list, null);
357         RecyclerView mList = customList.requireViewById(R.id.list);
358         mList.setLayoutManager(new LinearLayoutManager(mContext));
359         mList.setAdapter(adapter);
360         mBuilder.setView(customList);
361     }
362 
363     /**
364      * Set a list of items, which are supplied by the given {@link Cursor}, to be
365      * displayed in the dialog as the content, you will be notified of the
366      * selected item via the supplied listener.
367      *
368      * @param cursor The {@link Cursor} to supply the list of items
369      * @param listener The listener that will be called when an item is clicked.
370      * @param labelColumn The column name on the cursor containing the string to display
371      * in the label.
372      * @return This Builder object to allow for chaining of calls to set methods
373      */
setCursor(final Cursor cursor, final DialogInterface.OnClickListener listener, String labelColumn)374     public AlertDialogBuilder setCursor(final Cursor cursor,
375             final DialogInterface.OnClickListener listener,
376             String labelColumn) {
377         mBuilder.setCursor(cursor, listener, labelColumn);
378         return this;
379     }
380 
381     /**
382      * Set a list of items to be displayed in the dialog as the content,
383      * you will be notified of the selected item via the supplied listener.
384      * This should be an array type, e.g. R.array.foo. The list will have
385      * a check mark displayed to the right of the text for each checked
386      * item. Clicking on an item in the list will not dismiss the dialog.
387      * Clicking on a button will dismiss the dialog.
388      *
389      * @param itemsId the resource id of an array i.e. R.array.foo
390      * @param checkedItems specifies which items are checked. It should be null in which case no
391      * items are checked. If non null it must be exactly the same length as the array of
392      * items.
393      * @param listener notified when an item on the list is clicked. The dialog will not be
394      * dismissed when an item is clicked. It will only be dismissed if clicked on a
395      * button, if no buttons are supplied it's up to the user to dismiss the dialog.
396      * @return This Builder object to allow for chaining of calls to set methods
397      */
setMultiChoiceItems(@rrayRes int itemsId, boolean[] checkedItems, final DialogInterface.OnMultiChoiceClickListener listener)398     public AlertDialogBuilder setMultiChoiceItems(@ArrayRes int itemsId, boolean[] checkedItems,
399             final DialogInterface.OnMultiChoiceClickListener listener) {
400         mBuilder.setMultiChoiceItems(itemsId, checkedItems, listener);
401         return this;
402     }
403 
404     /**
405      * Set a list of items to be displayed in the dialog as the content,
406      * you will be notified of the selected item via the supplied listener.
407      * The list will have a check mark displayed to the right of the text
408      * for each checked item. Clicking on an item in the list will not
409      * dismiss the dialog. Clicking on a button will dismiss the dialog.
410      *
411      * @param items the text of the items to be displayed in the list.
412      * @param checkedItems specifies which items are checked. It should be null in which case no
413      * items are checked. If non null it must be exactly the same length as the array of
414      * items.
415      * @param listener notified when an item on the list is clicked. The dialog will not be
416      * dismissed when an item is clicked. It will only be dismissed if clicked on a
417      * button, if no buttons are supplied it's up to the user to dismiss the dialog.
418      * @return This Builder object to allow for chaining of calls to set methods
419      */
setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems, final DialogInterface.OnMultiChoiceClickListener listener)420     public AlertDialogBuilder setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems,
421             final DialogInterface.OnMultiChoiceClickListener listener) {
422         mBuilder.setMultiChoiceItems(items, checkedItems, listener);
423         return this;
424     }
425 
426     /**
427      * Set a list of items to be displayed in the dialog as the content,
428      * you will be notified of the selected item via the supplied listener.
429      * The list will have a check mark displayed to the right of the text
430      * for each checked item. Clicking on an item in the list will not
431      * dismiss the dialog. Clicking on a button will dismiss the dialog.
432      *
433      * @param cursor the cursor used to provide the items.
434      * @param isCheckedColumn specifies the column name on the cursor to use to determine
435      * whether a checkbox is checked or not. It must return an integer value where 1
436      * means checked and 0 means unchecked.
437      * @param labelColumn The column name on the cursor containing the string to display in the
438      * label.
439      * @param listener notified when an item on the list is clicked. The dialog will not be
440      * dismissed when an item is clicked. It will only be dismissed if clicked on a
441      * button, if no buttons are supplied it's up to the user to dismiss the dialog.
442      * @return This Builder object to allow for chaining of calls to set methods
443      */
setMultiChoiceItems(Cursor cursor, String isCheckedColumn, String labelColumn, final DialogInterface.OnMultiChoiceClickListener listener)444     public AlertDialogBuilder setMultiChoiceItems(Cursor cursor, String isCheckedColumn,
445             String labelColumn,
446             final DialogInterface.OnMultiChoiceClickListener listener) {
447         mBuilder.setMultiChoiceItems(cursor, isCheckedColumn, labelColumn, listener);
448         return this;
449     }
450 
451     /**
452      * Set a list of items to be displayed in the dialog as the content, you will be notified of
453      * the selected item via the supplied listener. This should be an array type i.e.
454      * R.array.foo The list will have a check mark displayed to the right of the text for the
455      * checked item. Clicking on an item in the list will not dismiss the dialog. Clicking on a
456      * button will dismiss the dialog.
457      *
458      * @param itemsId the resource id of an array i.e. R.array.foo
459      * @param checkedItem specifies which item is checked. If -1 no items are checked.
460      * @param listener notified when an item on the list is clicked. The dialog will not be
461      * dismissed when an item is clicked. It will only be dismissed if clicked on a
462      * button, if no buttons are supplied it's up to the user to dismiss the dialog.
463      * @return This Builder object to allow for chaining of calls to set methods
464      */
setSingleChoiceItems(@rrayRes int itemsId, int checkedItem, final DialogInterface.OnClickListener listener)465     public AlertDialogBuilder setSingleChoiceItems(@ArrayRes int itemsId, int checkedItem,
466             final DialogInterface.OnClickListener listener) {
467         mBuilder.setSingleChoiceItems(itemsId, checkedItem, listener);
468         return this;
469     }
470 
471     /**
472      * Set a list of items to be displayed in the dialog as the content, you will be notified of
473      * the selected item via the supplied listener. The list will have a check mark displayed to
474      * the right of the text for the checked item. Clicking on an item in the list will not
475      * dismiss the dialog. Clicking on a button will dismiss the dialog.
476      *
477      * @param cursor the cursor to retrieve the items from.
478      * @param checkedItem specifies which item is checked. If -1 no items are checked.
479      * @param labelColumn The column name on the cursor containing the string to display in the
480      * label.
481      * @param listener notified when an item on the list is clicked. The dialog will not be
482      * dismissed when an item is clicked. It will only be dismissed if clicked on a
483      * button, if no buttons are supplied it's up to the user to dismiss the dialog.
484      * @return This Builder object to allow for chaining of calls to set methods
485      */
setSingleChoiceItems(Cursor cursor, int checkedItem, String labelColumn, final DialogInterface.OnClickListener listener)486     public AlertDialogBuilder setSingleChoiceItems(Cursor cursor, int checkedItem,
487             String labelColumn,
488             final DialogInterface.OnClickListener listener) {
489         mBuilder.setSingleChoiceItems(cursor, checkedItem, labelColumn, listener);
490         return this;
491     }
492 
493     /**
494      * Set a list of items to be displayed in the dialog as the content, you will be notified of
495      * the selected item via the supplied listener. The list will have a check mark displayed to
496      * the right of the text for the checked item. Clicking on an item in the list will not
497      * dismiss the dialog. Clicking on a button will dismiss the dialog.
498      *
499      * @param items the items to be displayed.
500      * @param checkedItem specifies which item is checked. If -1 no items are checked.
501      * @param listener notified when an item on the list is clicked. The dialog will not be
502      * dismissed when an item is clicked. It will only be dismissed if clicked on a
503      * button, if no buttons are supplied it's up to the user to dismiss the dialog.
504      * @return This Builder object to allow for chaining of calls to set methods
505      */
setSingleChoiceItems(CharSequence[] items, int checkedItem, final DialogInterface.OnClickListener listener)506     public AlertDialogBuilder setSingleChoiceItems(CharSequence[] items, int checkedItem,
507             final DialogInterface.OnClickListener listener) {
508         mBuilder.setSingleChoiceItems(items, checkedItem, listener);
509         return this;
510     }
511 
512     /**
513      * This was not supposed to be in the Chassis API because it allows custom views.
514      *
515      * @deprecated Use {@link #setSingleChoiceItems(CarUiRadioButtonListItemAdapter,
516      * DialogInterface.OnClickListener)} instead.
517      */
518     @Deprecated
setSingleChoiceItems(ListAdapter adapter, int checkedItem, final DialogInterface.OnClickListener listener)519     public AlertDialogBuilder setSingleChoiceItems(ListAdapter adapter, int checkedItem,
520             final DialogInterface.OnClickListener listener) {
521         mBuilder.setSingleChoiceItems(adapter, checkedItem, listener);
522         return this;
523     }
524 
525     /**
526      * Set a list of items to be displayed in the dialog as the content, you will be notified of
527      * the selected item via the supplied listener. The list will have a check mark displayed to
528      * the right of the text for the checked item. Clicking on an item in the list will not
529      * dismiss the dialog. Clicking on a button will dismiss the dialog.
530      *
531      * @param adapter The {@link CarUiRadioButtonListItemAdapter} to supply the list of items
532      * @param listener notified when an item on the list is clicked. The dialog will not be
533      * dismissed when an item is clicked. It will only be dismissed if clicked on a
534      * button, if no buttons are supplied it's up to the user to dismiss the dialog.
535      * @return This Builder object to allow for chaining of calls to set methods
536      *
537      * @deprecated Use {@link #setSingleChoiceItems(CarUiRadioButtonListItemAdapter)} instead.
538      */
539     @Deprecated
setSingleChoiceItems(CarUiRadioButtonListItemAdapter adapter, final DialogInterface.OnClickListener listener)540     public AlertDialogBuilder setSingleChoiceItems(CarUiRadioButtonListItemAdapter adapter,
541             final DialogInterface.OnClickListener listener) {
542         setCustomList(adapter);
543         return this;
544     }
545 
546     /**
547      * Set a list of items to be displayed in the dialog as the content,The list will have a check
548      * mark displayed to the right of the text for the checked item. Clicking on an item in the list
549      * will not dismiss the dialog. Clicking on a button will dismiss the dialog.
550      *
551      * @param adapter The {@link CarUiRadioButtonListItemAdapter} to supply the list of items
552      * dismissed when an item is clicked. It will only be dismissed if clicked on a
553      * button, if no buttons are supplied it's up to the user to dismiss the dialog.
554      * @return This Builder object to allow for chaining of calls to set methods
555      */
setSingleChoiceItems(CarUiRadioButtonListItemAdapter adapter)556     public AlertDialogBuilder setSingleChoiceItems(CarUiRadioButtonListItemAdapter adapter) {
557         setCustomList(adapter);
558         return this;
559     }
560 
561     /**
562      * Sets a listener to be invoked when an item in the list is selected.
563      *
564      * @param listener the listener to be invoked
565      * @return this Builder object to allow for chaining of calls to set methods
566      * @see AdapterView#setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)
567      */
setOnItemSelectedListener( final AdapterView.OnItemSelectedListener listener)568     public AlertDialogBuilder setOnItemSelectedListener(
569             final AdapterView.OnItemSelectedListener listener) {
570         mBuilder.setOnItemSelectedListener(listener);
571         return this;
572     }
573 
574     /**
575      * Sets a custom edit text box within the alert dialog.
576      *
577      * @param prompt the string that will be set on the edit text view
578      * @param textChangedListener textWatcher whose methods are called whenever this TextView's text
579      * changes {@link null} otherwise.
580      * @param inputFilters list of input filters, {@link null} if no filter is needed
581      * @param inputType See {@link EditText#setInputType(int)}, except
582      *                  {@link android.text.InputType#TYPE_NULL} will not be set.
583      * @return this Builder object to allow for chaining of calls to set methods
584      */
setEditBox(String prompt, TextWatcher textChangedListener, InputFilter[] inputFilters, int inputType)585     public AlertDialogBuilder setEditBox(String prompt, TextWatcher textChangedListener,
586             InputFilter[] inputFilters, int inputType) {
587         View contentView = LayoutInflater.from(mContext).inflate(
588                 R.layout.car_ui_alert_dialog_edit_text, null);
589 
590         EditText editText = contentView.requireViewById(R.id.textbox);
591         editText.setText(prompt);
592 
593         if (textChangedListener != null) {
594             editText.addTextChangedListener(textChangedListener);
595         }
596 
597         if (inputFilters != null) {
598             editText.setFilters(inputFilters);
599         }
600 
601         if (inputType != 0) {
602             editText.setInputType(inputType);
603         }
604 
605         mBuilder.setView(contentView);
606         return this;
607     }
608 
609     /**
610      * Sets a custom edit text box within the alert dialog.
611      *
612      * @param prompt the string that will be set on the edit text view
613      * @param textChangedListener textWatcher whose methods are called whenever this TextView's text
614      * changes {@link null} otherwise.
615      * @param inputFilters list of input filters, {@link null} if no filter is needed
616      * @return this Builder object to allow for chaining of calls to set methods
617      */
setEditBox(String prompt, TextWatcher textChangedListener, InputFilter[] inputFilters)618     public AlertDialogBuilder setEditBox(String prompt, TextWatcher textChangedListener,
619             InputFilter[] inputFilters) {
620         return setEditBox(prompt, textChangedListener, inputFilters, 0);
621     }
622 
623 
624     /** Final steps common to both {@link #create()} and {@link #show()} */
prepareDialog()625     private void prepareDialog() {
626         if (mSubtitle != null) {
627 
628             View customTitle = LayoutInflater.from(mContext).inflate(
629                     R.layout.car_ui_alert_dialog_title_with_subtitle, null);
630 
631             TextView mTitleView = customTitle.requireViewById(R.id.alertTitle);
632             TextView mSubtitleView = customTitle.requireViewById(R.id.alertSubtitle);
633             ImageView mIconView = customTitle.requireViewById(R.id.icon);
634 
635             mTitleView.setText(mTitle);
636             mSubtitleView.setText(mSubtitle);
637             mIconView.setImageDrawable(mIcon);
638             mIconView.setVisibility(mIcon != null ? View.VISIBLE : View.GONE);
639             mBuilder.setCustomTitle(customTitle);
640         }
641 
642         if (!mNeutralButtonSet && !mNegativeButtonSet && !mPositiveButtonSet) {
643             String mDefaultButtonText = mContext.getString(
644                     R.string.car_ui_alert_dialog_default_button);
645             mBuilder.setNegativeButton(mDefaultButtonText, (dialog, which) -> {
646             });
647         }
648     }
649 
650     /**
651      * Creates an {@link AlertDialog} with the arguments supplied to this
652      * builder.
653      * <p>
654      * Calling this method does not display the dialog. If no additional
655      * processing is needed, {@link #show()} may be called instead to both
656      * create and display the dialog.
657      */
create()658     public AlertDialog create() {
659         prepareDialog();
660         return mBuilder.create();
661     }
662 
663     /**
664      * Creates an {@link AlertDialog} with the arguments supplied to this
665      * builder and immediately displays the dialog.
666      * <p>
667      * Calling this method is functionally identical to:
668      * <pre>
669      *     AlertDialog dialog = builder.create();
670      *     dialog.show();
671      * </pre>
672      */
show()673     public AlertDialog show() {
674         prepareDialog();
675         return mBuilder.show();
676     }
677 }
678