/* * Copyright (C) 2007 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.launcher; import android.app.Dialog; import android.content.Context; import android.content.res.Resources; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.SweepGradient; import android.graphics.Typeface; import android.graphics.BlurMaskFilter.Blur; import android.graphics.Paint.Style; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.SystemClock; import android.text.Editable; import android.text.TextWatcher; import android.util.StateSet; import android.view.MotionEvent; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Transformation; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; public class ColorPickerDialog extends Dialog { private static int CENTER_X = 100; private static int CENTER_Y = 100; private static int CENTER_RADIUS = 32; public interface OnColorChangedListener { void colorChanged(int color); } private OnColorChangedListener mListener; private int mInitialColor; private class ColorPickerView extends View { private Paint mPaint; private Paint mCenterPaint; private final int[] mColors; private OnColorChangedListener mListener; ColorPickerView(Context c, OnColorChangedListener l, int color) { super(c); mListener = l; mColors = new int[] { 0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 0xFFFFFF00, 0xFFFFFFFF, 0xFF808080, 0xFF000000, 0xFFFF0000 }; Shader s = new SweepGradient(0, 0, mColors, null); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setShader(s); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(CENTER_RADIUS); mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mCenterPaint.setColor(color); mCenterPaint.setStrokeWidth(5); } private boolean mTrackingCenter; private boolean mHighlightCenter; @Override protected void onDraw(Canvas canvas) { float r = CENTER_X - mPaint.getStrokeWidth()*0.8f; canvas.translate(CENTER_X, CENTER_X); canvas.drawOval(new RectF(-r, -r, r, r), mPaint); canvas.drawCircle(0, 0, CENTER_RADIUS, mCenterPaint); if (mTrackingCenter) { int c = mCenterPaint.getColor(); mCenterPaint.setStyle(Paint.Style.STROKE); if (mHighlightCenter) { mCenterPaint.setAlpha(0xFF); } else { mCenterPaint.setAlpha(0x80); } canvas.drawCircle(0, 0, CENTER_RADIUS + mCenterPaint.getStrokeWidth(), mCenterPaint); mCenterPaint.setStyle(Paint.Style.FILL); mCenterPaint.setColor(c); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(CENTER_X*2, CENTER_Y*2); } public void setCenterColor(int color) { mCenterPaint.setColor(color); invalidate(); } public void setTransparency(int alpha) { int color = mCenterPaint.getColor(); int newColor = Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); mCenterPaint.setColor(newColor); mEditText.setText(convertToARGB(newColor)); invalidate(); } private int ave(int s, int d, float p) { return s + java.lang.Math.round(p * (d - s)); } private int interpColor(int colors[], float unit) { if (unit <= 0) { return colors[0]; } if (unit >= 1) { return colors[colors.length - 1]; } float p = unit * (colors.length - 1); int i = (int)p; p -= i; // now p is just the fractional part [0...1) and i is the index int c0 = colors[i]; int c1 = colors[i+1]; int a = ave(Color.alpha(c0), Color.alpha(c1), p); int r = ave(Color.red(c0), Color.red(c1), p); int g = ave(Color.green(c0), Color.green(c1), p); int b = ave(Color.blue(c0), Color.blue(c1), p); return Color.argb(a, r, g, b); } private static final float PI = 3.1415926f; @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX() - CENTER_X; float y = event.getY() - CENTER_Y; boolean inCenter = java.lang.Math.sqrt(x*x + y*y) <= CENTER_RADIUS; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mTrackingCenter = inCenter; if (inCenter) { mHighlightCenter = true; invalidate(); break; } case MotionEvent.ACTION_MOVE: if (mTrackingCenter) { if (mHighlightCenter != inCenter) { mHighlightCenter = inCenter; invalidate(); } } else { float angle = (float)java.lang.Math.atan2(y, x); // need to turn angle [-PI ... PI] into unit [0....1] float unit = angle/(2*PI); if (unit < 0) { unit += 1; } int color = interpColor(mColors, unit); mCenterPaint.setColor(color); mEditText.setText(convertToARGB(color)); invalidate(); } break; case MotionEvent.ACTION_UP: if (mTrackingCenter) { if (inCenter) { mListener.colorChanged(mCenterPaint.getColor()); } mTrackingCenter = false; // so we draw w/o halo invalidate(); } break; } return true; } } private String convertToARGB(int color) { String alpha = Integer.toHexString(Color.alpha(color)); String red = Integer.toHexString(Color.red(color)); String green = Integer.toHexString(Color.green(color)); String blue = Integer.toHexString(Color.blue(color)); if (alpha.length() == 1) { alpha = "0" + alpha; } if (red.length() == 1) { red = "0" + red; } if (green.length() == 1) { green = "0" + green; } if (blue.length() == 1) { blue = "0" + blue; } return "#" + alpha + red + green + blue; } private int convertToColorInt(String argb) throws NumberFormatException { int alpha = -1, red = -1, green = -1, blue = -1; if (argb.length() == 8) { alpha = Integer.parseInt(argb.substring(0, 2), 16); red = Integer.parseInt(argb.substring(2, 4), 16); green = Integer.parseInt(argb.substring(4, 6), 16); blue = Integer.parseInt(argb.substring(6, 8), 16); } else if (argb.length() == 6) { alpha = 255; red = Integer.parseInt(argb.substring(0, 2), 16); green = Integer.parseInt(argb.substring(2, 4), 16); blue = Integer.parseInt(argb.substring(4, 6), 16); } return Color.argb(alpha, red, green, blue); } private Context mContext; private EditText mEditText; private ColorPickerView mColorPickerView; private SeekBar mTransparencyBar; public ColorPickerDialog(Context context, OnColorChangedListener listener, int initialColor) { super(context); mContext = context; mListener = listener; mInitialColor = initialColor; CENTER_Y=CENTER_X=context.getResources().getDimensionPixelSize(R.dimen.color_picker_center); CENTER_RADIUS=context.getResources().getDimensionPixelSize(R.dimen.color_picker_radius); } protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout layout = new LinearLayout(mContext); layout.setOrientation(LinearLayout.VERTICAL); layout.setGravity(android.view.Gravity.CENTER); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); layoutParams.setMargins(10, 0, 10, 5); TextView tv = new TextView(mContext); tv.setText(mContext.getString(R.string.pref_dialog_message_color_picker)); layout.addView(tv, layoutParams); mColorPickerView = new ColorPickerView(getContext(), onColorChangedListener, mInitialColor); layout.addView(mColorPickerView, layoutParams); mTransparencyBar = new SeekBar(mContext); mTransparencyBar.setMax(255); mTransparencyBar.setProgressDrawable(new TextSeekBarDrawable(mContext.getResources(), mContext.getString(R.string.pref_dialog_color_picker_alpha), true)); mTransparencyBar.setProgress(Color.alpha(mInitialColor)); mTransparencyBar.setOnSeekBarChangeListener(onTransparencyChangedListener); layout.addView(mTransparencyBar, layoutParams); mEditText = new EditText(mContext); mEditText.addTextChangedListener(mEditTextListener); mEditText.setText(convertToARGB(mInitialColor)); layout.addView(mEditText, layoutParams); setContentView(layout); setTitle(mContext.getString(R.string.pref_dialog_title_color_picker)); } private OnColorChangedListener onColorChangedListener = new OnColorChangedListener() { public void colorChanged(int color) { mListener.colorChanged(color); dismiss(); } }; private SeekBar.OnSeekBarChangeListener onTransparencyChangedListener = new SeekBar.OnSeekBarChangeListener() { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mColorPickerView.setTransparency(progress); } public void onStartTrackingTouch(SeekBar seekBar) { } public void onStopTrackingTouch(SeekBar seekBar) { } }; private TextWatcher mEditTextListener = new TextWatcher() { public void afterTextChanged(Editable s) { } public void beforeTextChanged(CharSequence s, int start, int count, int after) { } public void onTextChanged(CharSequence s, int start, int before, int count) { try { String s2 = (s.toString()).replace("#", ""); if (s2.length() == 6 || s2.length() == 8) { int color = convertToColorInt(s2); mColorPickerView.setCenterColor(color); mTransparencyBar.setProgress(Color.alpha(color)); } } catch (NumberFormatException e) { } } }; //Source: http://www.anddev.org/announce_color_picker_dialog-t10771.html static final int[] STATE_FOCUSED = {android.R.attr.state_focused}; static final int[] STATE_PRESSED = {android.R.attr.state_pressed}; static class TextSeekBarDrawable extends Drawable implements Runnable { private static final long DELAY = 50; private String mText; private Drawable mProgress; private Paint mPaint; private Paint mOutlinePaint; private float mTextWidth; private boolean mActive; private float mTextXScale; private int mDelta; private ScrollAnimation mAnimation; public TextSeekBarDrawable(Resources res, String label, boolean labelOnRight) { mText = label; mProgress = res.getDrawable(android.R.drawable.progress_horizontal); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setTypeface(Typeface.DEFAULT_BOLD); mPaint.setTextSize(16); mPaint.setColor(0xff000000); mOutlinePaint = new Paint(mPaint); mOutlinePaint.setStyle(Style.STROKE); mOutlinePaint.setStrokeWidth(3); mOutlinePaint.setColor(0xbbffc300); mOutlinePaint.setMaskFilter(new BlurMaskFilter(1, Blur.NORMAL)); mTextWidth = mOutlinePaint.measureText(mText); mTextXScale = labelOnRight? 1 : 0; mAnimation = new ScrollAnimation(); } @Override protected void onBoundsChange(Rect bounds) { mProgress.setBounds(bounds); } @Override protected boolean onStateChange(int[] state) { mActive = StateSet.stateSetMatches(STATE_FOCUSED, state) | StateSet.stateSetMatches(STATE_PRESSED, state); invalidateSelf(); return false; } @Override public boolean isStateful() { return true; } @Override protected boolean onLevelChange(int level) { // Log.d(TAG, "onLevelChange " + level); if (level < 4000 && mDelta <= 0) { // Log.d(TAG, "onLevelChange scheduleSelf ++"); mDelta = 1; mAnimation.startScrolling(mTextXScale, 1); scheduleSelf(this, SystemClock.uptimeMillis() + DELAY); } else if (level > 6000 && mDelta >= 0) { // Log.d(TAG, "onLevelChange scheduleSelf --"); mDelta = -1; mAnimation.startScrolling(mTextXScale, 0); scheduleSelf(this, SystemClock.uptimeMillis() + DELAY); } return mProgress.setLevel(level); } @Override public void draw(Canvas canvas) { mProgress.draw(canvas); if (mAnimation.hasStarted() && !mAnimation.hasEnded()) { // pending animation mAnimation.getTransformation(AnimationUtils.currentAnimationTimeMillis(), null); mTextXScale = mAnimation.getCurrent(); // Log.d(TAG, "draw " + mTextX + " " + SystemClock.uptimeMillis()); } Rect bounds = getBounds(); float x = 6 + mTextXScale * (bounds.width() - mTextWidth - 6 - 6); float y = (bounds.height() + mPaint.getTextSize()) / 2; mOutlinePaint.setAlpha(mActive? 255 : 255 / 2); mPaint.setAlpha(mActive? 255 : 255 / 2); canvas.drawText(mText, x, y, mOutlinePaint); canvas.drawText(mText, x, y, mPaint); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public void setAlpha(int alpha) { } @Override public void setColorFilter(ColorFilter cf) { } public void run() { mAnimation.getTransformation(AnimationUtils.currentAnimationTimeMillis(), null); // close interpolation of mTextX mTextXScale = mAnimation.getCurrent(); if (!mAnimation.hasEnded()) { scheduleSelf(this, SystemClock.uptimeMillis() + DELAY); } invalidateSelf(); // Log.d(TAG, "run " + mTextX + " " + SystemClock.uptimeMillis()); } } static class ScrollAnimation extends Animation { private static final long DURATION = 750; private float mFrom; private float mTo; private float mCurrent; public ScrollAnimation() { setDuration(DURATION); setInterpolator(new DecelerateInterpolator()); } public void startScrolling(float from, float to) { mFrom = from; mTo = to; startNow(); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { mCurrent = mFrom + (mTo - mFrom) * interpolatedTime; // Log.d(TAG, "applyTransformation " + mCurrent); } public float getCurrent() { return mCurrent; } } }