diff --git a/app/src/main/java/com/hippo/preference/DialogPreference.java b/app/src/main/java/com/hippo/preference/DialogPreference.java new file mode 100644 index 000000000..5b757bbcd --- /dev/null +++ b/app/src/main/java/com/hippo/preference/DialogPreference.java @@ -0,0 +1,390 @@ +/* + * 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.annotation.TargetApi; +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.Build; +import android.os.Bundle; +import android.os.Parcelable; +import android.preference.Preference; +import android.support.annotation.DrawableRes; +import android.support.annotation.StringRes; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AlertDialog; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; + +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 static final String KEY_SUPER = "super"; + private static final String KEY_IS_DIALOG_SHOWING = "is_dialog_showing"; + private static final String KEY_DIALOG_BUNDLE = "dialog_bundle"; + + 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, 0); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public DialogPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(context, attrs, defStyleAttr, defStyleRes); + } + + 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(); + } + + /** + * 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 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 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. + */ + 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. + *
+ * 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);
+ }
+
+ onPrepareDialogBuilder(mBuilder);
+
+ // 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 Bundle myState = new Bundle();
+ myState.putParcelable(KEY_SUPER, superState);
+ myState.putBoolean(KEY_IS_DIALOG_SHOWING, true);
+ myState.putBundle(KEY_DIALOG_BUNDLE, mDialog.onSaveInstanceState());
+ return myState;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ if (state == null || !state.getClass().equals(Bundle.class)) {
+ // Didn't save state for us in onSaveInstanceState
+ super.onRestoreInstanceState(state);
+ return;
+ }
+
+ Bundle myState = (Bundle) state;
+ super.onRestoreInstanceState(myState.getParcelable(KEY_SUPER));
+ if (myState.getBoolean(KEY_IS_DIALOG_SHOWING)) {
+ showDialog(myState.getBundle(KEY_DIALOG_BUNDLE));
+ }
+ }
+}
diff --git a/app/src/main/java/com/hippo/preference/SimpleDialogPreference.java b/app/src/main/java/com/hippo/preference/MessageDialogPreference.java
similarity index 52%
rename from app/src/main/java/com/hippo/preference/SimpleDialogPreference.java
rename to app/src/main/java/com/hippo/preference/MessageDialogPreference.java
index 9e0cecc03..214378ae2 100644
--- a/app/src/main/java/com/hippo/preference/SimpleDialogPreference.java
+++ b/app/src/main/java/com/hippo/preference/MessageDialogPreference.java
@@ -17,59 +17,74 @@
package com.hippo.preference;
import android.annotation.TargetApi;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
-import android.preference.DialogPreference;
+import android.support.v7.app.AlertDialog;
+import android.text.method.LinkMovementMethod;
import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
import com.hippo.ehviewer.R;
import com.hippo.text.Html;
-// TODO Add html attr
-// TODO Add url clickable
-public class SimpleDialogPreference extends DialogPreference {
+public class MessageDialogPreference extends DialogPreference {
- private CharSequence mMessage;
+ private CharSequence mDialogMessage;
+ private boolean mDialogMessageLinkify;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
- public SimpleDialogPreference(Context context) {
+ public MessageDialogPreference(Context context) {
super(context);
init(context, null, 0, 0);
}
- public SimpleDialogPreference(Context context, AttributeSet attrs) {
+ public MessageDialogPreference(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0, 0);
}
- public SimpleDialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ public MessageDialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr, 0);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
- public SimpleDialogPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ public MessageDialogPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs, defStyleAttr, defStyleRes);
}
public void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SimpleDialogPreference, defStyleAttr, defStyleRes);
- setMessage(a.getString(R.styleable.SimpleDialogPreference_message));
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MessageDialogPreference, defStyleAttr, defStyleRes);
+ String message = a.getString(R.styleable.MessageDialogPreference_dialogMessage);
+ if (a.getBoolean(R.styleable.MessageDialogPreference_dialogMessageHtml, false)) {
+ mDialogMessage = Html.fromHtml(message);
+ } else {
+ mDialogMessage = message;
+ }
+ mDialogMessageLinkify = a.getBoolean(R.styleable.MessageDialogPreference_dialogMessageLinkify, false);
a.recycle();
}
- public void setMessage(String message) {
- mMessage = Html.fromHtml(message);
- }
-
@Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
super.onPrepareDialogBuilder(builder);
- builder.setMessage(mMessage);
+ builder.setMessage(mDialogMessage);
builder.setPositiveButton(android.R.string.ok, null);
builder.setNegativeButton(null, null);
}
+
+ @Override
+ protected void onDialogCreated(AlertDialog dialog) {
+ super.onDialogCreated(dialog);
+
+ if (mDialogMessageLinkify) {
+ final View messageView = dialog.findViewById(android.R.id.message);
+ if (null != messageView && messageView instanceof TextView) {
+ ((TextView) messageView).setMovementMethod(LinkMovementMethod.getInstance());
+ }
+ }
+ }
}
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index a91dda631..719381c07 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -108,10 +108,6 @@