/* * Copyright (C) 2015 Jorge Ruesga * * 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.ruesga.android.wallpapers.photophase.preferences; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.SharedPreferences; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.PixelFormat; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.preference.DialogPreference; import android.preference.Preference; import android.support.annotation.NonNull; import android.support.v7.app.AlertDialog; import android.text.Editable; import android.text.InputFilter; import android.text.Spanned; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.widget.EditText; import android.widget.FrameLayout; import com.github.danielnilsson9.colorpickerview.view.ColorPanelView; import com.github.danielnilsson9.colorpickerview.view.ColorPickerView; import com.ruesga.android.wallpapers.photophase.AndroidHelper; import com.ruesga.android.wallpapers.photophase.R; import java.util.Locale; /** * A {@link Preference} that allow to select/pick a color in a new window dialog. */ public class ColorPickerPreference extends DialogPreference { private AlertDialog mDialog; private ColorPanelView mColorPicker; private int mColor; private static class ColorDialogView extends FrameLayout implements TextWatcher { private EditText mColorText; private ColorPickerView mColorPicker; private ColorPanelView mCurrentColor; private ColorPanelView mNewColor; private int mMaxLayoutWidth; private int mMaxLayoutHeight; private boolean mIgnoreTextChanged; public ColorDialogView(Context context) { super(context); init(); } private void init() { DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); mMaxLayoutWidth = mMaxLayoutHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 550, metrics); LayoutInflater inflater = LayoutInflater.from(getContext()); View v = inflater.inflate(R.layout.color_picker_pref_dialog_view, this, false); mColorText = (EditText) v.findViewById(R.id.color_picker_pref_color_text); mColorPicker = (ColorPickerView) v.findViewById(R.id.color_picker_pref_color_picker); mCurrentColor = (ColorPanelView) v.findViewById(R.id.color_picker_pref_color_current); mNewColor = (ColorPanelView) v.findViewById(R.id.color_picker_pref_color_new); // Configure the color picker with alpha slider mColorPicker.setAlphaSliderVisible(true); mColorPicker.setAlphaSliderText(R.string.color_picker_alpha_slider_text); mColorPicker.setOnColorChangedListener(new ColorPickerView.OnColorChangedListener() { @Override public void onColorChanged(int newColor) { mNewColor.setColor(newColor); mIgnoreTextChanged = true; mColorText.setText(String.format("%06X", newColor)); mIgnoreTextChanged = false; } }); // Allow reset by clicking the current color mCurrentColor.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { setColor(mCurrentColor.getColor()); } }); // Configure the edittext and listen for text changes (only allow valid hex colors) mIgnoreTextChanged = false; InputFilter[] filters = new InputFilter[2]; filters[0] = new InputFilter.LengthFilter(8); filters[1] = new InputFilter() { @Override public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { if (start >= end) return ""; String s = source.subSequence(start, end).toString(); StringBuilder sb = new StringBuilder(); int cc = s.length(); for (int i = 0; i < cc; i++) { char c = s.charAt(i); if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { sb.append(c); } } return sb.toString().toUpperCase(Locale.getDefault()); } }; mColorText.setFilters(filters); mColorText.addTextChangedListener(this); addView(v); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int measuredWidth = MeasureSpec.getSize(widthMeasureSpec); if(mMaxLayoutWidth < measuredWidth) { int measureMode = MeasureSpec.getMode(widthMeasureSpec); widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxLayoutWidth, measureMode); } int measuredHeight = MeasureSpec.getSize(heightMeasureSpec); if(mMaxLayoutHeight < measuredHeight) { int measureMode = MeasureSpec.getMode(heightMeasureSpec); heightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxLayoutHeight, measureMode); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } public int getColor() { return mColorPicker.getColor(); } public void setColor(int newColor) { setColor(newColor, false); } private void setColor(int newColor, boolean user) { mColorPicker.setColor(newColor, false); mCurrentColor.setColor(newColor); mNewColor.setColor(newColor); if (!user) { mColorText.setText(String.format("%06X", newColor)); } } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // Ignore } @Override public void afterTextChanged(Editable s) { // Ignore } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (!mIgnoreTextChanged && s.length() == 8) { try { setColor(Color.parseColor("#" + s.toString()), true); } catch (Exception e) {/**NON BLOCK**/} } } } /** * Constructor of <code>ColorPickerPreference</code> * * @param context The current context */ public ColorPickerPreference(Context context) { this(context, null); } /** * Constructor of <code>ColorPickerPreference</code> * * @param context The current context * @param attrs The attributes of the XML tag that is inflating the preference. */ public ColorPickerPreference(Context context, AttributeSet attrs) { super(context, attrs); setWidgetLayoutResource(R.layout.color_picker_pref_item); } /** * Returns the color of the picker. * * @return The color of the picker. */ public int getColor() { return mColor; } /** * Sets the color of the picker and saves it to the {@link SharedPreferences}. * * @param color The new color. */ public void setColor(int color) { // Always persist/notify the first time; don't assume the field's default of false. final boolean changed = mColor != color; if (changed) { mColor = color; // when called from onSetInitialValue the view is still not set if (mColorPicker != null) { mColorPicker.setColor(color); } persistInt(color); notifyDependencyChange(shouldDisableDependents()); notifyChanged(); } } /** * {@inheritDoc} */ @Override protected Object onGetDefaultValue(TypedArray a, int index) { return a.getColor(index, 0); } /** * {@inheritDoc} */ @Override protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { int def = 0; if (defaultValue != null) { def = (int) defaultValue; } setColor(restoreValue ? getPersistedInt(def) : def); } protected void onPrepareDialogBuilderCompat(AlertDialog.Builder builder) { final ColorDialogView v = new ColorDialogView(getContext()); v.setColor(mColor); builder.setView(v); // The color is selected by the user and confirmed by clicking ok builder.setPositiveButton(android.R.string.ok, new OnClickListener() { @Override @SuppressWarnings("synthetic-access") public void onClick(DialogInterface dialog, int which) { int color = v.getColor(); if (callChangeListener(color)) { setColor(color); } dialog.dismiss(); } }); } @Override protected void showDialog(Bundle state) { AlertDialog.Builder builder = new AlertDialog.Builder(getContext()) .setTitle(getDialogTitle()) .setIcon(getDialogIcon()) .setPositiveButton(getPositiveButtonText(), this) .setNegativeButton(getNegativeButtonText(), this); onPrepareDialogBuilderCompat(builder); AndroidHelper.tryRegisterActivityDestroyListener(getPreferenceManager(), this); mDialog = builder.create(); mDialog.getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); mDialog.setOnDismissListener(this); if (state != null) { mDialog.onRestoreInstanceState(state); } mDialog.show(); mDialog.getWindow().setFormat(PixelFormat.RGBA_8888); } @Override protected void onClick() { if (mDialog != null && mDialog.isShowing()) return; showDialog(null); } public void onActivityDestroy() { if (mDialog == null || !mDialog.isShowing()) { return; } mDialog.dismiss(); } @Override protected void onDialogClosed(boolean positiveResult) { super.onDialogClosed(false); mDialog = null; } /** * {@inheritDoc} */ @Override protected void onBindView(@NonNull View view) { super.onBindView(view); View v = view.findViewById(R.id.color_picker); if (v != null && v instanceof ColorPanelView) { mColorPicker = (ColorPanelView)v; mColorPicker.setColor(mColor); } } @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(); myState.color = getColor(); 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()); setColor(myState.color); if (myState.isDialogShowing) { showDialog(myState.dialogBundle); } } /** * A class for managing the instance state of a {@link ColorPickerPreference}. */ static class SavedState extends BaseSavedState { boolean isDialogShowing; Bundle dialogBundle; int color; /** * Constructor of <code>SavedState</code> * * @param source The source */ public SavedState(Parcel source) { super(source); isDialogShowing = source.readInt() == 1; dialogBundle = source.readBundle(getClass().getClassLoader()); color = source.readInt(); } /** * Constructor of <code>SavedState</code> * * @param superState The parcelable state */ public SavedState(Parcelable superState) { super(superState); } /** * {@inheritDoc} */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(isDialogShowing ? 1 : 0); dest.writeBundle(dialogBundle); dest.writeInt(color); } /** * A class that generates instances of the <code>SavedState</code> class from a Parcel. */ @SuppressWarnings("hiding") public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { /** * {@inheritDoc} */ @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } /** * {@inheritDoc} */ @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } }