/* * Copyright 2016 Hippo Seven * * 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.hippo.preference; 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.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.Window; import android.view.WindowManager; import androidx.annotation.DrawableRes; import androidx.annotation.StringRes; import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; import androidx.preference.Preference; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.hippo.ehviewer.R; /** * A base class for {@link Preference} objects that are * dialog-based. These preferences will, when clicked, open a dialog showing the * actual preference controls. */ public abstract class DialogPreference extends Preference implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener { private AlertDialog.Builder mBuilder; private CharSequence mDialogTitle; private Drawable mDialogIcon; private CharSequence mPositiveButtonText; private CharSequence mNegativeButtonText; private int mDialogLayoutResId; /** * The dialog, if it is showing. */ private AlertDialog mDialog; /** * Which button was clicked. */ private int mWhichButtonClicked; public DialogPreference(Context context) { super(context); init(context, null, 0, 0); } public DialogPreference(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs, 0, 0); } public DialogPreference(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr, defStyleAttr); } private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DialogPreference, defStyleAttr, defStyleRes); mDialogTitle = a.getString(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(); } mDialogIcon = a.getDrawable(R.styleable.DialogPreference_dialogIcon); mPositiveButtonText = a.getString(R.styleable.DialogPreference_positiveButtonText); mNegativeButtonText = a.getString(R.styleable.DialogPreference_negativeButtonText); mDialogLayoutResId = a.getResourceId(R.styleable.DialogPreference_dialogLayout, mDialogLayoutResId); a.recycle(); } /** * Returns the title to be shown on subsequent dialogs. * * @return The title. */ public CharSequence getDialogTitle() { return mDialogTitle; } /** * Sets the title of the dialog. This will be shown on subsequent dialogs. * * @param dialogTitle The title. */ public void setDialogTitle(CharSequence dialogTitle) { mDialogTitle = dialogTitle; } /** * @param dialogTitleResId The dialog title as a resource. * @see #setDialogTitle(CharSequence) */ public void setDialogTitle(int dialogTitleResId) { setDialogTitle(getContext().getString(dialogTitleResId)); } /** * Returns the icon to be shown on subsequent dialogs. * * @return The icon, as a {@link Drawable}. */ public Drawable getDialogIcon() { return mDialogIcon; } /** * 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 = ContextCompat.getDrawable(getContext(), dialogIconRes); } /** * 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 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; } /** * @param positiveButtonTextResId The positive button text as a resource. * @see #setPositiveButtonText(CharSequence) */ public void setPositiveButtonText(@StringRes int positiveButtonTextResId) { setPositiveButtonText(getContext().getString(positiveButtonTextResId)); } /** * 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 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; } /** * @param negativeButtonTextResId The negative button text as a resource. * @see #setNegativeButtonText(CharSequence) */ public void setNegativeButtonText(@StringRes int negativeButtonTextResId) { setNegativeButtonText(getContext().getString(negativeButtonTextResId)); } /** * Returns the layout resource that is used as the content View for * subsequent dialogs. * * @return The layout resource. */ public int getDialogLayoutResource() { return mDialogLayoutResId; } /** * 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. */ public void setDialogLayoutResource(int dialogLayoutResId) { mDialogLayoutResId = dialogLayoutResId; } /** * Prepares the dialog builder to be shown when the preference is clicked. * Use this to set custom properties on the dialog. *
* 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 MaterialAlertDialogBuilder(context)
.setTitle(mDialogTitle)
.setIcon(mDialogIcon)
.setPositiveButton(mPositiveButtonText, this)
.setNegativeButton(mNegativeButtonText, this);
View contentView = onCreateDialogView();
if (contentView != null) {
onBindDialogView(contentView);
mBuilder.setView(contentView);
}
onPrepareDialogBuilder(mBuilder);
//PreferenceUtils.registerOnActivityDestroyListener(this, this);
// Create the dialog
final AlertDialog dialog = mDialog = mBuilder.create();
if (state != null) {
dialog.onRestoreInstanceState(state);
}
if (needInputMethod()) {
requestInputMethod(dialog);
}
dialog.setOnDismissListener(this);
dialog.show();
onDialogCreated(dialog);
}
/**
* 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.
*/
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.
*
* @param view The content View of the dialog, if it is custom.
*/
protected void onBindDialogView(View view) {
}
protected void onDialogCreated(AlertDialog dialog) {
}
@Override
public void onClick(DialogInterface dialog, int which) {
mWhichButtonClicked = which;
}
@Override
public void onDismiss(DialogInterface dialog) {
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;
}
@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 {
public static final Parcelable.Creator