blob: ef2cb490ce495022ea49399a1ef73323876352a4 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.ui;
import static android.view.WindowInsets.Type.ime;
import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.ADD_DESC_TITLE_TO_CONTENT_AREA;
import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.ADD_DESC_TO_CONTENT_AREA;
import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.WIDE_SCREEN_ACTION;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.TextView;
import androidx.annotation.ArrayRes;
import androidx.annotation.AttrRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.car.ui.recyclerview.CarUiListItemAdapter;
import com.android.car.ui.recyclerview.CarUiRadioButtonListItemAdapter;
import com.android.car.ui.utils.CarUiUtils;
/**
* Wrapper for AlertDialog.Builder
*/
public class AlertDialogBuilder {
private AlertDialog.Builder mBuilder;
private Context mContext;
private boolean mPositiveButtonSet;
private boolean mNeutralButtonSet;
private boolean mNegativeButtonSet;
private CharSequence mTitle;
private CharSequence mSubtitle;
private Drawable mIcon;
private boolean mIconTinted;
private EditText mCarUiEditText;
private InputMethodManager mInputMethodManager;
private String mWideScreenTitle;
private String mWideScreenTitleDesc;
private ViewGroup mRoot;
private boolean mAllowDismissButton = true;
private boolean mHasSingleChoiceBodyButton = false;
private final TextWatcher mTextWatcherWideScreen = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Bundle bundle = new Bundle();
String titleString = mWideScreenTitle != null ? mWideScreenTitle : mTitle.toString();
bundle.putString(ADD_DESC_TITLE_TO_CONTENT_AREA, titleString);
bundle.putString(ADD_DESC_TO_CONTENT_AREA, s.toString());
mInputMethodManager.sendAppPrivateCommand(mCarUiEditText, WIDE_SCREEN_ACTION,
bundle);
}
@Override
public void afterTextChanged(Editable s) {
}
};
// Whenever the IME is closed and opened again, the title and desc information needs to be
// passed to the IME to be rendered. If the information is not passed to the IME the content
// area of the IME will render nothing into the content area.
private final View.OnApplyWindowInsetsListener mOnApplyWindowInsetsListener = (v, insets) -> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
// WindowInsets.isVisible() is only available on R or above
return v.onApplyWindowInsets(insets);
}
if (insets.isVisible(ime())) {
Bundle bundle = new Bundle();
String title = mWideScreenTitle != null ? mWideScreenTitle : mTitle.toString();
bundle.putString(ADD_DESC_TITLE_TO_CONTENT_AREA, title);
if (mWideScreenTitleDesc != null) {
bundle.putString(ADD_DESC_TO_CONTENT_AREA, mWideScreenTitleDesc);
}
mInputMethodManager.sendAppPrivateCommand(mCarUiEditText, WIDE_SCREEN_ACTION,
bundle);
}
return v.onApplyWindowInsets(insets);
};
private final AlertDialog.OnDismissListener mOnDismissListener = dialog -> {
if (mRoot != null) {
mRoot.setOnApplyWindowInsetsListener(null);
}
};
public AlertDialogBuilder(Context context) {
// Resource id specified as 0 uses the parent contexts resolved value for alertDialogTheme.
this(context, /* themeResId= */0);
}
public AlertDialogBuilder(Context context, int themeResId) {
mBuilder = new AlertDialog.Builder(context, themeResId);
mInputMethodManager = (InputMethodManager)
context.getSystemService(Context.INPUT_METHOD_SERVICE);
mContext = context;
}
public Context getContext() {
return mBuilder.getContext();
}
/**
* Set the title using the given resource id.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setTitle(@StringRes int titleId) {
return setTitle(mContext.getText(titleId));
}
/**
* Set the title displayed in the {@link Dialog}.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setTitle(CharSequence title) {
mTitle = title;
mBuilder.setTitle(title);
return this;
}
/**
* Sets a subtitle to be displayed in the {@link Dialog}.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setSubtitle(@StringRes int subtitle) {
return setSubtitle(mContext.getString(subtitle));
}
/**
* Sets a subtitle to be displayed in the {@link Dialog}.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setSubtitle(CharSequence subtitle) {
mSubtitle = subtitle;
return this;
}
/**
* Set the message to display using the given resource id.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setMessage(@StringRes int messageId) {
mBuilder.setMessage(messageId);
return this;
}
/**
* Set the message to display.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setMessage(CharSequence message) {
mBuilder.setMessage(message);
return this;
}
/**
* Set the resource id of the {@link Drawable} to be used in the title.
* <p>
* Takes precedence over values set using {@link #setIcon(Drawable)}.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setIcon(@DrawableRes int iconId) {
return setIcon(mContext.getDrawable(iconId));
}
/**
* Set the {@link Drawable} to be used in the title.
* <p>
* <strong>Note:</strong> To ensure consistent styling, the drawable
* should be inflated or constructed using the alert dialog's themed
* context obtained via {@link #getContext()}.
*
* @return this Builder object to allow for chaining of calls to set
* methods
*/
public AlertDialogBuilder setIcon(Drawable icon) {
mIcon = icon;
return this;
}
/**
* Whether the icon provided by {@link #setIcon(Drawable)} should be
* tinted with the default system color.
*
* @return this Builder object to allow for chaining of calls to set
* methods.
*/
public AlertDialogBuilder setIconTinted(boolean tinted) {
mIconTinted = tinted;
return this;
}
/**
* Set an icon as supplied by a theme attribute. e.g.
* {@link android.R.attr#alertDialogIcon}.
* <p>
* Takes precedence over values set using {@link #setIcon(Drawable)}.
*
* @param attrId ID of a theme attribute that points to a drawable resource.
*/
public AlertDialogBuilder setIconAttribute(@AttrRes int attrId) {
mBuilder.setIconAttribute(attrId);
return this;
}
/**
* Set a listener to be invoked when the positive button of the dialog is pressed.
*
* @param textId The resource id of the text to display in the positive button
* @param listener The {@link DialogInterface.OnClickListener} to use.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setPositiveButton(@StringRes int textId,
final DialogInterface.OnClickListener listener) {
mBuilder.setPositiveButton(textId, listener);
mPositiveButtonSet = true;
return this;
}
/**
* Set a listener to be invoked when the positive button of the dialog is pressed.
*
* @param text The text to display in the positive button
* @param listener The {@link DialogInterface.OnClickListener} to use.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setPositiveButton(CharSequence text,
final DialogInterface.OnClickListener listener) {
mBuilder.setPositiveButton(text, listener);
mPositiveButtonSet = true;
return this;
}
/**
* Set a listener to be invoked when the negative button of the dialog is pressed.
*
* @param textId The resource id of the text to display in the negative button
* @param listener The {@link DialogInterface.OnClickListener} to use.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setNegativeButton(@StringRes int textId,
final DialogInterface.OnClickListener listener) {
mBuilder.setNegativeButton(textId, listener);
mNegativeButtonSet = true;
return this;
}
/**
* Set a listener to be invoked when the negative button of the dialog is pressed.
*
* @param text The text to display in the negative button
* @param listener The {@link DialogInterface.OnClickListener} to use.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setNegativeButton(CharSequence text,
final DialogInterface.OnClickListener listener) {
mBuilder.setNegativeButton(text, listener);
mNegativeButtonSet = true;
return this;
}
/**
* Set a listener to be invoked when the neutral button of the dialog is pressed.
*
* @param textId The resource id of the text to display in the neutral button
* @param listener The {@link DialogInterface.OnClickListener} to use.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setNeutralButton(@StringRes int textId,
final DialogInterface.OnClickListener listener) {
mBuilder.setNeutralButton(textId, listener);
mNeutralButtonSet = true;
return this;
}
/**
* Set a listener to be invoked when the neutral button of the dialog is pressed.
*
* @param text The text to display in the neutral button
* @param listener The {@link DialogInterface.OnClickListener} to use.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setNeutralButton(CharSequence text,
final DialogInterface.OnClickListener listener) {
mBuilder.setNeutralButton(text, listener);
mNeutralButtonSet = true;
return this;
}
/**
* Sets whether the dialog is cancelable or not. Default is true.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setCancelable(boolean cancelable) {
mBuilder.setCancelable(cancelable);
return this;
}
/**
* Sets the callback that will be called if the dialog is canceled.
*
* <p>Even in a cancelable dialog, the dialog may be dismissed for reasons other than
* being canceled or one of the supplied choices being selected.
* If you are interested in listening for all cases where the dialog is dismissed
* and not just when it is canceled, see
* {@link #setOnDismissListener(android.content.DialogInterface.OnDismissListener)
* setOnDismissListener}.</p>
*
* @return This Builder object to allow for chaining of calls to set methods
* @see #setCancelable(boolean)
* @see #setOnDismissListener(android.content.DialogInterface.OnDismissListener)
*/
public AlertDialogBuilder setOnCancelListener(
DialogInterface.OnCancelListener onCancelListener) {
mBuilder.setOnCancelListener(onCancelListener);
return this;
}
/**
* Sets the callback that will be called when the dialog is dismissed for any reason.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setOnDismissListener(
DialogInterface.OnDismissListener onDismissListener) {
mBuilder.setOnDismissListener(onDismissListener);
return this;
}
/**
* Sets the callback that will be called if a key is dispatched to the dialog.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setOnKeyListener(DialogInterface.OnKeyListener onKeyListener) {
mBuilder.setOnKeyListener(onKeyListener);
return this;
}
/**
* Set a list of items to be displayed in the dialog as the content, you will be notified of the
* selected item via the supplied listener. This should be an array type i.e. R.array.foo
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setItems(@ArrayRes int itemsId,
final DialogInterface.OnClickListener listener) {
mBuilder.setItems(itemsId, listener);
mHasSingleChoiceBodyButton = true;
return this;
}
/**
* Set a list of items to be displayed in the dialog as the content, you will be notified of the
* selected item via the supplied listener.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setItems(CharSequence[] items,
final DialogInterface.OnClickListener listener) {
mBuilder.setItems(items, listener);
mHasSingleChoiceBodyButton = true;
return this;
}
/**
* This was not supposed to be in the Chassis API because it allows custom views.
*
* @deprecated Use {@link #setAdapter(CarUiListItemAdapter)} instead.
*/
@Deprecated
public AlertDialogBuilder setAdapter(final ListAdapter adapter,
final DialogInterface.OnClickListener listener) {
mBuilder.setAdapter(adapter, listener);
mHasSingleChoiceBodyButton = true;
return this;
}
/**
* Display all the {@link com.android.car.ui.recyclerview.CarUiListItem CarUiListItems} in a
* {@link CarUiListItemAdapter}. You should set click listeners on the CarUiListItems as
* opposed to a callback in this function.
*/
public AlertDialogBuilder setAdapter(final CarUiListItemAdapter adapter) {
setCustomList(adapter);
mHasSingleChoiceBodyButton = true;
return this;
}
private void setCustomList(@NonNull CarUiListItemAdapter adapter) {
View customList = LayoutInflater.from(mContext).inflate(
R.layout.car_ui_alert_dialog_list, null);
RecyclerView mList = CarUiUtils.requireViewByRefId(customList, R.id.list);
mList.setLayoutManager(new LinearLayoutManager(mContext));
mList.setAdapter(adapter);
mBuilder.setView(customList);
}
/**
* Set a list of items, which are supplied by the given {@link Cursor}, to be
* displayed in the dialog as the content, you will be notified of the
* selected item via the supplied listener.
*
* @param cursor The {@link Cursor} to supply the list of items
* @param listener The listener that will be called when an item is clicked.
* @param labelColumn The column name on the cursor containing the string to display
* in the label.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setCursor(final Cursor cursor,
final DialogInterface.OnClickListener listener,
String labelColumn) {
mBuilder.setCursor(cursor, listener, labelColumn);
mHasSingleChoiceBodyButton = true;
return this;
}
/**
* Set a list of items to be displayed in the dialog as the content,
* you will be notified of the selected item via the supplied listener.
* This should be an array type, e.g. R.array.foo. The list will have
* a check mark displayed to the right of the text for each checked
* item. Clicking on an item in the list will not dismiss the dialog.
* Clicking on a button will dismiss the dialog.
*
* @param itemsId the resource id of an array i.e. R.array.foo
* @param checkedItems specifies which items are checked. It should be null in which case no
* items are checked. If non null it must be exactly the same length as the
* array of
* items.
* @param listener notified when an item on the list is clicked. The dialog will not be
* dismissed when an item is clicked. It will only be dismissed if clicked
* on a
* button, if no buttons are supplied it's up to the user to dismiss the
* dialog.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setMultiChoiceItems(@ArrayRes int itemsId, boolean[] checkedItems,
final DialogInterface.OnMultiChoiceClickListener listener) {
mBuilder.setMultiChoiceItems(itemsId, checkedItems, listener);
mHasSingleChoiceBodyButton = false;
return this;
}
/**
* Set a list of items to be displayed in the dialog as the content,
* you will be notified of the selected item via the supplied listener.
* The list will have a check mark displayed to the right of the text
* for each checked item. Clicking on an item in the list will not
* dismiss the dialog. Clicking on a button will dismiss the dialog.
*
* @param items the text of the items to be displayed in the list.
* @param checkedItems specifies which items are checked. It should be null in which case no
* items are checked. If non null it must be exactly the same length as the
* array of
* items.
* @param listener notified when an item on the list is clicked. The dialog will not be
* dismissed when an item is clicked. It will only be dismissed if clicked
* on a
* button, if no buttons are supplied it's up to the user to dismiss the
* dialog.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems,
final DialogInterface.OnMultiChoiceClickListener listener) {
mBuilder.setMultiChoiceItems(items, checkedItems, listener);
mHasSingleChoiceBodyButton = false;
return this;
}
/**
* Set a list of items to be displayed in the dialog as the content,
* you will be notified of the selected item via the supplied listener.
* The list will have a check mark displayed to the right of the text
* for each checked item. Clicking on an item in the list will not
* dismiss the dialog. Clicking on a button will dismiss the dialog.
*
* @param cursor the cursor used to provide the items.
* @param isCheckedColumn specifies the column name on the cursor to use to determine
* whether a checkbox is checked or not. It must return an integer value
* where 1
* means checked and 0 means unchecked.
* @param labelColumn The column name on the cursor containing the string to display in the
* label.
* @param listener notified when an item on the list is clicked. The dialog will not be
* dismissed when an item is clicked. It will only be dismissed if
* clicked on a
* button, if no buttons are supplied it's up to the user to dismiss the
* dialog.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setMultiChoiceItems(Cursor cursor, String isCheckedColumn,
String labelColumn,
final DialogInterface.OnMultiChoiceClickListener listener) {
mBuilder.setMultiChoiceItems(cursor, isCheckedColumn, labelColumn, listener);
mHasSingleChoiceBodyButton = false;
return this;
}
/**
* Set a list of items to be displayed in the dialog as the content, you will be notified of
* the selected item via the supplied listener. This should be an array type i.e.
* R.array.foo The list will have a check mark displayed to the right of the text for the
* checked item. Clicking on an item in the list will not dismiss the dialog. Clicking on a
* button will dismiss the dialog.
*
* @param itemsId the resource id of an array i.e. R.array.foo
* @param checkedItem specifies which item is checked. If -1 no items are checked.
* @param listener notified when an item on the list is clicked. The dialog will not be
* dismissed when an item is clicked. It will only be dismissed if clicked on
* a
* button, if no buttons are supplied it's up to the user to dismiss the
* dialog.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setSingleChoiceItems(@ArrayRes int itemsId, int checkedItem,
final DialogInterface.OnClickListener listener) {
mBuilder.setSingleChoiceItems(itemsId, checkedItem, listener);
mHasSingleChoiceBodyButton = true;
return this;
}
/**
* Set a list of items to be displayed in the dialog as the content, you will be notified of
* the selected item via the supplied listener. The list will have a check mark displayed to
* the right of the text for the checked item. Clicking on an item in the list will not
* dismiss the dialog. Clicking on a button will dismiss the dialog.
*
* @param cursor the cursor to retrieve the items from.
* @param checkedItem specifies which item is checked. If -1 no items are checked.
* @param labelColumn The column name on the cursor containing the string to display in the
* label.
* @param listener notified when an item on the list is clicked. The dialog will not be
* dismissed when an item is clicked. It will only be dismissed if clicked on
* a
* button, if no buttons are supplied it's up to the user to dismiss the
* dialog.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setSingleChoiceItems(Cursor cursor, int checkedItem,
String labelColumn,
final DialogInterface.OnClickListener listener) {
mBuilder.setSingleChoiceItems(cursor, checkedItem, labelColumn, listener);
mHasSingleChoiceBodyButton = true;
return this;
}
/**
* Set a list of items to be displayed in the dialog as the content, you will be notified of
* the selected item via the supplied listener. The list will have a check mark displayed to
* the right of the text for the checked item. Clicking on an item in the list will not
* dismiss the dialog. Clicking on a button will dismiss the dialog.
*
* @param items the items to be displayed.
* @param checkedItem specifies which item is checked. If -1 no items are checked.
* @param listener notified when an item on the list is clicked. The dialog will not be
* dismissed when an item is clicked. It will only be dismissed if clicked on
* a
* button, if no buttons are supplied it's up to the user to dismiss the
* dialog.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setSingleChoiceItems(CharSequence[] items, int checkedItem,
final DialogInterface.OnClickListener listener) {
mBuilder.setSingleChoiceItems(items, checkedItem, listener);
mHasSingleChoiceBodyButton = true;
return this;
}
/**
* This was not supposed to be in the Chassis API because it allows custom views.
*
* @deprecated Use {@link #setSingleChoiceItems(CarUiRadioButtonListItemAdapter,
* DialogInterface.OnClickListener)} instead.
*/
@Deprecated
public AlertDialogBuilder setSingleChoiceItems(ListAdapter adapter, int checkedItem,
final DialogInterface.OnClickListener listener) {
mBuilder.setSingleChoiceItems(adapter, checkedItem, listener);
mHasSingleChoiceBodyButton = true;
return this;
}
/**
* Set a list of items to be displayed in the dialog as the content, you will be notified of
* the selected item via the supplied listener. The list will have a check mark displayed to
* the right of the text for the checked item. Clicking on an item in the list will not
* dismiss the dialog. Clicking on a button will dismiss the dialog.
*
* @param adapter The {@link CarUiRadioButtonListItemAdapter} to supply the list of items
* @param listener notified when an item on the list is clicked. The dialog will not be
* dismissed when an item is clicked. It will only be dismissed if clicked on a
* button, if no buttons are supplied it's up to the user to dismiss the dialog.
* @return This Builder object to allow for chaining of calls to set methods
* @deprecated Use {@link #setSingleChoiceItems(CarUiRadioButtonListItemAdapter)} instead.
*/
@Deprecated
public AlertDialogBuilder setSingleChoiceItems(CarUiRadioButtonListItemAdapter adapter,
final DialogInterface.OnClickListener listener) {
setCustomList(adapter);
mHasSingleChoiceBodyButton = false;
return this;
}
/**
* Set a list of items to be displayed in the dialog as the content,The list will have a check
* mark displayed to the right of the text for the checked item. Clicking on an item in the list
* will not dismiss the dialog. Clicking on a button will dismiss the dialog.
*
* @param adapter The {@link CarUiRadioButtonListItemAdapter} to supply the list of items
* dismissed when an item is clicked. It will only be dismissed if clicked on a
* button, if no buttons are supplied it's up to the user to dismiss the dialog.
* @return This Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setSingleChoiceItems(CarUiRadioButtonListItemAdapter adapter) {
setCustomList(adapter);
mHasSingleChoiceBodyButton = false;
return this;
}
/**
* Sets a listener to be invoked when an item in the list is selected.
*
* @param listener the listener to be invoked
* @return this Builder object to allow for chaining of calls to set methods
* @see AdapterView#setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)
*/
public AlertDialogBuilder setOnItemSelectedListener(
final AdapterView.OnItemSelectedListener listener) {
mBuilder.setOnItemSelectedListener(listener);
mHasSingleChoiceBodyButton = true;
return this;
}
/**
* Sets a custom edit text box within the alert dialog.
*
* @param prompt the string that will be set on the edit text view
* @param textChangedListener textWatcher whose methods are called whenever this TextView's text
* changes {@link null} otherwise.
* @param inputFilters list of input filters, {@link null} if no filter is needed
* @param inputType See {@link EditText#setInputType(int)}, except
* {@link android.text.InputType#TYPE_NULL} will not be set.
* @return this Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setEditBox(String prompt, TextWatcher textChangedListener,
InputFilter[] inputFilters, int inputType) {
View contentView = LayoutInflater.from(mContext).inflate(
R.layout.car_ui_alert_dialog_edit_text, null);
mCarUiEditText = CarUiUtils.requireViewByRefId(contentView, R.id.textbox);
mCarUiEditText.setText(prompt);
if (textChangedListener != null) {
mCarUiEditText.addTextChangedListener(textChangedListener);
}
if (inputFilters != null) {
mCarUiEditText.setFilters(inputFilters);
}
if (inputType != 0) {
mCarUiEditText.setInputType(inputType);
}
mBuilder.setView(contentView);
return this;
}
/**
* Sets a custom edit text box within the alert dialog.
*
* @param prompt the string that will be set on the edit text view
* @param textChangedListener textWatcher whose methods are called whenever this TextView's text
* changes {@link null} otherwise.
* @param inputFilters list of input filters, {@link null} if no filter is needed
* @return this Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setEditBox(String prompt, TextWatcher textChangedListener,
InputFilter[] inputFilters) {
return setEditBox(prompt, textChangedListener, inputFilters, 0);
}
/**
* Sets the title and desc related to the dialog within the IMS templates.
*
* @param title title to be set.
* @param desc description related to the dialog.
* @return this Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setEditTextTitleAndDescForWideScreen(String title, String desc) {
mWideScreenTitle = title;
mWideScreenTitleDesc = desc;
return this;
}
/**
* By default, the AlertDialogBuilder will just display the static text in the content area of
* widescreen IME provided by {@link #setEditTextTitleAndDescForWideScreen(String, String)}. To
* display the text typed by the user in the description set this to true.
*
* @return this Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setAutoDescUpdateForWidescreen(boolean autoUpdateDesc) {
if (autoUpdateDesc) {
mCarUiEditText.addTextChangedListener(mTextWatcherWideScreen);
} else {
mCarUiEditText.removeTextChangedListener(mTextWatcherWideScreen);
}
return this;
}
/**
* By default, the AlertDialogBuilder may add a "Dismiss" button if you don't provide
* a positive/negative/neutral button. This is so that the dialog is still dismissible
* using the rotary controller. If however, you add buttons that can close the dialog via
* {@link #setAdapter(CarUiListItemAdapter)} or a similar method, then you may wish to
* suppress the addition of the dismiss button, which this method allows for.
*
* @param allowDismissButton If true, a "Dismiss" button may be added to the dialog.
* If false, it will never be added.
* @return this Builder object to allow for chaining of calls to set methods
*/
public AlertDialogBuilder setAllowDismissButton(boolean allowDismissButton) {
mAllowDismissButton = allowDismissButton;
return this;
}
/** Final steps common to both {@link #create()} and {@link #show()} */
private void prepareDialog() {
View customTitle = LayoutInflater.from(mContext).inflate(
R.layout.car_ui_alert_dialog_title_with_subtitle, null);
TextView mTitleView = CarUiUtils.requireViewByRefId(customTitle, R.id.car_ui_alert_title);
TextView mSubtitleView =
CarUiUtils.requireViewByRefId(customTitle, R.id.car_ui_alert_subtitle);
mSubtitleView.setMovementMethod(LinkMovementMethod.getInstance());
ImageView mIconView = CarUiUtils.requireViewByRefId(customTitle, R.id.car_ui_alert_icon);
mTitleView.setText(mTitle);
mTitleView.setVisibility(TextUtils.isEmpty(mTitle) ? View.GONE : View.VISIBLE);
mSubtitleView.setText(mSubtitle);
mSubtitleView.setVisibility(TextUtils.isEmpty(mSubtitle) ? View.GONE : View.VISIBLE);
mIconView.setImageDrawable(mIcon);
mIconView.setVisibility(mIcon != null ? View.VISIBLE : View.GONE);
if (mIconTinted) {
mIconView.setImageTintList(
mContext.getColorStateList(R.color.car_ui_dialog_icon_color));
}
mBuilder.setCustomTitle(customTitle);
if (!mAllowDismissButton && !mHasSingleChoiceBodyButton
&& !mNeutralButtonSet && !mNegativeButtonSet && !mPositiveButtonSet) {
throw new RuntimeException(
"The dialog must have at least one button to disable the dismiss button");
}
if (mContext.getResources().getBoolean(R.bool.car_ui_alert_dialog_force_dismiss_button)
&& !mNeutralButtonSet && !mNegativeButtonSet && !mPositiveButtonSet
&& mAllowDismissButton) {
String mDefaultButtonText = mContext.getString(
R.string.car_ui_alert_dialog_default_button);
mBuilder.setNegativeButton(mDefaultButtonText, (dialog, which) -> {
});
}
}
/**
* Creates an {@link AlertDialog} with the arguments supplied to this
* builder.
* <p>
* Calling this method does not display the dialog. If no additional
* processing is needed, {@link #show()} may be called instead to both
* create and display the dialog.
*/
public AlertDialog create() {
prepareDialog();
AlertDialog alertDialog = mBuilder.create();
// Put a FocusParkingView at the end of dialog window to prevent rotary controller
// wrap-around. Android will focus on the first view automatically when the dialog is shown,
// and we want it to focus on the title instead of the FocusParkingView, so we put the
// FocusParkingView at the end of dialog window.
mRoot = (ViewGroup) alertDialog.getWindow().getDecorView().getRootView();
FocusParkingView fpv = new FocusParkingView(mContext);
mRoot.addView(fpv);
// apply window insets listener to know when IME is visible so we can set title and desc.
mRoot.setOnApplyWindowInsetsListener(mOnApplyWindowInsetsListener);
setOnDismissListener(mOnDismissListener);
return alertDialog;
}
/**
* Creates an {@link AlertDialog} with the arguments supplied to this
* builder and immediately displays the dialog.
*/
public AlertDialog show() {
AlertDialog alertDialog = create();
alertDialog.show();
return alertDialog;
}
}