diff --git a/build.xml b/build.xml
index 0731c6677..01c856b27 100644
--- a/build.xml
+++ b/build.xml
@@ -316,8 +316,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/plugins/HoloColorPicker/.gitignore b/plugins/HoloColorPicker/.gitignore
new file mode 100644
index 000000000..d486aca22
--- /dev/null
+++ b/plugins/HoloColorPicker/.gitignore
@@ -0,0 +1,24 @@
+#Android generated
+bin
+gen
+
+#Eclipse
+.project
+.classpath
+.settings
+
+#IntelliJ IDEA
+.idea
+*.iml
+classes
+
+#Command line
+build.xml
+proguard-project.txt
+
+#Mac specific
+.DS_Store
+
+#User specific
+local.properties
+sample/src/com/cyrilmottier/android/polarissample/util/LocalConfig.java
\ No newline at end of file
diff --git a/plugins/HoloColorPicker/AndroidManifest.xml b/plugins/HoloColorPicker/AndroidManifest.xml
new file mode 100644
index 000000000..c9153d65c
--- /dev/null
+++ b/plugins/HoloColorPicker/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/HoloColorPicker/LICENSE b/plugins/HoloColorPicker/LICENSE
new file mode 100644
index 000000000..fc68cee19
--- /dev/null
+++ b/plugins/HoloColorPicker/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [2012] Lars Werkmann
+
+ 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.
diff --git a/plugins/HoloColorPicker/README.md b/plugins/HoloColorPicker/README.md
new file mode 100644
index 000000000..02de1bac1
--- /dev/null
+++ b/plugins/HoloColorPicker/README.md
@@ -0,0 +1,46 @@
+
Android Holo ColorPicker
+
+A Holo themed colorpicker designed by Marie Schweiz and Nick Butcher.
+
+![image](https://lh3.googleusercontent.com/-RzpEyLl-1xM/UOMyztql1gI/AAAAAAAAATs/UKBuqZZtaZw//HoloColorPickerFramed1.png)
+![image](https://lh4.googleusercontent.com/-sXAPd8onJ_8/UOMyzjA6c4I/AAAAAAAAATo/DY4kIzo7TtU//HoloColorPickerFramed2.png)
+
+
+Documentation
+Add this to your xml
+
+
+
+To change the thickness of the wheel and the pointer you can add this.
+
+ app:wheel_size="2"
+ app:pointer_size="4"
+
+To get the color of the colorpicker
+
+ ColorPicker picker = (ColorPicker) findViewById(R.id.picker);
+
+ picker.getColor();
+
+License
+
+ Copyright 2012 Lars Werkman
+
+ 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.
+
+
+Devoleped By
+**Lars Werkman**
diff --git a/plugins/HoloColorPicker/libs/android-support-v4.jar b/plugins/HoloColorPicker/libs/android-support-v4.jar
new file mode 100644
index 000000000..6080877d4
Binary files /dev/null and b/plugins/HoloColorPicker/libs/android-support-v4.jar differ
diff --git a/plugins/HoloColorPicker/project.properties b/plugins/HoloColorPicker/project.properties
new file mode 100644
index 000000000..484dab075
--- /dev/null
+++ b/plugins/HoloColorPicker/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
+android.library=true
diff --git a/plugins/HoloColorPicker/res/values/attrs.xml b/plugins/HoloColorPicker/res/values/attrs.xml
new file mode 100644
index 000000000..53f6b2097
--- /dev/null
+++ b/plugins/HoloColorPicker/res/values/attrs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/HoloColorPicker/src/com/larswerkman/colorpicker/ColorPicker.java b/plugins/HoloColorPicker/src/com/larswerkman/colorpicker/ColorPicker.java
new file mode 100644
index 000000000..bca59e8b8
--- /dev/null
+++ b/plugins/HoloColorPicker/src/com/larswerkman/colorpicker/ColorPicker.java
@@ -0,0 +1,486 @@
+/*
+* Copyright 2012 Lars Werkman
+*
+* 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.larswerkman.colorpicker;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.SweepGradient;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * Displays a holo-themed color picker.
+ *
+ *
+ * Use {@link #getColor()} to retrieve the selected color.
+ *
+ */
+public class ColorPicker extends View {
+ /*
+ * Constants used to save/restore the instance state.
+ */
+ private static final String STATE_PARENT = "parent";
+ private static final String STATE_ANGLE = "angle";
+
+ /**
+ * Colors to construct the color wheel using {@link SweepGradient}.
+ *
+ *
+ * Note: The algorithm in {@link #normalizeColor(int)} highly depends on these exact values. Be
+ * aware that {@link #setColor(int)} might break if you change this array.
+ *
+ */
+ private static final int[] COLORS = new int[] { 0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF,
+ 0xFF00FF00, 0xFFFFFF00, 0xFFFF0000 };
+
+
+ /**
+ * {@code Paint} instance used to draw the color wheel.
+ */
+ private Paint mColorWheelPaint;
+
+ /**
+ * {@code Paint} instance used to draw the pointer's "halo".
+ */
+ private Paint mPointerHaloPaint;
+
+ /**
+ * {@code Paint} instance used to draw the pointer (the selected color).
+ */
+ private Paint mPointerColor;
+
+ /**
+ * The stroke width used to paint the color wheel (in pixels).
+ */
+ private int mColorWheelStrokeWidth;
+
+ /**
+ * The radius of the pointer (in pixels).
+ */
+ private int mPointerRadius;
+
+ /**
+ * The rectangle enclosing the color wheel.
+ */
+ private RectF mColorWheelRectangle = new RectF();
+
+ /**
+ * {@code true} if the user clicked on the pointer to start the move mode. {@code false} once
+ * the user stops touching the screen.
+ *
+ * @see #onTouchEvent(MotionEvent)
+ */
+ private boolean mUserIsMovingPointer = false;
+
+ /**
+ * The ARGB value of the currently selected color.
+ */
+ private int mColor;
+
+ /**
+ * Number of pixels the origin of this view is moved in X- and Y-direction.
+ *
+ *
+ * We use the center of this (quadratic) View as origin of our internal coordinate system.
+ * Android uses the upper left corner as origin for the View-specific coordinate system. So this
+ * is the value we use to translate from one coordinate system to the other.
+ *
+ *
+ * Note: (Re)calculated in {@link #onMeasure(int, int)}.
+ *
+ * @see #onDraw(Canvas)
+ */
+ private float mTranslationOffset;
+
+ /**
+ * Radius of the color wheel in pixels.
+ *
+ * Note: (Re)calculated in {@link #onMeasure(int, int)}.
+ */
+ private float mColorWheelRadius;
+
+ /**
+ * The pointer's position expressed as angle (in rad).
+ */
+ private float mAngle;
+
+
+ public ColorPicker(Context context) {
+ super(context);
+ init(null, 0);
+ }
+
+ public ColorPicker(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(attrs, 0);
+ }
+
+ public ColorPicker(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(attrs, defStyle);
+ }
+
+ private void init(AttributeSet attrs, int defStyle) {
+ final TypedArray a = getContext().obtainStyledAttributes(attrs,
+ R.styleable.ColorPicker, defStyle, 0);
+
+ mColorWheelStrokeWidth = a.getInteger(R.styleable.ColorPicker_wheel_size, 16);
+ mPointerRadius = a.getInteger(R.styleable.ColorPicker_pointer_size, 48);
+
+ a.recycle();
+
+ Shader s = new SweepGradient(0, 0, COLORS, null);
+
+ mColorWheelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mColorWheelPaint.setShader(s);
+ mColorWheelPaint.setStyle(Paint.Style.STROKE);
+ mColorWheelPaint.setStrokeWidth(mColorWheelStrokeWidth);
+
+ mPointerHaloPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPointerHaloPaint.setColor(Color.BLACK);
+ mPointerHaloPaint.setStrokeWidth(5);
+ mPointerHaloPaint.setAlpha(0x60);
+
+ mPointerColor = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPointerColor.setStrokeWidth(5);
+
+ mAngle = (float) (-Math.PI / 2);
+ mPointerColor.setColor(calculateColor(mAngle));
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ // All of our positions are using our internal coordinate system. Instead of translating
+ // them we let Canvas do the work for us.
+ canvas.translate(mTranslationOffset, mTranslationOffset);
+
+ // Draw the color wheel.
+ canvas.drawOval(mColorWheelRectangle, mColorWheelPaint);
+
+ float[] pointerPosition = calculatePointerPosition(mAngle);
+
+ // Draw the pointer's "halo"
+ canvas.drawCircle(pointerPosition[0], pointerPosition[1], mPointerRadius, mPointerHaloPaint);
+
+ // Draw the pointer (the currently selected color) slightly smaller on top.
+ canvas.drawCircle(pointerPosition[0], pointerPosition[1],
+ (float) (mPointerRadius / 1.2), mPointerColor);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int height = getDefaultSize(getSuggestedMinimumHeight(),
+ heightMeasureSpec);
+ int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
+ int min = Math.min(width, height);
+ setMeasuredDimension(min, min);
+
+ mTranslationOffset = min * 0.5f;
+ mColorWheelRadius = mTranslationOffset - mPointerRadius;
+
+ mColorWheelRectangle.set(-mColorWheelRadius, -mColorWheelRadius, mColorWheelRadius,
+ mColorWheelRadius);
+ }
+
+ private int ave(int s, int d, float p) {
+ return s + java.lang.Math.round(p * (d - s));
+ }
+
+ /**
+ * Calculate the color using the supplied angle.
+ *
+ * @param angle
+ * The selected color's position expressed as angle (in rad).
+ *
+ * @return The ARGB value of the color on the color wheel at the specified angle.
+ */
+ private int calculateColor(float angle) {
+ float unit = (float) (angle / (2 * Math.PI));
+ if (unit < 0) {
+ unit += 1;
+ }
+
+ 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;
+
+ 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);
+
+ mColor = Color.argb(a, r, g, b);
+ return Color.argb(a, r, g, b);
+ }
+
+ /**
+ * Get the currently selected color.
+ *
+ * @return The ARGB value of the currently selected color.
+ */
+ public int getColor() {
+ return mColor;
+ }
+
+ /**
+ * Set the color to be highlighted by the pointer.
+ *
+ * @param color
+ * The RGB value of the color to highlight. If this is not a color displayed on the
+ * color wheel a very simple algorithm is used to map it to the color wheel. The
+ * resulting color often won't look close to the original color. This is especially true
+ * for shades of grey. You have been warned!
+ */
+ public void setColor(int color) {
+ mAngle = colorToAngle(color);
+ mPointerColor.setColor(calculateColor(mAngle));
+ invalidate();
+ }
+
+ /**
+ * Convert a color to an angle.
+ *
+ * @param color
+ * The RGB value of the color to "find" on the color wheel. {@link #normalizeColor(int)}
+ * will be used to map this color to one on the color wheel if necessary.
+ *
+ * @return The angle (in rad) the "normalized" color is displayed on the color wheel.
+ */
+ private float colorToAngle(int color) {
+ int[] colorInfo = normalizeColor(color);
+ int normColor = colorInfo[0];
+ int colorMask = colorInfo[1];
+ int shiftValue = colorInfo[2];
+
+ int anchorColor = (normColor & ~colorMask);
+
+ // Find the "anchor" color in the COLORS array
+ for (int i = 0; i < COLORS.length - 1; i++) {
+ if (COLORS[i] == anchorColor) {
+ int nextValue = COLORS[i + 1];
+
+ double value;
+ double decimals = ((normColor >> shiftValue) & 0xFF) / 255D;
+
+ // Find out if the gradient our color belongs to goes from the element just found to
+ // the next element in the array.
+ if ((nextValue & colorMask) != (anchorColor & colorMask)) {
+ // Compute value depending of the gradient direction
+ if (nextValue < anchorColor) {
+ value = i + 1 - decimals;
+ } else {
+ value = i + decimals;
+ }
+ } else {
+ // It's a gradient from this element to the previous element in the array.
+
+ // Wrap to the end of the array if the "anchor" color is the first element.
+ int index = (i == 0) ? COLORS.length - 1 : i;
+ int prevValue = COLORS[index - 1];
+
+ // Compute value depending of the gradient direction
+ if (prevValue < anchorColor) {
+ value = index - 1 + decimals;
+ } else {
+ value = index - decimals;
+ }
+ }
+
+ // Calculate the angle in rad (from -PI to PI)
+ float angle = (float) (2 * Math.PI * value / (COLORS.length - 1));
+ if (angle > Math.PI) {
+ angle -= 2 * Math.PI;
+ }
+
+ return angle;
+ }
+ }
+
+ // This shouldn't happen
+ return 0;
+ }
+
+ /**
+ * "Normalize" the supplied color.
+ *
+ *
+ * This will set the lowest value of R,G,B to 0, the highest to 255, and will keep the middle
+ * value.
+ * For values close to those on the color wheel this will result in close matches. For other
+ * values, especially shades of grey this will produce funny results.
+ *
+ *
+ * @param color
+ * The color to "normalize".
+ *
+ * @return An {@code int} array with the following contents:
+ *
+ * - The ARGB value of the "normalized" color.
+ * - A mask with all bits {@code 0} but those for the byte representing the
+ * "middle value" that remains unchanged in the "normalized" color.
+ * - The number of bits the "normalized" color has to be shifted to the right so the
+ * "middle value" is in the lower 8 bits.
+ *
+ */
+ private int[] normalizeColor(int color) {
+ int red = Color.red(color);
+ int green = Color.green(color);
+ int blue = Color.blue(color);
+
+ int newRed = red;
+ int newGreen = green;
+ int newBlue = blue;
+
+ int maskRed = 0;
+ int maskGreen = 0;
+ int maskBlue = 0;
+ int shiftValue;
+
+ if (red < green && red < blue) {
+ // Red is the smallest component
+ newRed = 0;
+ if (green > blue) {
+ // Green is the largest component
+ shiftValue = 0;
+ maskBlue = 0xFF;
+ newGreen = 0xFF;
+ } else {
+ // We make blue the largest component
+ shiftValue = 8;
+ maskGreen = 0xFF;
+ newBlue = 0xFF;
+ }
+ } else if (green < red && green < blue) {
+ // Green is the smallest component
+ newGreen = 0;
+ if (red > blue) {
+ // Red is the largest component
+ shiftValue = 0;
+ maskBlue = 0xFF;
+ newRed = 0xFF;
+ } else {
+ // We make blue the largest component
+ shiftValue = 16;
+ maskRed = 0xFF;
+ newBlue = 0xFF;
+ }
+ } else {
+ // We make blue the smallest component
+ newBlue = 0;
+ if (red > green) {
+ // Red is the largest component
+ shiftValue = 8;
+ maskGreen = 0xFF;
+ newRed = 0xFF;
+ } else {
+ // We make green the largest component
+ shiftValue = 16;
+ maskRed = 0xFF;
+ newGreen = 0xFF;
+ }
+ }
+
+ int normColor = Color.argb(255, newRed, newGreen, newBlue);
+ int colorMask = Color.argb(0, maskRed, maskGreen, maskBlue);
+
+ return new int[] { normColor, colorMask, shiftValue };
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ // Convert coordinates to our internal coordinate system
+ float x = event.getX() - mTranslationOffset;
+ float y = event.getY() - mTranslationOffset;
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ // Check whether the user pressed on (or near) the pointer
+ float[] pointerPosition = calculatePointerPosition(mAngle);
+ if (x >= (pointerPosition[0] - 48) && x <= (pointerPosition[0] + 48)
+ && y >= (pointerPosition[1] - 48) && y <= (pointerPosition[1] + 48)) {
+ mUserIsMovingPointer = true;
+ invalidate();
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mUserIsMovingPointer) {
+ mAngle = (float) java.lang.Math.atan2(y, x);
+ mPointerColor.setColor(calculateColor(mAngle));
+ invalidate();
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ mUserIsMovingPointer = false;
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * Calculate the pointer's coordinates on the color wheel using the supplied angle.
+ *
+ * @param angle
+ * The position of the pointer expressed as angle (in rad).
+ *
+ * @return The coordinates of the pointer's center in our internal coordinate system.
+ */
+ private float[] calculatePointerPosition(float angle) {
+ float x = (float) (mColorWheelRadius * Math.cos(angle));
+ float y = (float) (mColorWheelRadius * Math.sin(angle));
+
+ return new float[] { x, y };
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+
+ Bundle state = new Bundle();
+ state.putParcelable(STATE_PARENT, superState);
+ state.putFloat(STATE_ANGLE, mAngle);
+
+ return state;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ Bundle savedState = (Bundle) state;
+
+ Parcelable superState = savedState.getParcelable(STATE_PARENT);
+ super.onRestoreInstanceState(superState);
+
+ mAngle = savedState.getFloat(STATE_ANGLE);
+ mPointerColor.setColor(calculateColor(mAngle));
+ }
+}
diff --git a/project.properties b/project.properties
index 161d43c8c..dbb016d2f 100644
--- a/project.properties
+++ b/project.properties
@@ -16,3 +16,4 @@ extensible.libs.classpath=compile-only-libs
android.library.reference.1=plugins/ActionBarSherlock/library
android.library.reference.2=plugins/Android-PullToRefresh/library
android.library.reference.3=plugins/ckChangeLog/library
+android.library.reference.4=plugins/HoloColorPicker
diff --git a/res/layout/color_picker_dialog.xml b/res/layout/color_picker_dialog.xml
new file mode 100644
index 000000000..f34f6c8ed
--- /dev/null
+++ b/res/layout/color_picker_dialog.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/com/fsck/k9/activity/Accounts.java b/src/com/fsck/k9/activity/Accounts.java
index 199293348..65f2959d6 100644
--- a/src/com/fsck/k9/activity/Accounts.java
+++ b/src/com/fsck/k9/activity/Accounts.java
@@ -1200,7 +1200,8 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
new String[] {"HtmlCleaner", "http://htmlcleaner.sourceforge.net/"},
new String[] {"ActionBarSherlock", "http://actionbarsherlock.com/"},
new String[] {"Android-PullToRefresh", "https://github.com/chrisbanes/Android-PullToRefresh"},
- new String[] {"ckChangeLog", "https://github.com/cketti/ckChangeLog"}
+ new String[] {"ckChangeLog", "https://github.com/cketti/ckChangeLog"},
+ new String[] {"HoloColorPicker", "https://github.com/LarsWerkman/HoloColorPicker"}
};
private void onAbout() {
diff --git a/src/com/fsck/k9/activity/ColorPickerDialog.java b/src/com/fsck/k9/activity/ColorPickerDialog.java
index b22587045..eff63b270 100644
--- a/src/com/fsck/k9/activity/ColorPickerDialog.java
+++ b/src/com/fsck/k9/activity/ColorPickerDialog.java
@@ -1,218 +1,68 @@
-/* Sourced from http://code.google.com/p/android-color-picker/source/browse/trunk/AmbilWarna/src/yuku/ambilwarna/AmbilWarnaDialog.java?r=1
- * On 2010-11-07
- * Translated to English, Ported to use the same (inferior) API as the more standard "ColorPickerDialog" and imported into the K-9 namespace by Jesse Vincent
- * In an ideal world, we should move to using AmbilWarna as an Android Library Project in the future
- * License: Apache 2.0
- * Author: yukuku@code.google.com
- */
-
-
package com.fsck.k9.activity;
+
import com.fsck.k9.R;
import android.app.AlertDialog;
import android.content.*;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.*;
-import android.widget.*;
-import com.fsck.k9.view.ColorPickerBox;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.larswerkman.colorpicker.ColorPicker;
+/**
+ * Dialog displaying a color picker.
+ */
public class ColorPickerDialog extends AlertDialog {
- private static final String TAG = ColorPickerDialog.class.getSimpleName();
-
- private static final String BUNDLE_KEY_PARENT_BUNDLE = "parent";
- private static final String BUNDLE_KEY_COLOR_OLD = "color_old";
- private static final String BUNDLE_KEY_COLOR_NEW = "color_new";
-
+ /**
+ * The interface users of {@link ColorPickerDialog} have to implement to learn the selected
+ * color.
+ */
public interface OnColorChangedListener {
+ /**
+ * This is called after the user pressed the "OK" button of the dialog.
+ *
+ * @param color
+ * The ARGB value of the selected color.
+ */
void colorChanged(int color);
}
- OnColorChangedListener listener;
- View viewHue;
- ColorPickerBox viewBox;
- ImageView arrow;
- View viewColorOld;
- View viewColorNew;
- ImageView viewSpyglass;
-
- float onedp;
- int colorOld;
- int colorNew;
- float hue;
- float sat;
- float val;
- float sizeUiDp = 240.f;
- float sizeUiPx; // diset di constructor
+ OnColorChangedListener mColorChangedListener;
+ ColorPicker mColorPicker;
public ColorPickerDialog(Context context, OnColorChangedListener listener, int color) {
super(context);
- this.listener = listener;
+ mColorChangedListener = listener;
- initColor(color);
+ View view = LayoutInflater.from(context).inflate(R.layout.color_picker_dialog, null);
- onedp = context.getResources().getDimension(R.dimen.colorpicker_onedp);
- sizeUiPx = sizeUiDp * onedp;
- Log.d(TAG, "onedp = " + onedp + ", sizeUiPx=" + sizeUiPx); //$NON-NLS-1$//$NON-NLS-2$
+ mColorPicker = (ColorPicker) view.findViewById(R.id.color_picker);
+ mColorPicker.setColor(color);
- View view = LayoutInflater.from(context).inflate(R.layout.colorpicker_dialog, null);
- viewHue = view.findViewById(R.id.colorpicker_viewHue);
- viewBox = (ColorPickerBox) view.findViewById(R.id.colorpicker_viewBox);
- arrow = (ImageView) view.findViewById(R.id.colorpicker_arrow);
- viewColorOld = view.findViewById(R.id.colorpicker_colorOld);
- viewColorNew = view.findViewById(R.id.colorpicker_colorNew);
- viewSpyglass = (ImageView) view.findViewById(R.id.colorpicker_spyglass);
+ setView(view);
- updateView();
-
- viewHue.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_MOVE
- || event.getAction() == MotionEvent.ACTION_DOWN
- || event.getAction() == MotionEvent.ACTION_UP) {
-
- float y = event.getY(); // dalam px, bukan dp
- if (y < 0.f) y = 0.f;
- if (y > sizeUiPx) y = sizeUiPx - 0.001f;
-
- hue = 360.f - 360.f / sizeUiPx * y;
- if (hue == 360.f) hue = 0.f;
-
- colorNew = calculateColor();
- // update view
- viewBox.setHue(hue);
- placeArrow();
- viewColorNew.setBackgroundColor(colorNew);
-
- return true;
- }
- return false;
- }
- });
- viewBox.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_MOVE
- || event.getAction() == MotionEvent.ACTION_DOWN
- || event.getAction() == MotionEvent.ACTION_UP) {
-
- float x = event.getX(); // dalam px, bukan dp
- float y = event.getY(); // dalam px, bukan dp
-
- if (x < 0.f) x = 0.f;
- if (x > sizeUiPx) x = sizeUiPx;
- if (y < 0.f) y = 0.f;
- if (y > sizeUiPx) y = sizeUiPx;
-
- sat = (1.f / sizeUiPx * x);
- val = 1.f - (1.f / sizeUiPx * y);
-
- colorNew = calculateColor();
- // update view
- placeSpyglass();
- viewColorNew.setBackgroundColor(colorNew);
-
- return true;
- }
- return false;
- }
- });
-
- this.setView(view);
- this.setButton(BUTTON_POSITIVE, context.getString(R.string.okay_action),
+ setButton(BUTTON_POSITIVE, context.getString(R.string.okay_action),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- if (ColorPickerDialog.this.listener != null) {
- ColorPickerDialog.this.listener.colorChanged(colorNew);
+ if (mColorChangedListener != null) {
+ mColorChangedListener.colorChanged(mColorPicker.getColor());
}
}
});
- this.setButton(BUTTON_NEGATIVE, context.getString(R.string.cancel_action), (OnClickListener) null);
- }
-
- private void updateView() {
- placeArrow();
- placeSpyglass();
- viewBox.setHue(hue);
- viewColorOld.setBackgroundColor(colorOld);
- viewColorNew.setBackgroundColor(colorNew);
- }
-
- private void initColor(int color) {
- colorNew = color;
- colorOld = color;
-
- Color.colorToHSV(color, tmp01);
- hue = tmp01[0];
- sat = tmp01[1];
- val = tmp01[2];
+ setButton(BUTTON_NEGATIVE, context.getString(R.string.cancel_action),
+ (OnClickListener) null);
}
+ /**
+ * Set the color the color picker should highlight as selected color.
+ *
+ * @param color
+ * The (A)RGB value of a color (the alpha channel will be ignored).
+ */
public void setColor(int color) {
- initColor(color);
- updateView();
- }
-
- @Override
- public Bundle onSaveInstanceState() {
- Bundle parentBundle = super.onSaveInstanceState();
-
- Bundle savedInstanceState = new Bundle();
- savedInstanceState.putBundle(BUNDLE_KEY_PARENT_BUNDLE, parentBundle);
- savedInstanceState.putInt(BUNDLE_KEY_COLOR_OLD, colorOld);
- savedInstanceState.putInt(BUNDLE_KEY_COLOR_NEW, colorNew);
-
- return savedInstanceState;
- }
-
- @Override
- public void onRestoreInstanceState(Bundle savedInstanceState) {
- Bundle parentBundle = savedInstanceState.getBundle(BUNDLE_KEY_PARENT_BUNDLE);
- super.onRestoreInstanceState(parentBundle);
-
- int color = savedInstanceState.getInt(BUNDLE_KEY_COLOR_NEW);
-
- // Sets colorOld, colorNew to color and initializes hue, sat, val from color
- initColor(color);
-
- // Now restore the real colorOld value
- colorOld = savedInstanceState.getInt(BUNDLE_KEY_COLOR_OLD);
-
- updateView();
- }
-
- @SuppressWarnings("deprecation")
- protected void placeArrow() {
- float y = sizeUiPx - (hue * sizeUiPx / 360.f);
- if (y == sizeUiPx) y = 0.f;
-
- AbsoluteLayout.LayoutParams layoutParams = (AbsoluteLayout.LayoutParams) arrow.getLayoutParams();
- layoutParams.y = (int)(y + 4);
- arrow.setLayoutParams(layoutParams);
- }
-
- @SuppressWarnings("deprecation")
- protected void placeSpyglass() {
- float x = sat * sizeUiPx;
- float y = (1.f - val) * sizeUiPx;
-
- AbsoluteLayout.LayoutParams layoutParams = (AbsoluteLayout.LayoutParams) viewSpyglass.getLayoutParams();
- layoutParams.x = (int)(x + 3);
- layoutParams.y = (int)(y + 3);
- viewSpyglass.setLayoutParams(layoutParams);
- }
-
- float[] tmp01 = new float[3];
- private int calculateColor() {
- tmp01[0] = hue;
- tmp01[1] = sat;
- tmp01[2] = val;
- return Color.HSVToColor(tmp01);
+ mColorPicker.setColor(color);
}
}