Add PreferenceDialogFragments for Chassis.
This is needed so that the dialog shown when selecting preferences that
launch dialogs will have the OEM controllable system theme. Classes are
kept as-is from their support lib equivalent save the addition of
documentation, static factory methods, and minor formatting.
Very similar to ag/6204405
Bug: 140443143
Test: manual in Paint Booth app.
Change-Id: I761e03c7064519263e00a6f54b11fee88f3d6bd3
diff --git a/car-chassis-lib/Android.mk b/car-chassis-lib/Android.mk
index 5ae4041..88e6efd 100644
--- a/car-chassis-lib/Android.mk
+++ b/car-chassis-lib/Android.mk
@@ -39,6 +39,7 @@
LOCAL_STATIC_ANDROID_LIBRARIES += \
androidx.annotation_annotation \
androidx-constraintlayout_constraintlayout \
+ androidx.preference_preference \
androidx.recyclerview_recyclerview
LOCAL_STATIC_JAVA_LIBRARIES += \
diff --git a/car-chassis-lib/src/com/android/car/chassis/preference/EditTextPreferenceDialogFragment.java b/car-chassis-lib/src/com/android/car/chassis/preference/EditTextPreferenceDialogFragment.java
new file mode 100644
index 0000000..b05eb5b
--- /dev/null
+++ b/car-chassis-lib/src/com/android/car/chassis/preference/EditTextPreferenceDialogFragment.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 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.chassis.preference;
+
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+
+import androidx.annotation.NonNull;
+import androidx.preference.EditTextPreference;
+
+/**
+ * Presents a dialog with an {@link EditText} associated with an {@link EditTextPreference}.
+ *
+ * <p>Note: this is borrowed as-is from androidx.preference.EditTextPreferenceDialogFragmentCompat
+ * with updates to formatting to match the project style. Automotive applications should use this
+ * implementations in order to launch the system themed platform {@link AlertDialog} instead of the
+ * one in the support library.
+ */
+public class EditTextPreferenceDialogFragment extends PreferenceDialogFragment {
+
+ private static final String SAVE_STATE_TEXT = "EditTextPreferenceDialogFragment.text";
+
+ private EditText mEditText;
+
+ private CharSequence mText;
+
+ /**
+ * Returns a new instance of {@link EditTextPreferenceDialogFragment} for the {@link
+ * EditTextPreference} with the given {@code key}.
+ */
+ public static EditTextPreferenceDialogFragment newInstance(String key) {
+ EditTextPreferenceDialogFragment fragment =
+ new EditTextPreferenceDialogFragment();
+ Bundle b = new Bundle(/* capacity= */ 1);
+ b.putString(ARG_KEY, key);
+ fragment.setArguments(b);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState == null) {
+ mText = getEditTextPreference().getText();
+ } else {
+ mText = savedInstanceState.getCharSequence(SAVE_STATE_TEXT);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putCharSequence(SAVE_STATE_TEXT, mText);
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+
+ mEditText = view.findViewById(android.R.id.edit);
+
+ if (mEditText == null) {
+ throw new IllegalStateException(
+ "Dialog view must contain an EditText with id @android:id/edit");
+ }
+
+ mEditText.requestFocus();
+ mEditText.setText(mText);
+ // Place cursor at the end
+ mEditText.setSelection(mEditText.getText().length());
+ }
+
+ private EditTextPreference getEditTextPreference() {
+ return (EditTextPreference) getPreference();
+ }
+
+ @Override
+ protected boolean needInputMethod() {
+ return true;
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ if (positiveResult) {
+ String value = mEditText.getText().toString();
+ if (getEditTextPreference().callChangeListener(value)) {
+ getEditTextPreference().setText(value);
+ }
+ }
+ }
+
+}
diff --git a/car-chassis-lib/src/com/android/car/chassis/preference/ListPreferenceDialogFragment.java b/car-chassis-lib/src/com/android/car/chassis/preference/ListPreferenceDialogFragment.java
new file mode 100644
index 0000000..fb0b9b2
--- /dev/null
+++ b/car-chassis-lib/src/com/android/car/chassis/preference/ListPreferenceDialogFragment.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 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.chassis.preference;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.preference.ListPreference;
+
+/**
+ * Presents a dialog with a list of options associated with a {@link ListPreference}.
+ *
+ * <p>Note: this is borrowed as-is from androidx.preference.ListPreferenceDialogFragmentCompat
+ * with updates to formatting to match the project style. Automotive applications should use this
+ * implementations in order to launch the system themed platform {@link AlertDialog} instead of the
+ * one in the support library.
+ */
+public class ListPreferenceDialogFragment extends PreferenceDialogFragment {
+
+ private static final String SAVE_STATE_INDEX = "ListPreferenceDialogFragment.index";
+ private static final String SAVE_STATE_ENTRIES = "ListPreferenceDialogFragment.entries";
+ private static final String SAVE_STATE_ENTRY_VALUES =
+ "ListPreferenceDialogFragment.entryValues";
+
+ private int mClickedDialogEntryIndex;
+ private CharSequence[] mEntries;
+ private CharSequence[] mEntryValues;
+
+ /**
+ * Returns a new instance of {@link ListPreferenceDialogFragment} for the {@link
+ * ListPreference} with the given {@code key}.
+ */
+ public static ListPreferenceDialogFragment newInstance(String key) {
+ ListPreferenceDialogFragment fragment = new ListPreferenceDialogFragment();
+ Bundle b = new Bundle(/* capacity= */ 1);
+ b.putString(ARG_KEY, key);
+ fragment.setArguments(b);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState == null) {
+ ListPreference preference = getListPreference();
+
+ if (preference.getEntries() == null || preference.getEntryValues() == null) {
+ throw new IllegalStateException(
+ "ListPreference requires an entries array and an entryValues array.");
+ }
+
+ mClickedDialogEntryIndex = preference.findIndexOfValue(preference.getValue());
+ mEntries = preference.getEntries();
+ mEntryValues = preference.getEntryValues();
+ } else {
+ mClickedDialogEntryIndex = savedInstanceState.getInt(SAVE_STATE_INDEX, 0);
+ mEntries = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRIES);
+ mEntryValues = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(SAVE_STATE_INDEX, mClickedDialogEntryIndex);
+ outState.putCharSequenceArray(SAVE_STATE_ENTRIES, mEntries);
+ outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
+ }
+
+ private ListPreference getListPreference() {
+ return (ListPreference) getPreference();
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+ super.onPrepareDialogBuilder(builder);
+
+ builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
+ (dialog, which) -> {
+ mClickedDialogEntryIndex = which;
+
+ // Clicking on an item simulates the positive button click, and dismisses the
+ // dialog.
+ ListPreferenceDialogFragment.this.onClick(dialog,
+ DialogInterface.BUTTON_POSITIVE);
+ dialog.dismiss();
+ });
+
+ // The typical interaction for list-based dialogs is to have click-on-an-item dismiss the
+ // dialog instead of the user having to press 'Ok'.
+ builder.setPositiveButton(null, null);
+ }
+
+ @Override
+ public void onDialogClosed(boolean positiveResult) {
+ if (positiveResult && mClickedDialogEntryIndex >= 0) {
+ String value = mEntryValues[mClickedDialogEntryIndex].toString();
+ ListPreference preference = getListPreference();
+ if (preference.callChangeListener(value)) {
+ preference.setValue(value);
+ }
+ }
+ }
+
+}
diff --git a/car-chassis-lib/src/com/android/car/chassis/preference/MultiSelectListPreferenceDialogFragment.java b/car-chassis-lib/src/com/android/car/chassis/preference/MultiSelectListPreferenceDialogFragment.java
new file mode 100644
index 0000000..2c40e40
--- /dev/null
+++ b/car-chassis-lib/src/com/android/car/chassis/preference/MultiSelectListPreferenceDialogFragment.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 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.chassis.preference;
+
+import android.app.AlertDialog;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.preference.MultiSelectListPreference;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Presents a dialog with a list of options associated with a {@link MultiSelectListPreference}.
+ *
+ * <p>Note: this is borrowed as-is from
+ * androidx.preference.MultiSelectListPreferenceDialogFragmentCompat with updates to formatting to
+ * match the project style. Automotive applications should use this implementations in order to
+ * launch the system themed platform {@link AlertDialog} instead of the one in the support library.
+ */
+public class MultiSelectListPreferenceDialogFragment extends PreferenceDialogFragment {
+
+ private static final String SAVE_STATE_VALUES =
+ "MultiSelectListPreferenceDialogFragment.values";
+ private static final String SAVE_STATE_CHANGED =
+ "MultiSelectListPreferenceDialogFragment.changed";
+ private static final String SAVE_STATE_ENTRIES =
+ "MultiSelectListPreferenceDialogFragment.entries";
+ private static final String SAVE_STATE_ENTRY_VALUES =
+ "MultiSelectListPreferenceDialogFragment.entryValues";
+
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ Set<String> mNewValues = new HashSet<>();
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ boolean mPreferenceChanged;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ CharSequence[] mEntries;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ CharSequence[] mEntryValues;
+
+ /**
+ * Returns a new instance of {@link MultiSelectListPreferenceDialogFragment} for the {@link
+ * MultiSelectListPreference} with the given {@code key}.
+ */
+ public static MultiSelectListPreferenceDialogFragment newInstance(String key) {
+ final MultiSelectListPreferenceDialogFragment fragment =
+ new MultiSelectListPreferenceDialogFragment();
+ final Bundle b = new Bundle(1);
+ b.putString(ARG_KEY, key);
+ fragment.setArguments(b);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ final MultiSelectListPreference preference = getListPreference();
+
+ if (preference.getEntries() == null || preference.getEntryValues() == null) {
+ throw new IllegalStateException(
+ "MultiSelectListPreference requires an entries array and an entryValues "
+ + "array.");
+ }
+
+ mNewValues.clear();
+ mNewValues.addAll(preference.getValues());
+ mPreferenceChanged = false;
+ mEntries = preference.getEntries();
+ mEntryValues = preference.getEntryValues();
+ } else {
+ mNewValues.clear();
+ mNewValues.addAll(savedInstanceState.getStringArrayList(SAVE_STATE_VALUES));
+ mPreferenceChanged = savedInstanceState.getBoolean(SAVE_STATE_CHANGED, false);
+ mEntries = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRIES);
+ mEntryValues = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putStringArrayList(SAVE_STATE_VALUES, new ArrayList<>(mNewValues));
+ outState.putBoolean(SAVE_STATE_CHANGED, mPreferenceChanged);
+ outState.putCharSequenceArray(SAVE_STATE_ENTRIES, mEntries);
+ outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
+ }
+
+ private MultiSelectListPreference getListPreference() {
+ return (MultiSelectListPreference) getPreference();
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+ super.onPrepareDialogBuilder(builder);
+
+ final int entryCount = mEntryValues.length;
+ final boolean[] checkedItems = new boolean[entryCount];
+ for (int i = 0; i < entryCount; i++) {
+ checkedItems[i] = mNewValues.contains(mEntryValues[i].toString());
+ }
+ builder.setMultiChoiceItems(mEntries, checkedItems,
+ (dialog, which, isChecked) -> {
+ if (isChecked) {
+ mPreferenceChanged |= mNewValues.add(
+ mEntryValues[which].toString());
+ } else {
+ mPreferenceChanged |= mNewValues.remove(
+ mEntryValues[which].toString());
+ }
+ });
+ }
+
+ @Override
+ public void onDialogClosed(boolean positiveResult) {
+ if (positiveResult && mPreferenceChanged) {
+ final MultiSelectListPreference preference = getListPreference();
+ if (preference.callChangeListener(mNewValues)) {
+ preference.setValues(mNewValues);
+ }
+ }
+ mPreferenceChanged = false;
+ }
+}
diff --git a/car-chassis-lib/src/com/android/car/chassis/preference/PreferenceDialogFragment.java b/car-chassis-lib/src/com/android/car/chassis/preference/PreferenceDialogFragment.java
new file mode 100644
index 0000000..c1f9239
--- /dev/null
+++ b/car-chassis-lib/src/com/android/car/chassis/preference/PreferenceDialogFragment.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright 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.chassis.preference;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+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.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.Fragment;
+import androidx.preference.DialogPreference;
+import androidx.preference.PreferenceFragmentCompat;
+
+/**
+ * Abstract base class which presents a dialog associated with a {@link
+ * androidx.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.
+ *
+ * <p>Note: this is borrowed as-is from androidx.preference.PreferenceDialogFragmentCompat with
+ * updates to formatting to match the project style. Automotive applications should use children of
+ * this fragment in order to launch the system themed platform {@link AlertDialog} instead of the
+ * one in the support library.
+ */
+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;
+ @LayoutRes
+ private int mDialogLayoutRes;
+
+ private BitmapDrawable mDialogIcon;
+
+ /** Which button was clicked. */
+ private int mWhichButtonClicked;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Fragment rawFragment = getTargetFragment();
+ if (!(rawFragment instanceof DialogPreference.TargetFragment)) {
+ throw new IllegalStateException(
+ "Target fragment must implement TargetFragment interface");
+ }
+
+ DialogPreference.TargetFragment fragment =
+ (DialogPreference.TargetFragment) rawFragment;
+
+ 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();
+
+ Drawable icon = mPreference.getDialogIcon();
+ if (icon == null || icon instanceof BitmapDrawable) {
+ mDialogIcon = (BitmapDrawable) icon;
+ } else {
+ Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(),
+ icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ 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);
+ 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
+ @NonNull
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Context context = getActivity();
+ mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
+
+ 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
+ Dialog dialog = builder.create();
+ if (needInputMethod()) {
+ // Request input only after the dialog is shown. This is to prevent an issue where the
+ // dialog view collapsed the content on small displays.
+ dialog.setOnShowListener(d -> requestInputMethod(dialog));
+ }
+
+ return dialog;
+ }
+
+ /**
+ * Get the preference that requested this dialog. Available after {@link #onCreate(Bundle)} has
+ * been called on the {@link PreferenceFragmentCompat} which launched this dialog.
+ *
+ * @return the {@link DialogPreference} associated with this dialog.
+ */
+ public DialogPreference getPreference() {
+ if (mPreference == null) {
+ String key = getArguments().getString(ARG_KEY);
+ 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.
+ *
+ * <p>Note: Ensure your subclass manually requests focus (ideally in {@link
+ * #onBindDialogView(View)}) for the input field in order to
+ * correctly attach the input method to the field.
+ */
+ 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) {
+ 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.
+ */
+ @CallSuper
+ protected void onBindDialogView(View view) {
+ View dialogMessageView = view.findViewById(android.R.id.message);
+
+ if (dialogMessageView != null) {
+ 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);
+ }
+
+ /**
+ * Called when the dialog is dismissed.
+ *
+ * @param positiveResult {@code true} if the dialog was dismissed with {@link
+ * DialogInterface#BUTTON_POSITIVE}.
+ */
+ protected abstract void onDialogClosed(boolean positiveResult);
+}