mirror of
https://github.com/material-components/material-components-android.git
synced 2026-01-16 18:01:42 +08:00
503 lines
17 KiB
Java
503 lines
17 KiB
Java
/*
|
|
* Copyright 2018 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.google.android.material.dialog;
|
|
|
|
import com.google.android.material.R;
|
|
|
|
import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap;
|
|
|
|
import android.content.Context;
|
|
import android.content.DialogInterface.OnCancelListener;
|
|
import android.content.DialogInterface.OnClickListener;
|
|
import android.content.DialogInterface.OnDismissListener;
|
|
import android.content.DialogInterface.OnKeyListener;
|
|
import android.content.DialogInterface.OnMultiChoiceClickListener;
|
|
import android.content.res.ColorStateList;
|
|
import android.content.res.Resources.Theme;
|
|
import android.content.res.TypedArray;
|
|
import android.database.Cursor;
|
|
import android.graphics.Rect;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Build;
|
|
import android.os.Build.VERSION_CODES;
|
|
import androidx.appcompat.app.AlertDialog;
|
|
import android.util.TypedValue;
|
|
import android.view.View;
|
|
import android.view.Window;
|
|
import android.widget.AdapterView;
|
|
import android.widget.ListAdapter;
|
|
import androidx.annotation.ArrayRes;
|
|
import androidx.annotation.AttrRes;
|
|
import androidx.annotation.DrawableRes;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.Px;
|
|
import androidx.annotation.StringRes;
|
|
import androidx.annotation.StyleRes;
|
|
import androidx.appcompat.view.ContextThemeWrapper;
|
|
import com.google.android.material.color.MaterialColors;
|
|
import com.google.android.material.resources.MaterialAttributes;
|
|
import com.google.android.material.shape.MaterialShapeDrawable;
|
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
|
|
|
/**
|
|
* An extension of {@link AlertDialog.Builder} for use with a Material theme (e.g.,
|
|
* Theme.MaterialComponents).
|
|
*
|
|
* <p>This Builder must be used in order for AlertDialog objects to respond to color and shape
|
|
* theming provided by Material themes.
|
|
*
|
|
* <p>The type of dialog returned is still an {@link AlertDialog}; there is no specific Material
|
|
* implementation of {@link AlertDialog}.
|
|
*
|
|
* <p>For more information, see the <a
|
|
* href="https://github.com/material-components/material-components-android/blob/master/docs/components/Dialog.md">component
|
|
* developer guidance</a> and <a href="https://material.io/components/dialogs/overview">design
|
|
* guidelines</a>.
|
|
*/
|
|
public class MaterialAlertDialogBuilder extends AlertDialog.Builder {
|
|
|
|
@AttrRes
|
|
private static final int DEF_STYLE_ATTR = androidx.appcompat.R.attr.alertDialogStyle;
|
|
|
|
@StyleRes private static final int DEF_STYLE_RES = R.style.MaterialAlertDialog_MaterialComponents;
|
|
|
|
@AttrRes
|
|
private static final int MATERIAL_ALERT_DIALOG_THEME_OVERLAY = R.attr.materialAlertDialogTheme;
|
|
|
|
@Nullable private Drawable background;
|
|
@NonNull private final Rect backgroundInsets;
|
|
|
|
private static int getMaterialAlertDialogThemeOverlay(@NonNull Context context) {
|
|
TypedValue materialAlertDialogThemeOverlay =
|
|
MaterialAttributes.resolve(context, MATERIAL_ALERT_DIALOG_THEME_OVERLAY);
|
|
if (materialAlertDialogThemeOverlay == null) {
|
|
return 0;
|
|
}
|
|
return materialAlertDialogThemeOverlay.data;
|
|
}
|
|
|
|
private static Context createMaterialAlertDialogThemedContext(@NonNull Context context) {
|
|
int themeOverlayId = getMaterialAlertDialogThemeOverlay(context);
|
|
Context themedContext = wrap(context, null, DEF_STYLE_ATTR, DEF_STYLE_RES);
|
|
if (themeOverlayId == 0) {
|
|
return themedContext;
|
|
}
|
|
return new ContextThemeWrapper(themedContext, themeOverlayId);
|
|
}
|
|
|
|
private static int getOverridingThemeResId(@NonNull Context context, int overrideThemeResId) {
|
|
return overrideThemeResId == 0
|
|
? getMaterialAlertDialogThemeOverlay(context)
|
|
: overrideThemeResId;
|
|
}
|
|
|
|
public MaterialAlertDialogBuilder(@NonNull Context context) {
|
|
this(context, 0);
|
|
}
|
|
|
|
public MaterialAlertDialogBuilder(@NonNull Context context, int overrideThemeResId) {
|
|
// Only pass in 0 for overrideThemeResId if both overrideThemeResId and
|
|
// MATERIAL_ALERT_DIALOG_THEME_OVERLAY are 0 otherwise alertDialogTheme will override both.
|
|
super(
|
|
createMaterialAlertDialogThemedContext(context),
|
|
getOverridingThemeResId(context, overrideThemeResId));
|
|
// Ensure we are using the correctly themed context rather than the context that was passed in.
|
|
context = getContext();
|
|
Theme theme = context.getTheme();
|
|
|
|
backgroundInsets =
|
|
MaterialDialogs.getDialogBackgroundInsets(context, DEF_STYLE_ATTR, DEF_STYLE_RES);
|
|
|
|
int surfaceColor =
|
|
MaterialColors.getColor(context, R.attr.colorSurface, getClass().getCanonicalName());
|
|
|
|
TypedArray a =
|
|
context.obtainStyledAttributes(
|
|
/* set= */ null, R.styleable.MaterialAlertDialog, DEF_STYLE_ATTR, DEF_STYLE_RES);
|
|
int backgroundColor = a.getColor(R.styleable.MaterialAlertDialog_backgroundTint, surfaceColor);
|
|
a.recycle();
|
|
|
|
MaterialShapeDrawable materialShapeDrawable =
|
|
new MaterialShapeDrawable(context, null, DEF_STYLE_ATTR, DEF_STYLE_RES);
|
|
materialShapeDrawable.initializeElevationOverlay(context);
|
|
materialShapeDrawable.setFillColor(ColorStateList.valueOf(backgroundColor));
|
|
|
|
// dialogCornerRadius first appeared in Android Pie
|
|
if (Build.VERSION.SDK_INT >= VERSION_CODES.P) {
|
|
TypedValue dialogCornerRadiusValue = new TypedValue();
|
|
theme.resolveAttribute(android.R.attr.dialogCornerRadius, dialogCornerRadiusValue, true);
|
|
float dialogCornerRadius =
|
|
dialogCornerRadiusValue.getDimension(getContext().getResources().getDisplayMetrics());
|
|
if (dialogCornerRadiusValue.type == TypedValue.TYPE_DIMENSION && dialogCornerRadius >= 0) {
|
|
materialShapeDrawable.setCornerSize(dialogCornerRadius);
|
|
}
|
|
}
|
|
background = materialShapeDrawable;
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public AlertDialog create() {
|
|
AlertDialog alertDialog = super.create();
|
|
Window window = alertDialog.getWindow();
|
|
/* {@link Window#getDecorView()} should be called before any changes are made to the Window
|
|
* as it locks in attributes and affects layout. */
|
|
View decorView = window.getDecorView();
|
|
if (background instanceof MaterialShapeDrawable) {
|
|
((MaterialShapeDrawable) background).setElevation(decorView.getElevation());
|
|
}
|
|
|
|
Drawable insetDrawable = MaterialDialogs.insetDrawable(background, backgroundInsets);
|
|
window.setBackgroundDrawable(insetDrawable);
|
|
decorView.setOnTouchListener(new InsetDialogOnTouchListener(alertDialog, backgroundInsets));
|
|
return alertDialog;
|
|
}
|
|
|
|
@Nullable
|
|
public Drawable getBackground() {
|
|
return background;
|
|
}
|
|
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setBackground(@Nullable Drawable background) {
|
|
this.background = background;
|
|
return this;
|
|
}
|
|
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setBackgroundInsetStart(@Px int backgroundInsetStart) {
|
|
if (getContext().getResources().getConfiguration().getLayoutDirection()
|
|
== View.LAYOUT_DIRECTION_RTL) {
|
|
backgroundInsets.right = backgroundInsetStart;
|
|
} else {
|
|
backgroundInsets.left = backgroundInsetStart;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setBackgroundInsetTop(@Px int backgroundInsetTop) {
|
|
backgroundInsets.top = backgroundInsetTop;
|
|
return this;
|
|
}
|
|
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setBackgroundInsetEnd(@Px int backgroundInsetEnd) {
|
|
if (getContext().getResources().getConfiguration().getLayoutDirection()
|
|
== View.LAYOUT_DIRECTION_RTL) {
|
|
backgroundInsets.left = backgroundInsetEnd;
|
|
} else {
|
|
backgroundInsets.right = backgroundInsetEnd;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setBackgroundInsetBottom(@Px int backgroundInsetBottom) {
|
|
backgroundInsets.bottom = backgroundInsetBottom;
|
|
return this;
|
|
}
|
|
|
|
// The following methods are all pass-through methods used to specify the return type for the
|
|
// builder chain.
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setTitle(@StringRes int titleId) {
|
|
return (MaterialAlertDialogBuilder) super.setTitle(titleId);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setTitle(@Nullable CharSequence title) {
|
|
return (MaterialAlertDialogBuilder) super.setTitle(title);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setCustomTitle(@Nullable View customTitleView) {
|
|
return (MaterialAlertDialogBuilder) super.setCustomTitle(customTitleView);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setMessage(@StringRes int messageId) {
|
|
return (MaterialAlertDialogBuilder) super.setMessage(messageId);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setMessage(@Nullable CharSequence message) {
|
|
return (MaterialAlertDialogBuilder) super.setMessage(message);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setIcon(@DrawableRes int iconId) {
|
|
return (MaterialAlertDialogBuilder) super.setIcon(iconId);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setIcon(@Nullable Drawable icon) {
|
|
return (MaterialAlertDialogBuilder) super.setIcon(icon);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setIconAttribute(@AttrRes int attrId) {
|
|
return (MaterialAlertDialogBuilder) super.setIconAttribute(attrId);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setPositiveButton(
|
|
@StringRes int textId, @Nullable final OnClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setPositiveButton(textId, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setPositiveButton(
|
|
@Nullable CharSequence text, @Nullable final OnClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setPositiveButton(text, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setPositiveButtonIcon(@Nullable Drawable icon) {
|
|
return (MaterialAlertDialogBuilder) super.setPositiveButtonIcon(icon);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setNegativeButton(
|
|
@StringRes int textId, @Nullable final OnClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setNegativeButton(textId, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setNegativeButton(
|
|
@Nullable CharSequence text, @Nullable final OnClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setNegativeButton(text, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setNegativeButtonIcon(@Nullable Drawable icon) {
|
|
return (MaterialAlertDialogBuilder) super.setNegativeButtonIcon(icon);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setNeutralButton(
|
|
@StringRes int textId, @Nullable final OnClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setNeutralButton(textId, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setNeutralButton(
|
|
@Nullable CharSequence text, @Nullable final OnClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setNeutralButton(text, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setNeutralButtonIcon(@Nullable Drawable icon) {
|
|
return (MaterialAlertDialogBuilder) super.setNeutralButtonIcon(icon);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setCancelable(boolean cancelable) {
|
|
return (MaterialAlertDialogBuilder) super.setCancelable(cancelable);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setOnCancelListener(
|
|
@Nullable OnCancelListener onCancelListener) {
|
|
return (MaterialAlertDialogBuilder) super.setOnCancelListener(onCancelListener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setOnDismissListener(
|
|
@Nullable OnDismissListener onDismissListener) {
|
|
return (MaterialAlertDialogBuilder) super.setOnDismissListener(onDismissListener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setOnKeyListener(@Nullable OnKeyListener onKeyListener) {
|
|
return (MaterialAlertDialogBuilder) super.setOnKeyListener(onKeyListener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setItems(
|
|
@ArrayRes int itemsId, @Nullable final OnClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setItems(itemsId, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setItems(
|
|
@Nullable CharSequence[] items, @Nullable final OnClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setItems(items, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setAdapter(
|
|
@Nullable final ListAdapter adapter, @Nullable final OnClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setAdapter(adapter, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setCursor(
|
|
@Nullable final Cursor cursor,
|
|
@Nullable final OnClickListener listener,
|
|
@NonNull String labelColumn) {
|
|
return (MaterialAlertDialogBuilder) super.setCursor(cursor, listener, labelColumn);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setMultiChoiceItems(
|
|
@ArrayRes int itemsId,
|
|
@Nullable boolean[] checkedItems,
|
|
@Nullable final OnMultiChoiceClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setMultiChoiceItems(itemsId, checkedItems, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setMultiChoiceItems(
|
|
@Nullable CharSequence[] items,
|
|
@Nullable boolean[] checkedItems,
|
|
@Nullable final OnMultiChoiceClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setMultiChoiceItems(items, checkedItems, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setMultiChoiceItems(
|
|
@Nullable Cursor cursor,
|
|
@NonNull String isCheckedColumn,
|
|
@NonNull String labelColumn,
|
|
@Nullable final OnMultiChoiceClickListener listener) {
|
|
return (MaterialAlertDialogBuilder)
|
|
super.setMultiChoiceItems(cursor, isCheckedColumn, labelColumn, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setSingleChoiceItems(
|
|
@ArrayRes int itemsId, int checkedItem, @Nullable final OnClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setSingleChoiceItems(itemsId, checkedItem, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setSingleChoiceItems(
|
|
@Nullable Cursor cursor,
|
|
int checkedItem,
|
|
@NonNull String labelColumn,
|
|
@Nullable final OnClickListener listener) {
|
|
return (MaterialAlertDialogBuilder)
|
|
super.setSingleChoiceItems(cursor, checkedItem, labelColumn, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setSingleChoiceItems(
|
|
@Nullable CharSequence[] items, int checkedItem, @Nullable final OnClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setSingleChoiceItems(items, checkedItem, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setSingleChoiceItems(
|
|
@Nullable ListAdapter adapter, int checkedItem, @Nullable final OnClickListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setSingleChoiceItems(adapter, checkedItem, listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setOnItemSelectedListener(
|
|
@Nullable final AdapterView.OnItemSelectedListener listener) {
|
|
return (MaterialAlertDialogBuilder) super.setOnItemSelectedListener(listener);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setView(int layoutResId) {
|
|
return (MaterialAlertDialogBuilder) super.setView(layoutResId);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
@CanIgnoreReturnValue
|
|
public MaterialAlertDialogBuilder setView(@Nullable View view) {
|
|
return (MaterialAlertDialogBuilder) super.setView(view);
|
|
}
|
|
}
|