blob: 32511e9afbeaaf443a68f9c7f2c03f5d895684aa [file] [log] [blame]
/*
* Copyright (C) 2007 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 android.preference;
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.UnsupportedAppUsage;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
/**
* A base class for {@link Preference} objects that are
* dialog-based. These preferences will, when clicked, open a dialog showing the
* actual preference controls.
*
* @attr ref android.R.styleable#DialogPreference_dialogTitle
* @attr ref android.R.styleable#DialogPreference_dialogMessage
* @attr ref android.R.styleable#DialogPreference_dialogIcon
* @attr ref android.R.styleable#DialogPreference_dialogLayout
* @attr ref android.R.styleable#DialogPreference_positiveButtonText
* @attr ref android.R.styleable#DialogPreference_negativeButtonText
*
* @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
* <a href="{@docRoot}reference/androidx/preference/package-summary.html">
* Preference Library</a> for consistent behavior across all devices. For more information on
* using the AndroidX Preference Library see
* <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
@Deprecated
public abstract class DialogPreference extends Preference implements
DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
PreferenceManager.OnActivityDestroyListener {
@UnsupportedAppUsage
private AlertDialog.Builder mBuilder;
@UnsupportedAppUsage
private CharSequence mDialogTitle;
@UnsupportedAppUsage
private CharSequence mDialogMessage;
@UnsupportedAppUsage
private Drawable mDialogIcon;
@UnsupportedAppUsage
private CharSequence mPositiveButtonText;
@UnsupportedAppUsage
private CharSequence mNegativeButtonText;
private int mDialogLayoutResId;
/** The dialog, if it is showing. */
@UnsupportedAppUsage
private Dialog mDialog;
/** Which button was clicked. */
@UnsupportedAppUsage
private int mWhichButtonClicked;
/** Dismiss the dialog on the UI thread, but not inline with handlers */
private final Runnable mDismissRunnable = new Runnable() {
@Override
public void run() {
mDialog.dismiss();
}
};
public DialogPreference(
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.DialogPreference, defStyleAttr, defStyleRes);
mDialogTitle = a.getString(com.android.internal.R.styleable.DialogPreference_dialogTitle);
if (mDialogTitle == null) {
// Fallback on the regular title of the preference
// (the one that is seen in the list)
mDialogTitle = getTitle();
}
mDialogMessage = a.getString(com.android.internal.R.styleable.DialogPreference_dialogMessage);
mDialogIcon = a.getDrawable(com.android.internal.R.styleable.DialogPreference_dialogIcon);
mPositiveButtonText = a.getString(com.android.internal.R.styleable.DialogPreference_positiveButtonText);
mNegativeButtonText = a.getString(com.android.internal.R.styleable.DialogPreference_negativeButtonText);
mDialogLayoutResId = a.getResourceId(com.android.internal.R.styleable.DialogPreference_dialogLayout,
mDialogLayoutResId);
a.recycle();
}
public DialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public DialogPreference(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle);
}
public DialogPreference(Context context) {
this(context, null);
}
/**
* Sets the title of the dialog. This will be shown on subsequent dialogs.
*
* @param dialogTitle The title.
*/
public void setDialogTitle(CharSequence dialogTitle) {
mDialogTitle = dialogTitle;
}
/**
* @see #setDialogTitle(CharSequence)
* @param dialogTitleResId The dialog title as a resource.
*/
public void setDialogTitle(int dialogTitleResId) {
setDialogTitle(getContext().getString(dialogTitleResId));
}
/**
* Returns the title to be shown on subsequent dialogs.
* @return The title.
*/
public CharSequence getDialogTitle() {
return mDialogTitle;
}
/**
* Sets the message of the dialog. This will be shown on subsequent dialogs.
* <p>
* This message forms the content View of the dialog and conflicts with
* list-based dialogs, for example. If setting a custom View on a dialog via
* {@link #setDialogLayoutResource(int)}, include a text View with ID
* {@link android.R.id#message} and it will be populated with this message.
*
* @param dialogMessage The message.
*/
public void setDialogMessage(CharSequence dialogMessage) {
mDialogMessage = dialogMessage;
}
/**
* @see #setDialogMessage(CharSequence)
* @param dialogMessageResId The dialog message as a resource.
*/
public void setDialogMessage(int dialogMessageResId) {
setDialogMessage(getContext().getString(dialogMessageResId));
}
/**
* Returns the message to be shown on subsequent dialogs.
* @return The message.
*/
public CharSequence getDialogMessage() {
return mDialogMessage;
}
/**
* Sets the icon of the dialog. This will be shown on subsequent dialogs.
*
* @param dialogIcon The icon, as a {@link Drawable}.
*/
public void setDialogIcon(Drawable dialogIcon) {
mDialogIcon = dialogIcon;
}
/**
* Sets the icon (resource ID) of the dialog. This will be shown on
* subsequent dialogs.
*
* @param dialogIconRes The icon, as a resource ID.
*/
public void setDialogIcon(@DrawableRes int dialogIconRes) {
mDialogIcon = getContext().getDrawable(dialogIconRes);
}
/**
* Returns the icon to be shown on subsequent dialogs.
* @return The icon, as a {@link Drawable}.
*/
public Drawable getDialogIcon() {
return mDialogIcon;
}
/**
* Sets the text of the positive button of the dialog. This will be shown on
* subsequent dialogs.
*
* @param positiveButtonText The text of the positive button.
*/
public void setPositiveButtonText(CharSequence positiveButtonText) {
mPositiveButtonText = positiveButtonText;
}
/**
* @see #setPositiveButtonText(CharSequence)
* @param positiveButtonTextResId The positive button text as a resource.
*/
public void setPositiveButtonText(@StringRes int positiveButtonTextResId) {
setPositiveButtonText(getContext().getString(positiveButtonTextResId));
}
/**
* Returns the text of the positive button to be shown on subsequent
* dialogs.
*
* @return The text of the positive button.
*/
public CharSequence getPositiveButtonText() {
return mPositiveButtonText;
}
/**
* Sets the text of the negative button of the dialog. This will be shown on
* subsequent dialogs.
*
* @param negativeButtonText The text of the negative button.
*/
public void setNegativeButtonText(CharSequence negativeButtonText) {
mNegativeButtonText = negativeButtonText;
}
/**
* @see #setNegativeButtonText(CharSequence)
* @param negativeButtonTextResId The negative button text as a resource.
*/
public void setNegativeButtonText(@StringRes int negativeButtonTextResId) {
setNegativeButtonText(getContext().getString(negativeButtonTextResId));
}
/**
* Returns the text of the negative button to be shown on subsequent
* dialogs.
*
* @return The text of the negative button.
*/
public CharSequence getNegativeButtonText() {
return mNegativeButtonText;
}
/**
* Sets the layout resource that is inflated as the {@link View} to be shown
* as the content View of subsequent dialogs.
*
* @param dialogLayoutResId The layout resource ID to be inflated.
* @see #setDialogMessage(CharSequence)
*/
public void setDialogLayoutResource(int dialogLayoutResId) {
mDialogLayoutResId = dialogLayoutResId;
}
/**
* Returns the layout resource that is used as the content View for
* subsequent dialogs.
*
* @return The layout resource.
*/
public int getDialogLayoutResource() {
return mDialogLayoutResId;
}
/**
* Prepares the dialog builder to be shown when the preference is clicked.
* Use this to set custom properties on the dialog.
* <p>
* Do not {@link AlertDialog.Builder#create()} or
* {@link AlertDialog.Builder#show()}.
*/
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
}
@Override
protected void onClick() {
if (mDialog != null && mDialog.isShowing()) return;
showDialog(null);
}
/**
* Shows the dialog associated with this Preference. This is normally initiated
* automatically on clicking on the preference. Call this method if you need to
* show the dialog on some other event.
*
* @param state Optional instance state to restore on the dialog
*/
protected void showDialog(Bundle state) {
Context context = getContext();
mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
mBuilder = new AlertDialog.Builder(context)
.setTitle(mDialogTitle)
.setIcon(mDialogIcon)
.setPositiveButton(mPositiveButtonText, this)
.setNegativeButton(mNegativeButtonText, this);
View contentView = onCreateDialogView();
if (contentView != null) {
onBindDialogView(contentView);
mBuilder.setView(contentView);
} else {
mBuilder.setMessage(mDialogMessage);
}
onPrepareDialogBuilder(mBuilder);
getPreferenceManager().registerOnActivityDestroyListener(this);
// Create the dialog
final Dialog dialog = mDialog = mBuilder.create();
if (state != null) {
dialog.onRestoreInstanceState(state);
}
if (needInputMethod()) {
requestInputMethod(dialog);
}
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
removeDismissCallbacks();
}
});
dialog.setOnDismissListener(this);
dialog.show();
}
/**
* Get the DecorView.
* @return the DecorView for the current dialog window, if it exists.
* If the window does not exist, null is returned.
*/
@Nullable
private View getDecorView() {
if (mDialog != null && mDialog.getWindow() != null) {
return mDialog.getWindow().getDecorView();
}
return null;
}
void postDismiss() {
removeDismissCallbacks();
View decorView = getDecorView();
if (decorView != null) {
// If decorView is null, dialog was already dismissed
decorView.post(mDismissRunnable);
}
}
private void removeDismissCallbacks() {
View decorView = getDecorView();
if (decorView != null) {
decorView.removeCallbacks(mDismissRunnable);
}
}
/**
* Returns whether the preference needs to display a soft input method when the dialog
* is displayed. Default is false. Subclasses should override this method if they need
* the soft input method brought up automatically.
* @hide
*/
protected boolean needInputMethod() {
return false;
}
/**
* Sets the required flags on the dialog window to enable input method window to show up.
*/
private void requestInputMethod(Dialog dialog) {
Window window = dialog.getWindow();
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
}
/**
* Creates the content view for the dialog (if a custom content view is
* required). By default, it inflates the dialog layout resource if it is
* set.
*
* @return The content View for the dialog.
* @see #setLayoutResource(int)
*/
protected View onCreateDialogView() {
if (mDialogLayoutResId == 0) {
return null;
}
LayoutInflater inflater = LayoutInflater.from(mBuilder.getContext());
return inflater.inflate(mDialogLayoutResId, null);
}
/**
* Binds views in the content View of the dialog to data.
* <p>
* Make sure to call through to the superclass implementation.
*
* @param view The content View of the dialog, if it is custom.
*/
@CallSuper
protected void onBindDialogView(View view) {
View dialogMessageView = view.findViewById(com.android.internal.R.id.message);
if (dialogMessageView != null) {
final CharSequence message = getDialogMessage();
int newVisibility = View.GONE;
if (!TextUtils.isEmpty(message)) {
if (dialogMessageView instanceof TextView) {
((TextView) dialogMessageView).setText(message);
}
newVisibility = View.VISIBLE;
}
if (dialogMessageView.getVisibility() != newVisibility) {
dialogMessageView.setVisibility(newVisibility);
}
}
}
public void onClick(DialogInterface dialog, int which) {
mWhichButtonClicked = which;
}
@Override
public void onDismiss(DialogInterface dialog) {
removeDismissCallbacks();
getPreferenceManager().unregisterOnActivityDestroyListener(this);
mDialog = null;
onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE);
}
/**
* Called when the dialog is dismissed and should be used to save data to
* the {@link SharedPreferences}.
*
* @param positiveResult Whether the positive button was clicked (true), or
* the negative button was clicked or the dialog was canceled (false).
*/
protected void onDialogClosed(boolean positiveResult) {
}
/**
* Gets the dialog that is shown by this preference.
*
* @return The dialog, or null if a dialog is not being shown.
*/
public Dialog getDialog() {
return mDialog;
}
/**
* {@inheritDoc}
*/
public void onActivityDestroy() {
if (mDialog == null || !mDialog.isShowing()) {
return;
}
mDialog.dismiss();
}
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (mDialog == null || !mDialog.isShowing()) {
return superState;
}
final SavedState myState = new SavedState(superState);
myState.isDialogShowing = true;
myState.dialogBundle = mDialog.onSaveInstanceState();
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
if (myState.isDialogShowing) {
showDialog(myState.dialogBundle);
}
}
private static class SavedState extends BaseSavedState {
boolean isDialogShowing;
Bundle dialogBundle;
public SavedState(Parcel source) {
super(source);
isDialogShowing = source.readInt() == 1;
dialogBundle = source.readBundle();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(isDialogShowing ? 1 : 0);
dest.writeBundle(dialogBundle);
}
public SavedState(Parcelable superState) {
super(superState);
}
public static final @android.annotation.NonNull Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}