From 757d9559557b608940c57edc024ae478564e5960 Mon Sep 17 00:00:00 2001 From: Administrator <1760316362@qq.com> Date: Tue, 30 Jul 2019 16:24:28 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=B8=BB=E9=A2=98=E9=A2=9C?= =?UTF-8?q?=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/lib/theme/ATH.kt | 3 + .../app/lib/theme/prefs/ATEColorPreference.kt | 445 ++++++++++++++++++ .../lib/theme/prefs/ATEEditTextPreference.kt | 184 ++++++++ .../app/ui/config/ThemeConfigFragment.kt | 1 + .../ui/main/bookshelf/BookshelfFragment.kt | 1 - app/src/main/res/xml/pref_config_theme.xml | 12 +- app/src/main/res/xml/pref_config_web_dav.xml | 6 +- 7 files changed, 642 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/io/legado/app/lib/theme/prefs/ATEColorPreference.kt create mode 100644 app/src/main/java/io/legado/app/lib/theme/prefs/ATEEditTextPreference.kt diff --git a/app/src/main/java/io/legado/app/lib/theme/ATH.kt b/app/src/main/java/io/legado/app/lib/theme/ATH.kt index 7b5ffd1d1..7553da7b8 100644 --- a/app/src/main/java/io/legado/app/lib/theme/ATH.kt +++ b/app/src/main/java/io/legado/app/lib/theme/ATH.kt @@ -117,6 +117,9 @@ object ATH { if (dialog.getButton(AlertDialog.BUTTON_POSITIVE) != null) { dialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(colorStateList) } + if (dialog.getButton(AlertDialog.BUTTON_NEUTRAL) != null) { + dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setTextColor(colorStateList) + } return dialog } diff --git a/app/src/main/java/io/legado/app/lib/theme/prefs/ATEColorPreference.kt b/app/src/main/java/io/legado/app/lib/theme/prefs/ATEColorPreference.kt new file mode 100644 index 000000000..8e1ec7260 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/prefs/ATEColorPreference.kt @@ -0,0 +1,445 @@ +package io.legado.app.lib.theme.prefs + +import android.content.Context +import android.content.ContextWrapper +import android.content.res.TypedArray +import android.graphics.Color +import android.os.Bundle +import android.util.AttributeSet +import androidx.annotation.ColorInt +import androidx.annotation.StringRes +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.FragmentActivity +import androidx.preference.Preference +import androidx.preference.PreferenceViewHolder +import com.jaredrummler.android.colorpicker.* +import io.legado.app.lib.theme.ATH + +class ATEColorPreference : Preference, + ColorPickerDialogListener { + + private val SIZE_NORMAL = 0 + private val SIZE_LARGE = 1 + + private var onShowDialogListener: OnShowDialogListener? = null + private var color = Color.BLACK + private var showDialog: Boolean = false + @ColorPickerDialog.DialogType + private var dialogType: Int = 0 + private var colorShape: Int = 0 + private var allowPresets: Boolean = false + private var allowCustom: Boolean = false + private var showAlphaSlider: Boolean = false + private var showColorShades: Boolean = false + private var previewSize: Int = 0 + private var presets: IntArray? = null + private var dialogTitle: Int = 0 + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super( + context, + attrs, + defStyleAttr, + defStyleRes + ) { + init(attrs) + } + + private fun init(attrs: AttributeSet) { + isPersistent = true + val a = context.obtainStyledAttributes(attrs, R.styleable.ColorPreference) + showDialog = a.getBoolean(R.styleable.ColorPreference_cpv_showDialog, true) + + dialogType = a.getInt(R.styleable.ColorPreference_cpv_dialogType, ColorPickerDialog.TYPE_PRESETS) + colorShape = a.getInt(R.styleable.ColorPreference_cpv_colorShape, ColorShape.CIRCLE) + allowPresets = a.getBoolean(R.styleable.ColorPreference_cpv_allowPresets, true) + allowCustom = a.getBoolean(R.styleable.ColorPreference_cpv_allowCustom, true) + showAlphaSlider = a.getBoolean(R.styleable.ColorPreference_cpv_showAlphaSlider, false) + showColorShades = a.getBoolean(R.styleable.ColorPreference_cpv_showColorShades, true) + previewSize = a.getInt(R.styleable.ColorPreference_cpv_previewSize, SIZE_NORMAL) + val presetsResId = a.getResourceId(R.styleable.ColorPreference_cpv_colorPresets, 0) + dialogTitle = a.getResourceId(R.styleable.ColorPreference_cpv_dialogTitle, R.string.cpv_default_title) + if (presetsResId != 0) { + presets = context.resources.getIntArray(presetsResId) + } else { + presets = ColorPickerDialog.MATERIAL_COLORS + } + if (colorShape == ColorShape.CIRCLE) { + widgetLayoutResource = + if (previewSize == SIZE_LARGE) R.layout.cpv_preference_circle_large else R.layout.cpv_preference_circle + } else { + widgetLayoutResource = + if (previewSize == SIZE_LARGE) R.layout.cpv_preference_square_large else R.layout.cpv_preference_square + } + a.recycle() + } + + override fun onClick() { + super.onClick() + if (onShowDialogListener != null) { + onShowDialogListener!!.onShowColorPickerDialog(title as String, color) + } else if (showDialog) { + val dialog = ColorPickerDialogCompat.newBuilder() + .setDialogType(dialogType) + .setDialogTitle(dialogTitle) + .setColorShape(colorShape) + .setPresets(presets!!) + .setAllowPresets(allowPresets) + .setAllowCustom(allowCustom) + .setShowAlphaSlider(showAlphaSlider) + .setShowColorShades(showColorShades) + .setColor(color) + .create() + dialog.setColorPickerDialogListener(this) + getActivity().supportFragmentManager + .beginTransaction() + .add(dialog, getFragmentTag()) + .commitAllowingStateLoss() + } + } + + fun getActivity(): FragmentActivity { + val context = context + if (context is FragmentActivity) { + return context + } else if (context is ContextWrapper) { + val baseContext = context.baseContext + if (baseContext is FragmentActivity) { + return baseContext + } + } + throw IllegalStateException("Error getting activity from context") + } + + override fun onAttached() { + super.onAttached() + if (showDialog) { + val fragment = + getActivity().supportFragmentManager.findFragmentByTag(getFragmentTag()) as ColorPickerDialog? + fragment?.setColorPickerDialogListener(this) + } + } + + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + val preview = holder.itemView.findViewById(R.id.cpv_preference_preview_color_panel) as ColorPanelView + preview.color = color + } + + override fun onSetInitialValue(defaultValue: Any?) { + super.onSetInitialValue(defaultValue) + if (defaultValue is Int) { + color = (defaultValue as Int?)!! + persistInt(color) + } else { + color = getPersistedInt(-0x1000000) + } + } + + override fun onGetDefaultValue(a: TypedArray?, index: Int): Any { + return a!!.getInteger(index, Color.BLACK) + } + + override fun onColorSelected(dialogId: Int, @ColorInt color: Int) { + saveValue(color) + } + + override fun onDialogDismissed(dialogId: Int) { + // no-op + } + + /** + * Set the new color + * + * @param color The newly selected color + */ + fun saveValue(@ColorInt color: Int) { + this.color = color + persistInt(this.color) + notifyChanged() + callChangeListener(color) + } + + /** + * Get the colors that will be shown in the [ColorPickerDialog]. + * + * @return An array of color ints + */ + fun getPresets(): IntArray? { + return presets + } + + /** + * Set the colors shown in the [ColorPickerDialog]. + * + * @param presets An array of color ints + */ + fun setPresets(presets: IntArray) { + this.presets = presets + } + + /** + * The listener used for showing the [ColorPickerDialog]. + * Call [.saveValue] after the user chooses a color. + * If this is set then it is up to you to show the dialog. + * + * @param listener The listener to show the dialog + */ + fun setOnShowDialogListener(listener: OnShowDialogListener) { + onShowDialogListener = listener + } + + /** + * The tag used for the [ColorPickerDialog]. + * + * @return The tag + */ + fun getFragmentTag(): String { + return "color_$key" + } + + interface OnShowDialogListener { + + fun onShowColorPickerDialog(title: String, currentColor: Int) + } + + + internal class ColorPickerDialogCompat : ColorPickerDialog() { + + override fun onStart() { + super.onStart() + val alertDialog = dialog as? AlertDialog + alertDialog?.let { + ATH.setAlertDialogTint(it) + } + } + + + companion object { + fun newBuilder(): Builder { + return Builder() + } + + private const val ARG_ID = "id" + private const val ARG_TYPE = "dialogType" + private const val ARG_COLOR = "color" + private const val ARG_ALPHA = "alpha" + private const val ARG_PRESETS = "presets" + private const val ARG_ALLOW_PRESETS = "allowPresets" + private const val ARG_ALLOW_CUSTOM = "allowCustom" + private const val ARG_DIALOG_TITLE = "dialogTitle" + private const val ARG_SHOW_COLOR_SHADES = "showColorShades" + private const val ARG_COLOR_SHAPE = "colorShape" + private const val ARG_PRESETS_BUTTON_TEXT = "presetsButtonText" + private const val ARG_CUSTOM_BUTTON_TEXT = "customButtonText" + private const val ARG_SELECTED_BUTTON_TEXT = "selectedButtonText" + } + + class Builder internal constructor() { + + internal var colorPickerDialogListener: ColorPickerDialogListener? = null + @StringRes + internal var dialogTitle = R.string.cpv_default_title + @StringRes + internal var presetsButtonText = R.string.cpv_presets + @StringRes + internal var customButtonText = R.string.cpv_custom + @StringRes + internal var selectedButtonText = R.string.cpv_select + @DialogType + internal var dialogType = TYPE_PRESETS + internal var presets = MATERIAL_COLORS + @ColorInt + internal var color = Color.BLACK + internal var dialogId = 0 + internal var showAlphaSlider = false + internal var allowPresets = true + internal var allowCustom = true + internal var showColorShades = true + @ColorShape + internal var colorShape = ColorShape.CIRCLE + + /** + * Set the dialog title string resource id + * + * @param dialogTitle The string resource used for the dialog title + * @return This builder object for chaining method calls + */ + fun setDialogTitle(@StringRes dialogTitle: Int): Builder { + this.dialogTitle = dialogTitle + return this + } + + /** + * Set the selected button text string resource id + * + * @param selectedButtonText The string resource used for the selected button text + * @return This builder object for chaining method calls + */ + fun setSelectedButtonText(@StringRes selectedButtonText: Int): Builder { + this.selectedButtonText = selectedButtonText + return this + } + + /** + * Set the presets button text string resource id + * + * @param presetsButtonText The string resource used for the presets button text + * @return This builder object for chaining method calls + */ + fun setPresetsButtonText(@StringRes presetsButtonText: Int): Builder { + this.presetsButtonText = presetsButtonText + return this + } + + /** + * Set the custom button text string resource id + * + * @param customButtonText The string resource used for the custom button text + * @return This builder object for chaining method calls + */ + fun setCustomButtonText(@StringRes customButtonText: Int): Builder { + this.customButtonText = customButtonText + return this + } + + /** + * Set which dialog view to show. + * + * @param dialogType Either [ColorPickerDialog.TYPE_CUSTOM] or [ColorPickerDialog.TYPE_PRESETS]. + * @return This builder object for chaining method calls + */ + fun setDialogType(@DialogType dialogType: Int): Builder { + this.dialogType = dialogType + return this + } + + /** + * Set the colors used for the presets + * + * @param presets An array of color ints. + * @return This builder object for chaining method calls + */ + fun setPresets(presets: IntArray): Builder { + this.presets = presets + return this + } + + /** + * Set the original color + * + * @param color The default color for the color picker + * @return This builder object for chaining method calls + */ + fun setColor(color: Int): Builder { + this.color = color + return this + } + + /** + * Set the dialog id used for callbacks + * + * @param dialogId The id that is sent back to the [ColorPickerDialogListener]. + * @return This builder object for chaining method calls + */ + fun setDialogId(dialogId: Int): Builder { + this.dialogId = dialogId + return this + } + + /** + * Show the alpha slider + * + * @param showAlphaSlider `true` to show the alpha slider. Currently only supported with the [ ]. + * @return This builder object for chaining method calls + */ + fun setShowAlphaSlider(showAlphaSlider: Boolean): Builder { + this.showAlphaSlider = showAlphaSlider + return this + } + + /** + * Show/Hide a neutral button to select preset colors. + * + * @param allowPresets `false` to disable showing the presets button. + * @return This builder object for chaining method calls + */ + fun setAllowPresets(allowPresets: Boolean): Builder { + this.allowPresets = allowPresets + return this + } + + /** + * Show/Hide the neutral button to select a custom color. + * + * @param allowCustom `false` to disable showing the custom button. + * @return This builder object for chaining method calls + */ + fun setAllowCustom(allowCustom: Boolean): Builder { + this.allowCustom = allowCustom + return this + } + + /** + * Show/Hide the color shades in the presets picker + * + * @param showColorShades `false` to hide the color shades. + * @return This builder object for chaining method calls + */ + fun setShowColorShades(showColorShades: Boolean): Builder { + this.showColorShades = showColorShades + return this + } + + /** + * Set the shape of the color panel view. + * + * @param colorShape Either [ColorShape.CIRCLE] or [ColorShape.SQUARE]. + * @return This builder object for chaining method calls + */ + fun setColorShape(colorShape: Int): Builder { + this.colorShape = colorShape + return this + } + + /** + * Create the [ColorPickerDialog] instance. + * + * @return A new [ColorPickerDialog]. + * @see .show + */ + fun create(): ColorPickerDialog { + val dialog = ColorPickerDialogCompat() + val args = Bundle() + args.putInt(ARG_ID, dialogId) + args.putInt(ARG_TYPE, dialogType) + args.putInt(ARG_COLOR, color) + args.putIntArray(ARG_PRESETS, presets) + args.putBoolean(ARG_ALPHA, showAlphaSlider) + args.putBoolean(ARG_ALLOW_CUSTOM, allowCustom) + args.putBoolean(ARG_ALLOW_PRESETS, allowPresets) + args.putInt(ARG_DIALOG_TITLE, dialogTitle) + args.putBoolean(ARG_SHOW_COLOR_SHADES, showColorShades) + args.putInt(ARG_COLOR_SHAPE, colorShape) + args.putInt(ARG_PRESETS_BUTTON_TEXT, presetsButtonText) + args.putInt(ARG_CUSTOM_BUTTON_TEXT, customButtonText) + args.putInt(ARG_SELECTED_BUTTON_TEXT, selectedButtonText) + dialog.arguments = args + return dialog + } + + /** + * Create and show the [ColorPickerDialog] created with this builder. + * + * @param activity The current activity. + */ + fun show(activity: FragmentActivity) { + create().show(activity.supportFragmentManager, "color-picker-dialog") + } + } + + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/prefs/ATEEditTextPreference.kt b/app/src/main/java/io/legado/app/lib/theme/prefs/ATEEditTextPreference.kt new file mode 100644 index 000000000..639ccab86 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/prefs/ATEEditTextPreference.kt @@ -0,0 +1,184 @@ +package io.legado.app.lib.theme.prefs + +import android.annotation.SuppressLint +import android.app.Dialog +import android.content.Context +import android.content.DialogInterface +import android.os.Bundle +import android.os.Parcel +import android.os.Parcelable +import android.preference.Preference +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.WindowManager +import android.widget.EditText +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.preference.EditTextPreference +import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.ThemeStore +import io.legado.app.utils.upTint + +class ATEEditTextPreference(context: Context?, attrs: AttributeSet?) : EditTextPreference( + context, + attrs +), + DialogInterface.OnClickListener, DialogInterface.OnDismissListener { + + private var builder: AlertDialog.Builder? = null + + private var dialog: AlertDialog? = null + + private var editText: EditText? = null + + /** Which button was clicked. */ + private var mWhichButtonClicked: Int = 0 + + override fun onClick() { + if (dialog != null && dialog!!.isShowing) return + + showDialog(null) + } + + protected fun showDialog(state: Bundle?) { + val context = context + + mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE + + builder = AlertDialog.Builder(context) + .setTitle(dialogTitle) + .setIcon(dialogIcon) + .setPositiveButton(positiveButtonText, this) + .setNegativeButton(negativeButtonText, this) + + val builder = this.builder!! + + val contentView = onCreateDialogView() + if (contentView != null) { + onBindDialogView(contentView) + builder.setView(contentView) + } else { + builder.setMessage(dialogMessage) + } + + // Create the dialog + dialog = builder.create() + + val dialog = this.dialog!! + if (state != null) { + dialog.onRestoreInstanceState(state) + } + requestInputMethod(dialog) + dialog.setOnDismissListener(this) + dialog.show() + dialog.upTint() + } + + protected fun onCreateDialogView(): View? { + if (dialogLayoutResource == 0) { + return null + } + + val inflater = LayoutInflater.from(context) + return inflater.inflate(dialogLayoutResource, null) + } + + private fun requestInputMethod(dialog: Dialog?) { + dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) + } + + + protected fun onBindDialogView(view: View) { + editText = view.findViewById(android.R.id.edit) + + if (editText == null) { + throw IllegalStateException("Dialog view must contain an EditText with id" + " @android:id/edit") + } + + view.findViewById(android.R.id.message).visibility = View.GONE + + val editText = this.editText!! + + ATH.setTint(editText, ThemeStore.accentColor(context)) + + editText.requestFocus() + editText.setText(text) + // Place cursor at the end + editText.setSelection(editText.length()) + } + + override fun onClick(dialog: DialogInterface?, which: Int) { + mWhichButtonClicked = which + } + + override fun onDismiss(dialog: DialogInterface?) { + onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE) + + text = editText?.text.toString() + callChangeListener(text) + } + + protected fun onDialogClosed(positiveResult: Boolean) { + + } + + override fun onSaveInstanceState(): Parcelable { + val superState = super.onSaveInstanceState() + if (dialog == null || !dialog!!.isShowing) { + return superState + } + + val myState = SavedState(superState) + myState.isDialogShowing = true + myState.dialogBundle = dialog!!.onSaveInstanceState() + return myState + } + + override fun onRestoreInstanceState(state: Parcelable?) { + if (state == null || state.javaClass != SavedState::class.java) { + // Didn't save state for us in onSaveInstanceState + super.onRestoreInstanceState(state) + return + } + + val myState = state as SavedState? + super.onRestoreInstanceState(myState!!.superState) + if (myState.isDialogShowing) { + showDialog(myState.dialogBundle) + } + } + + private class SavedState : Preference.BaseSavedState { + internal var isDialogShowing: Boolean = false + internal var dialogBundle: Bundle? = null + + @SuppressLint("ParcelClassLoader") + constructor(source: Parcel) : super(source) { + isDialogShowing = source.readInt() == 1 + dialogBundle = source.readBundle() + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeInt(if (isDialogShowing) 1 else 0) + dest.writeBundle(dialogBundle) + } + + constructor(superState: Parcelable) : super(superState) {} + + companion object { + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(`in`: Parcel): SavedState { + return SavedState(`in`) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt index 0f9afe96c..07b60b065 100644 --- a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt @@ -6,6 +6,7 @@ import android.os.Handler import androidx.appcompat.app.AlertDialog import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat +import com.jaredrummler.android.colorpicker.ColorPreferenceCompat import com.jeremyliao.liveeventbus.LiveEventBus import io.legado.app.App import io.legado.app.R diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt index 2947321bc..6bd0a54df 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt @@ -23,7 +23,6 @@ import io.legado.app.ui.bookshelf.BookshelfActivity import io.legado.app.ui.search.SearchActivity import io.legado.app.utils.disableAutoFill import kotlinx.android.synthetic.main.fragment_bookshelf.* -import kotlinx.android.synthetic.main.fragment_find_book.* import kotlinx.android.synthetic.main.view_title_bar.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.launch diff --git a/app/src/main/res/xml/pref_config_theme.xml b/app/src/main/res/xml/pref_config_theme.xml index 04f68f5e9..71af6eeba 100644 --- a/app/src/main/res/xml/pref_config_theme.xml +++ b/app/src/main/res/xml/pref_config_theme.xml @@ -33,7 +33,7 @@ android:title="白天" app:iconSpaceReserved="false"> - - - - - - - - -