| /* |
| * Copyright (C) 2015 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.support.v14.preference; |
| |
| import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; |
| |
| import android.app.AlertDialog; |
| import android.app.Dialog; |
| import android.app.DialogFragment; |
| import android.app.Fragment; |
| import android.content.Context; |
| import android.content.DialogInterface; |
| import android.graphics.Bitmap; |
| import android.graphics.Canvas; |
| import android.graphics.drawable.BitmapDrawable; |
| import android.graphics.drawable.Drawable; |
| import android.os.Bundle; |
| import android.support.annotation.LayoutRes; |
| import android.support.annotation.NonNull; |
| import android.support.annotation.RestrictTo; |
| import android.support.v7.preference.DialogPreference; |
| import android.text.TextUtils; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.Window; |
| import android.view.WindowManager; |
| import android.widget.TextView; |
| |
| /** |
| * Abstract base class which presents a dialog associated with a |
| * {@link android.support.v7.preference.DialogPreference}. Since the preference object may |
| * not be available during fragment re-creation, the necessary information for displaying the dialog |
| * is read once during the initial call to {@link #onCreate(Bundle)} and saved/restored in the saved |
| * instance state. Custom subclasses should also follow this pattern. |
| */ |
| public abstract class PreferenceDialogFragment extends DialogFragment implements |
| DialogInterface.OnClickListener { |
| |
| protected static final String ARG_KEY = "key"; |
| |
| private static final String SAVE_STATE_TITLE = "PreferenceDialogFragment.title"; |
| private static final String SAVE_STATE_POSITIVE_TEXT = "PreferenceDialogFragment.positiveText"; |
| private static final String SAVE_STATE_NEGATIVE_TEXT = "PreferenceDialogFragment.negativeText"; |
| private static final String SAVE_STATE_MESSAGE = "PreferenceDialogFragment.message"; |
| private static final String SAVE_STATE_LAYOUT = "PreferenceDialogFragment.layout"; |
| private static final String SAVE_STATE_ICON = "PreferenceDialogFragment.icon"; |
| |
| private DialogPreference mPreference; |
| |
| private CharSequence mDialogTitle; |
| private CharSequence mPositiveButtonText; |
| private CharSequence mNegativeButtonText; |
| private CharSequence mDialogMessage; |
| private @LayoutRes int mDialogLayoutRes; |
| |
| private BitmapDrawable mDialogIcon; |
| |
| /** Which button was clicked. */ |
| private int mWhichButtonClicked; |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| final Fragment rawFragment = getTargetFragment(); |
| if (!(rawFragment instanceof DialogPreference.TargetFragment)) { |
| throw new IllegalStateException("Target fragment must implement TargetFragment" + |
| " interface"); |
| } |
| |
| final DialogPreference.TargetFragment fragment = |
| (DialogPreference.TargetFragment) rawFragment; |
| |
| final String key = getArguments().getString(ARG_KEY); |
| if (savedInstanceState == null) { |
| mPreference = (DialogPreference) fragment.findPreference(key); |
| mDialogTitle = mPreference.getDialogTitle(); |
| mPositiveButtonText = mPreference.getPositiveButtonText(); |
| mNegativeButtonText = mPreference.getNegativeButtonText(); |
| mDialogMessage = mPreference.getDialogMessage(); |
| mDialogLayoutRes = mPreference.getDialogLayoutResource(); |
| |
| final Drawable icon = mPreference.getDialogIcon(); |
| if (icon == null || icon instanceof BitmapDrawable) { |
| mDialogIcon = (BitmapDrawable) icon; |
| } else { |
| final Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), |
| icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); |
| final Canvas canvas = new Canvas(bitmap); |
| icon.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); |
| icon.draw(canvas); |
| mDialogIcon = new BitmapDrawable(getResources(), bitmap); |
| } |
| } else { |
| mDialogTitle = savedInstanceState.getCharSequence(SAVE_STATE_TITLE); |
| mPositiveButtonText = savedInstanceState.getCharSequence(SAVE_STATE_POSITIVE_TEXT); |
| mNegativeButtonText = savedInstanceState.getCharSequence(SAVE_STATE_NEGATIVE_TEXT); |
| mDialogMessage = savedInstanceState.getCharSequence(SAVE_STATE_MESSAGE); |
| mDialogLayoutRes = savedInstanceState.getInt(SAVE_STATE_LAYOUT, 0); |
| final Bitmap bitmap = savedInstanceState.getParcelable(SAVE_STATE_ICON); |
| if (bitmap != null) { |
| mDialogIcon = new BitmapDrawable(getResources(), bitmap); |
| } |
| } |
| } |
| |
| @Override |
| public void onSaveInstanceState(@NonNull Bundle outState) { |
| super.onSaveInstanceState(outState); |
| |
| outState.putCharSequence(SAVE_STATE_TITLE, mDialogTitle); |
| outState.putCharSequence(SAVE_STATE_POSITIVE_TEXT, mPositiveButtonText); |
| outState.putCharSequence(SAVE_STATE_NEGATIVE_TEXT, mNegativeButtonText); |
| outState.putCharSequence(SAVE_STATE_MESSAGE, mDialogMessage); |
| outState.putInt(SAVE_STATE_LAYOUT, mDialogLayoutRes); |
| if (mDialogIcon != null) { |
| outState.putParcelable(SAVE_STATE_ICON, mDialogIcon.getBitmap()); |
| } |
| } |
| |
| @Override |
| public @NonNull |
| Dialog onCreateDialog(Bundle savedInstanceState) { |
| final Context context = getActivity(); |
| mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE; |
| |
| final AlertDialog.Builder builder = new AlertDialog.Builder(context) |
| .setTitle(mDialogTitle) |
| .setIcon(mDialogIcon) |
| .setPositiveButton(mPositiveButtonText, this) |
| .setNegativeButton(mNegativeButtonText, this); |
| |
| View contentView = onCreateDialogView(context); |
| if (contentView != null) { |
| onBindDialogView(contentView); |
| builder.setView(contentView); |
| } else { |
| builder.setMessage(mDialogMessage); |
| } |
| |
| onPrepareDialogBuilder(builder); |
| |
| // Create the dialog |
| final Dialog dialog = builder.create(); |
| if (needInputMethod()) { |
| requestInputMethod(dialog); |
| } |
| |
| return dialog; |
| } |
| |
| /** |
| * Get the preference that requested this dialog. Available after {@link #onCreate(Bundle)} has |
| * been called on the {@link PreferenceFragment} which launched this dialog. |
| * |
| * @return The {@link DialogPreference} associated with this |
| * dialog. |
| */ |
| public DialogPreference getPreference() { |
| if (mPreference == null) { |
| final String key = getArguments().getString(ARG_KEY); |
| final DialogPreference.TargetFragment fragment = |
| (DialogPreference.TargetFragment) getTargetFragment(); |
| mPreference = (DialogPreference) fragment.findPreference(key); |
| } |
| return mPreference; |
| } |
| |
| /** |
| * 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) {} |
| |
| /** |
| * 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 |
| */ |
| @RestrictTo(LIBRARY_GROUP) |
| 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 DialogPreference#setLayoutResource(int) |
| */ |
| protected View onCreateDialogView(Context context) { |
| final int resId = mDialogLayoutRes; |
| if (resId == 0) { |
| return null; |
| } |
| |
| LayoutInflater inflater = LayoutInflater.from(context); |
| return inflater.inflate(resId, 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. |
| */ |
| protected void onBindDialogView(View view) { |
| View dialogMessageView = view.findViewById(android.R.id.message); |
| |
| if (dialogMessageView != null) { |
| final CharSequence message = mDialogMessage; |
| 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); |
| } |
| } |
| } |
| |
| @Override |
| public void onClick(DialogInterface dialog, int which) { |
| mWhichButtonClicked = which; |
| } |
| |
| @Override |
| public void onDismiss(DialogInterface dialog) { |
| super.onDismiss(dialog); |
| onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE); |
| } |
| |
| public abstract void onDialogClosed(boolean positiveResult); |
| } |