Mercurial > 510Connectbot
annotate app/src/main/java/com/five_ten_sg/connectbot/util/UberColorPickerDialog.java @ 509:2eb4fa13b9ef
update 5250 encryption to allow TLS1.3, remove old SSLv2 and SSLv3 methods
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Sun, 29 Jan 2023 10:25:21 -0700 |
parents | 105815cce146 |
children |
rev | line source |
---|---|
0 | 1 /* |
2 * Copyright (C) 2007 The Android Open Source Project | |
3 * | |
4 * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 * you may not use this file except in compliance with the License. | |
6 * You may obtain a copy of the License at | |
7 * | |
8 * http://www.apache.org/licenses/LICENSE-2.0 | |
9 * | |
10 * Unless required by applicable law or agreed to in writing, software | |
11 * distributed under the License is distributed on an "AS IS" BASIS, | |
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 * See the License for the specific language governing permissions and | |
14 * limitations under the License. | |
15 */ | |
16 | |
17 /* | |
18 * 090408 | |
19 * Keith Wiley | |
20 * kwiley@keithwiley.com | |
21 * http://keithwiley.com | |
22 * | |
23 * UberColorPickerDialog v1.1 | |
24 * | |
25 * This color picker was implemented as a (significant) extension of the | |
26 * ColorPickerDialog class provided in the Android API Demos. You are free | |
27 * to drop it unchanged into your own projects or to modify it as you see | |
28 * fit. I would appreciate it if this comment block were let intact, | |
29 * merely for credit's sake. | |
30 * | |
31 * Enjoy! | |
32 */ | |
33 | |
34 package com.five_ten_sg.connectbot.util; | |
35 | |
36 import android.app.Dialog; | |
37 import android.content.Context; | |
38 import android.graphics.Bitmap; | |
39 import android.graphics.Canvas; | |
40 import android.graphics.Color; | |
41 import android.graphics.ColorMatrix; | |
42 import android.graphics.ComposeShader; | |
43 import android.graphics.Paint; | |
44 import android.graphics.PorterDuff; | |
45 import android.graphics.PorterDuffXfermode; | |
46 import android.graphics.RadialGradient; | |
47 import android.graphics.Rect; | |
48 import android.graphics.RectF; | |
49 import android.graphics.Shader; | |
50 import android.graphics.SweepGradient; | |
51 import android.graphics.drawable.GradientDrawable; | |
52 import android.graphics.drawable.GradientDrawable.Orientation; | |
53 import android.os.Bundle; | |
54 import android.util.DisplayMetrics; | |
55 import android.view.MotionEvent; | |
56 import android.view.View; | |
57 | |
58 /** | |
59 * UberColorPickerDialog is a seriously enhanced version of the UberColorPickerDialog | |
60 * class provided in the Android API Demos.<p> | |
61 * | |
62 * NOTE (from Kenny Root): This is a VERY slimmed down version custom for ConnectBot. | |
63 * Visit Keith's site for the full version at the URL listed in the author line.<p> | |
64 * | |
65 * @author Keith Wiley, kwiley@keithwiley.com, http://keithwiley.com | |
66 */ | |
67 public class UberColorPickerDialog extends Dialog { | |
68 private final OnColorChangedListener mListener; | |
69 private final int mInitialColor; | |
70 | |
71 /** | |
72 * Callback to the creator of the dialog, informing the creator of a new color and notifying that the dialog is about to dismiss. | |
73 */ | |
74 public interface OnColorChangedListener { | |
75 void colorChanged(int color); | |
76 } | |
77 | |
78 /** | |
79 * Ctor | |
80 * @param context | |
81 * @param listener | |
82 * @param initialColor | |
83 * @param showTitle If true, a title is shown across the top of the dialog. If false a toast is shown instead. | |
84 */ | |
85 public UberColorPickerDialog(Context context, | |
86 OnColorChangedListener listener, | |
87 int initialColor) { | |
88 super(context); | |
89 mListener = listener; | |
90 mInitialColor = initialColor; | |
91 } | |
92 | |
93 /** | |
94 * Activity entry point | |
95 */ | |
96 @Override | |
97 protected void onCreate(Bundle savedInstanceState) { | |
98 super.onCreate(savedInstanceState); | |
99 OnColorChangedListener l = new OnColorChangedListener() { | |
100 public void colorChanged(int color) { | |
101 mListener.colorChanged(color); | |
102 dismiss(); | |
103 } | |
104 }; | |
105 DisplayMetrics dm = new DisplayMetrics(); | |
106 getWindow().getWindowManager().getDefaultDisplay().getMetrics(dm); | |
107 int screenWidth = dm.widthPixels; | |
108 int screenHeight = dm.heightPixels; | |
109 setTitle("Pick a color (try the trackball)"); | |
110 | |
111 try { | |
112 setContentView(new ColorPickerView(getContext(), l, screenWidth, screenHeight, mInitialColor)); | |
113 } | |
114 catch (Exception e) { | |
115 //There is currently only one kind of ctor exception, that where no methods are enabled. | |
116 dismiss(); //This doesn't work! The dialog is still shown (its title at least, the layout is empty from the exception being thrown). <sigh> | |
117 } | |
118 } | |
119 | |
120 /** | |
121 * ColorPickerView is the meat of this color picker (as opposed to the enclosing class). | |
122 * All the heavy lifting is done directly by this View subclass. | |
123 * <P> | |
124 * You can enable/disable whichever color chooser methods you want by modifying the ENABLED_METHODS switches. They *should* | |
125 * do all the work required to properly enable/disable methods without losing track of what goes with what and what maps to what. | |
126 * <P> | |
127 * If you add a new color chooser method, do a text search for "NEW_METHOD_WORK_NEEDED_HERE". That tag indicates all | |
128 * the locations in the code that will have to be amended in order to properly add a new color chooser method. | |
129 * I highly recommend adding new methods to the end of the list. If you want to try to reorder the list, you're on your own. | |
130 */ | |
131 private static class ColorPickerView extends View { | |
132 private static int SWATCH_WIDTH = 95; | |
133 private static final int SWATCH_HEIGHT = 60; | |
134 | |
135 private static int PALETTE_POS_X = 0; | |
136 private static int PALETTE_POS_Y = SWATCH_HEIGHT; | |
137 private static final int PALETTE_DIM = SWATCH_WIDTH * 2; | |
138 private static final int PALETTE_RADIUS = PALETTE_DIM / 2; | |
139 private static final int PALETTE_CENTER_X = PALETTE_RADIUS; | |
140 private static final int PALETTE_CENTER_Y = PALETTE_RADIUS; | |
141 | |
142 private static final int SLIDER_THICKNESS = 40; | |
143 | |
144 private static int VIEW_DIM_X = PALETTE_DIM; | |
145 private static int VIEW_DIM_Y = SWATCH_HEIGHT; | |
146 | |
147 //NEW_METHOD_WORK_NEEDED_HERE | |
148 private static final int METHOD_HS_V_PALETTE = 0; | |
149 | |
150 //NEW_METHOD_WORK_NEEDED_HERE | |
151 //Add a new entry to the list for each controller in the new method | |
152 private static final int TRACKED_NONE = -1; //No object on screen is currently being tracked | |
153 private static final int TRACK_SWATCH_OLD = 10; | |
154 private static final int TRACK_SWATCH_NEW = 11; | |
155 private static final int TRACK_HS_PALETTE = 30; | |
156 private static final int TRACK_VER_VALUE_SLIDER = 31; | |
157 | |
158 private static final int TEXT_SIZE = 12; | |
159 private static int[] TEXT_HSV_POS = new int[2]; | |
160 private static int[] TEXT_RGB_POS = new int[2]; | |
161 private static int[] TEXT_YUV_POS = new int[2]; | |
162 private static int[] TEXT_HEX_POS = new int[2]; | |
163 | |
164 private static final float PI = 3.141592653589793f; | |
165 | |
166 private int mMethod = METHOD_HS_V_PALETTE; | |
167 private int mTracking = TRACKED_NONE; //What object on screen is currently being tracked for movement | |
168 | |
169 //Zillions of persistant Paint objecs for drawing the View | |
170 | |
171 private Paint mSwatchOld, mSwatchNew; | |
172 | |
173 //NEW_METHOD_WORK_NEEDED_HERE | |
174 //Add Paints to represent the palettes of the new method's UI controllers | |
175 private Paint mOvalHueSat; | |
176 | |
177 private Bitmap mVerSliderBM; | |
178 private Canvas mVerSliderCv; | |
179 | |
180 private Bitmap[] mHorSlidersBM = new Bitmap[3]; | |
181 private Canvas[] mHorSlidersCv = new Canvas[3]; | |
182 | |
183 private Paint mValDimmer; | |
184 | |
185 //NEW_METHOD_WORK_NEEDED_HERE | |
186 //Add Paints to represent the icon for the new method | |
187 private Paint mOvalHueSatSmall; | |
188 | |
189 private Paint mPosMarker; | |
190 private Paint mText; | |
191 | |
192 private Rect mOldSwatchRect = new Rect(); | |
193 private Rect mNewSwatchRect = new Rect(); | |
194 private Rect mPaletteRect = new Rect(); | |
195 private Rect mVerSliderRect = new Rect(); | |
196 | |
197 private int[] mSpectrumColorsRev; | |
198 private int mOriginalColor = 0; //The color passed in at the beginning, which can be reverted to at any time by tapping the old swatch. | |
199 private float[] mHSV = new float[3]; | |
200 private int[] mRGB = new int[3]; | |
201 private float[] mYUV = new float[3]; | |
202 private String mHexStr = ""; | |
203 private boolean mHSVenabled = true; //Only true if an HSV method is enabled | |
204 private boolean mRGBenabled = true; //Only true if an RGB method is enabled | |
205 private boolean mYUVenabled = true; //Only true if a YUV method is enabled | |
206 private boolean mHexenabled = true; //Only true if an RGB method is enabled | |
207 private int[] mCoord = new int[3]; //For drawing slider/palette markers | |
208 private int mFocusedControl = -1; //Which control receives trackball events. | |
209 private OnColorChangedListener mListener; | |
210 | |
211 /** | |
212 * Ctor. | |
213 * @param c | |
214 * @param l | |
215 * @param width Used to determine orientation and adjust layout accordingly | |
216 * @param height Used to determine orientation and adjust layout accordingly | |
217 * @param color The initial color | |
218 * @throws Exception | |
219 */ | |
220 ColorPickerView(Context c, OnColorChangedListener l, int width, int height, int color) | |
221 throws Exception { | |
222 super(c); | |
223 //We need to make the dialog focusable to retrieve trackball events. | |
224 setFocusable(true); | |
225 mListener = l; | |
226 mOriginalColor = color; | |
227 Color.colorToHSV(color, mHSV); | |
228 updateAllFromHSV(); | |
229 | |
230 //Setup the layout based on whether this is a portrait or landscape orientation. | |
231 if (width <= height) { //Portrait layout | |
232 SWATCH_WIDTH = (PALETTE_DIM + SLIDER_THICKNESS) / 2; | |
233 PALETTE_POS_X = 0; | |
234 PALETTE_POS_Y = TEXT_SIZE * 4 + SWATCH_HEIGHT; | |
235 //Set more rects, lots of rects | |
236 mOldSwatchRect.set(0, TEXT_SIZE * 4, SWATCH_WIDTH, TEXT_SIZE * 4 + SWATCH_HEIGHT); | |
237 mNewSwatchRect.set(SWATCH_WIDTH, TEXT_SIZE * 4, SWATCH_WIDTH * 2, TEXT_SIZE * 4 + SWATCH_HEIGHT); | |
238 mPaletteRect.set(0, PALETTE_POS_Y, PALETTE_DIM, PALETTE_POS_Y + PALETTE_DIM); | |
239 mVerSliderRect.set(PALETTE_DIM, PALETTE_POS_Y, PALETTE_DIM + SLIDER_THICKNESS, PALETTE_POS_Y + PALETTE_DIM); | |
240 TEXT_HSV_POS[0] = 3; | |
241 TEXT_HSV_POS[1] = 0; | |
242 TEXT_RGB_POS[0] = TEXT_HSV_POS[0] + 50; | |
243 TEXT_RGB_POS[1] = TEXT_HSV_POS[1]; | |
244 TEXT_YUV_POS[0] = TEXT_HSV_POS[0] + 100; | |
245 TEXT_YUV_POS[1] = TEXT_HSV_POS[1]; | |
246 TEXT_HEX_POS[0] = TEXT_HSV_POS[0] + 150; | |
247 TEXT_HEX_POS[1] = TEXT_HSV_POS[1]; | |
248 VIEW_DIM_X = PALETTE_DIM + SLIDER_THICKNESS; | |
249 VIEW_DIM_Y = SWATCH_HEIGHT + PALETTE_DIM + TEXT_SIZE * 4; | |
250 } | |
251 else { //Landscape layout | |
252 SWATCH_WIDTH = 110; | |
253 PALETTE_POS_X = SWATCH_WIDTH; | |
254 PALETTE_POS_Y = 0; | |
255 //Set more rects, lots of rects | |
256 mOldSwatchRect.set(0, TEXT_SIZE * 7, SWATCH_WIDTH, TEXT_SIZE * 7 + SWATCH_HEIGHT); | |
257 mNewSwatchRect.set(0, TEXT_SIZE * 7 + SWATCH_HEIGHT, SWATCH_WIDTH, TEXT_SIZE * 7 + SWATCH_HEIGHT * 2); | |
258 mPaletteRect.set(SWATCH_WIDTH, PALETTE_POS_Y, SWATCH_WIDTH + PALETTE_DIM, PALETTE_POS_Y + PALETTE_DIM); | |
259 mVerSliderRect.set(SWATCH_WIDTH + PALETTE_DIM, PALETTE_POS_Y, SWATCH_WIDTH + PALETTE_DIM + SLIDER_THICKNESS, PALETTE_POS_Y + PALETTE_DIM); | |
260 TEXT_HSV_POS[0] = 3; | |
261 TEXT_HSV_POS[1] = 0; | |
262 TEXT_RGB_POS[0] = TEXT_HSV_POS[0]; | |
263 TEXT_RGB_POS[1] = (int)(TEXT_HSV_POS[1] + TEXT_SIZE * 3.5); | |
264 TEXT_YUV_POS[0] = TEXT_HSV_POS[0] + 50; | |
265 TEXT_YUV_POS[1] = (int)(TEXT_HSV_POS[1] + TEXT_SIZE * 3.5); | |
266 TEXT_HEX_POS[0] = TEXT_HSV_POS[0] + 50; | |
267 TEXT_HEX_POS[1] = TEXT_HSV_POS[1]; | |
268 VIEW_DIM_X = PALETTE_POS_X + PALETTE_DIM + SLIDER_THICKNESS; | |
269 VIEW_DIM_Y = Math.max(mNewSwatchRect.bottom, PALETTE_DIM); | |
270 } | |
271 | |
272 //Rainbows make everybody happy! | |
273 mSpectrumColorsRev = new int[] { | |
274 0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, | |
275 0xFF00FF00, 0xFFFFFF00, 0xFFFF0000, | |
276 }; | |
277 //Setup all the Paint and Shader objects. There are lots of them! | |
278 //NEW_METHOD_WORK_NEEDED_HERE | |
279 //Add Paints to represent the palettes of the new method's UI controllers | |
280 mSwatchOld = new Paint(Paint.ANTI_ALIAS_FLAG); | |
281 mSwatchOld.setStyle(Paint.Style.FILL); | |
282 mSwatchOld.setColor(Color.HSVToColor(mHSV)); | |
283 mSwatchNew = new Paint(Paint.ANTI_ALIAS_FLAG); | |
284 mSwatchNew.setStyle(Paint.Style.FILL); | |
285 mSwatchNew.setColor(Color.HSVToColor(mHSV)); | |
286 Shader shaderA = new SweepGradient(0, 0, mSpectrumColorsRev, null); | |
287 Shader shaderB = new RadialGradient(0, 0, PALETTE_CENTER_X, 0xFFFFFFFF, 0xFF000000, Shader.TileMode.CLAMP); | |
288 Shader shader = new ComposeShader(shaderA, shaderB, PorterDuff.Mode.SCREEN); | |
289 mOvalHueSat = new Paint(Paint.ANTI_ALIAS_FLAG); | |
290 mOvalHueSat.setShader(shader); | |
291 mOvalHueSat.setStyle(Paint.Style.FILL); | |
292 mOvalHueSat.setDither(true); | |
293 mVerSliderBM = Bitmap.createBitmap(SLIDER_THICKNESS, PALETTE_DIM, Bitmap.Config.RGB_565); | |
294 mVerSliderCv = new Canvas(mVerSliderBM); | |
295 | |
296 for (int i = 0; i < 3; i++) { | |
297 mHorSlidersBM[i] = Bitmap.createBitmap(PALETTE_DIM, SLIDER_THICKNESS, Bitmap.Config.RGB_565); | |
298 mHorSlidersCv[i] = new Canvas(mHorSlidersBM[i]); | |
299 } | |
300 | |
301 mValDimmer = new Paint(Paint.ANTI_ALIAS_FLAG); | |
302 mValDimmer.setStyle(Paint.Style.FILL); | |
303 mValDimmer.setDither(true); | |
304 mValDimmer.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); | |
305 //Whew, we're done making the big Paints and Shaders for the swatches, palettes, and sliders. | |
306 //Now we need to make the Paints and Shaders that will draw the little method icons in the method selector list. | |
307 //NEW_METHOD_WORK_NEEDED_HERE | |
308 //Add Paints to represent the icon for the new method | |
309 shaderA = new SweepGradient(0, 0, mSpectrumColorsRev, null); | |
310 shaderB = new RadialGradient(0, 0, PALETTE_DIM / 2, 0xFFFFFFFF, 0xFF000000, Shader.TileMode.CLAMP); | |
311 shader = new ComposeShader(shaderA, shaderB, PorterDuff.Mode.SCREEN); | |
312 mOvalHueSatSmall = new Paint(Paint.ANTI_ALIAS_FLAG); | |
313 mOvalHueSatSmall.setShader(shader); | |
314 mOvalHueSatSmall.setStyle(Paint.Style.FILL); | |
315 //Make a simple stroking Paint for drawing markers and borders and stuff like that. | |
316 mPosMarker = new Paint(Paint.ANTI_ALIAS_FLAG); | |
317 mPosMarker.setStyle(Paint.Style.STROKE); | |
318 mPosMarker.setStrokeWidth(2); | |
319 //Make a basic text Paint. | |
320 mText = new Paint(Paint.ANTI_ALIAS_FLAG); | |
321 mText.setTextSize(TEXT_SIZE); | |
322 mText.setColor(Color.WHITE); | |
323 //Kickstart | |
324 initUI(); | |
325 } | |
326 | |
327 /** | |
328 * Draw the entire view (the entire dialog). | |
329 */ | |
330 @Override | |
331 protected void onDraw(Canvas canvas) { | |
332 //Draw the old and new swatches | |
333 drawSwatches(canvas); | |
334 //Write the text | |
335 writeColorParams(canvas); | |
336 | |
337 //Draw the palette and sliders (the UI) | |
338 if (mMethod == METHOD_HS_V_PALETTE) | |
339 drawHSV1Palette(canvas); | |
340 } | |
341 | |
342 /** | |
343 * Draw the old and new swatches. | |
344 * @param canvas | |
345 */ | |
346 private void drawSwatches(Canvas canvas) { | |
347 float[] hsv = new float[3]; | |
348 mText.setTextSize(16); | |
349 //Draw the original swatch | |
350 canvas.drawRect(mOldSwatchRect, mSwatchOld); | |
351 Color.colorToHSV(mOriginalColor, hsv); | |
352 | |
353 //if (UberColorPickerDialog.isGray(mColor)) //Don't need this right here, but imp't to note | |
354 // hsv[1] = 0; | |
355 if (hsv[2] > .5) | |
356 mText.setColor(Color.BLACK); | |
357 | |
358 canvas.drawText("Revert", mOldSwatchRect.left + SWATCH_WIDTH / 2 - mText.measureText("Revert") / 2, mOldSwatchRect.top + 16, mText); | |
359 mText.setColor(Color.WHITE); | |
360 //Draw the new swatch | |
361 canvas.drawRect(mNewSwatchRect, mSwatchNew); | |
362 | |
363 if (mHSV[2] > .5) | |
364 mText.setColor(Color.BLACK); | |
365 | |
366 canvas.drawText("Accept", mNewSwatchRect.left + SWATCH_WIDTH / 2 - mText.measureText("Accept") / 2, mNewSwatchRect.top + 16, mText); | |
367 mText.setColor(Color.WHITE); | |
368 mText.setTextSize(TEXT_SIZE); | |
369 } | |
370 | |
371 /** | |
372 * Write the color parametes (HSV, RGB, YUV, Hex, etc.). | |
373 * @param canvas | |
374 */ | |
375 private void writeColorParams(Canvas canvas) { | |
376 if (mHSVenabled) { | |
377 canvas.drawText("H: " + Integer.toString((int)(mHSV[0] / 360.0f * 255)), TEXT_HSV_POS[0], TEXT_HSV_POS[1] + TEXT_SIZE, mText); | |
378 canvas.drawText("S: " + Integer.toString((int)(mHSV[1] * 255)), TEXT_HSV_POS[0], TEXT_HSV_POS[1] + TEXT_SIZE * 2, mText); | |
379 canvas.drawText("V: " + Integer.toString((int)(mHSV[2] * 255)), TEXT_HSV_POS[0], TEXT_HSV_POS[1] + TEXT_SIZE * 3, mText); | |
380 } | |
381 | |
382 if (mRGBenabled) { | |
383 canvas.drawText("R: " + mRGB[0], TEXT_RGB_POS[0], TEXT_RGB_POS[1] + TEXT_SIZE, mText); | |
384 canvas.drawText("G: " + mRGB[1], TEXT_RGB_POS[0], TEXT_RGB_POS[1] + TEXT_SIZE * 2, mText); | |
385 canvas.drawText("B: " + mRGB[2], TEXT_RGB_POS[0], TEXT_RGB_POS[1] + TEXT_SIZE * 3, mText); | |
386 } | |
387 | |
388 if (mYUVenabled) { | |
389 canvas.drawText("Y: " + Integer.toString((int)(mYUV[0] * 255)), TEXT_YUV_POS[0], TEXT_YUV_POS[1] + TEXT_SIZE, mText); | |
390 canvas.drawText("U: " + Integer.toString((int)((mYUV[1] + .5f) * 255)), TEXT_YUV_POS[0], TEXT_YUV_POS[1] + TEXT_SIZE * 2, mText); | |
391 canvas.drawText("V: " + Integer.toString((int)((mYUV[2] + .5f) * 255)), TEXT_YUV_POS[0], TEXT_YUV_POS[1] + TEXT_SIZE * 3, mText); | |
392 } | |
393 | |
394 if (mHexenabled) | |
395 canvas.drawText("#" + mHexStr, TEXT_HEX_POS[0], TEXT_HEX_POS[1] + TEXT_SIZE, mText); | |
396 } | |
397 | |
398 /** | |
399 * Place a small circle on the 2D palette to indicate the current values. | |
400 * @param canvas | |
401 * @param markerPosX | |
402 * @param markerPosY | |
403 */ | |
404 private void mark2DPalette(Canvas canvas, int markerPosX, int markerPosY) { | |
405 mPosMarker.setColor(Color.BLACK); | |
406 canvas.drawOval(new RectF(markerPosX - 5, markerPosY - 5, markerPosX + 5, markerPosY + 5), mPosMarker); | |
407 mPosMarker.setColor(Color.WHITE); | |
408 canvas.drawOval(new RectF(markerPosX - 3, markerPosY - 3, markerPosX + 3, markerPosY + 3), mPosMarker); | |
409 } | |
410 | |
411 /** | |
412 * Draw a line across the slider to indicate its current value. | |
413 * @param canvas | |
414 * @param markerPos | |
415 */ | |
416 private void markVerSlider(Canvas canvas, int markerPos) { | |
417 mPosMarker.setColor(Color.BLACK); | |
418 canvas.drawRect(new Rect(0, markerPos - 2, SLIDER_THICKNESS, markerPos + 3), mPosMarker); | |
419 mPosMarker.setColor(Color.WHITE); | |
420 canvas.drawRect(new Rect(0, markerPos, SLIDER_THICKNESS, markerPos + 1), mPosMarker); | |
421 } | |
422 | |
423 /** | |
424 * Frame the slider to indicate that it has trackball focus. | |
425 * @param canvas | |
426 */ | |
427 private void hilightFocusedVerSlider(Canvas canvas) { | |
428 mPosMarker.setColor(Color.WHITE); | |
429 canvas.drawRect(new Rect(0, 0, SLIDER_THICKNESS, PALETTE_DIM), mPosMarker); | |
430 mPosMarker.setColor(Color.BLACK); | |
431 canvas.drawRect(new Rect(2, 2, SLIDER_THICKNESS - 2, PALETTE_DIM - 2), mPosMarker); | |
432 } | |
433 | |
434 /** | |
435 * Frame the 2D palette to indicate that it has trackball focus. | |
436 * @param canvas | |
437 */ | |
438 private void hilightFocusedOvalPalette(Canvas canvas) { | |
439 mPosMarker.setColor(Color.WHITE); | |
440 canvas.drawOval(new RectF(-PALETTE_RADIUS, -PALETTE_RADIUS, PALETTE_RADIUS, PALETTE_RADIUS), mPosMarker); | |
441 mPosMarker.setColor(Color.BLACK); | |
442 canvas.drawOval(new RectF(-PALETTE_RADIUS + 2, -PALETTE_RADIUS + 2, PALETTE_RADIUS - 2, PALETTE_RADIUS - 2), mPosMarker); | |
443 } | |
444 | |
445 //NEW_METHOD_WORK_NEEDED_HERE | |
446 //To add a new method, replicate the basic draw functions here. Use the 2D palette or 1D sliders as templates for the new method. | |
447 /** | |
448 * Draw the UI for HSV with angular H and radial S combined in 2D and a 1D V slider. | |
449 * @param canvas | |
450 */ | |
451 private void drawHSV1Palette(Canvas canvas) { | |
452 canvas.save(); | |
453 canvas.translate(PALETTE_POS_X, PALETTE_POS_Y); | |
454 //Draw the 2D palette | |
455 canvas.translate(PALETTE_CENTER_X, PALETTE_CENTER_Y); | |
456 canvas.drawOval(new RectF(-PALETTE_RADIUS, -PALETTE_RADIUS, PALETTE_RADIUS, PALETTE_RADIUS), mOvalHueSat); | |
457 canvas.drawOval(new RectF(-PALETTE_RADIUS, -PALETTE_RADIUS, PALETTE_RADIUS, PALETTE_RADIUS), mValDimmer); | |
458 | |
459 if (mFocusedControl == 0) | |
460 hilightFocusedOvalPalette(canvas); | |
461 | |
462 mark2DPalette(canvas, mCoord[0], mCoord[1]); | |
463 canvas.translate(-PALETTE_CENTER_X, -PALETTE_CENTER_Y); | |
464 //Draw the 1D slider | |
465 canvas.translate(PALETTE_DIM, 0); | |
466 canvas.drawBitmap(mVerSliderBM, 0, 0, null); | |
467 | |
468 if (mFocusedControl == 1) | |
469 hilightFocusedVerSlider(canvas); | |
470 | |
471 markVerSlider(canvas, mCoord[2]); | |
472 canvas.restore(); | |
473 } | |
474 | |
475 /** | |
476 * Initialize the current color chooser's UI (set its color parameters and set its palette and slider values accordingly). | |
477 */ | |
478 private void initUI() { | |
479 initHSV1Palette(); | |
480 //Focus on the first controller (arbitrary). | |
481 mFocusedControl = 0; | |
482 } | |
483 | |
484 //NEW_METHOD_WORK_NEEDED_HERE | |
485 //To add a new method, replicate and extend the last init function shown below | |
486 /** | |
487 * Initialize a color chooser. | |
488 */ | |
489 private void initHSV1Palette() { | |
490 setOvalValDimmer(); | |
491 setVerValSlider(); | |
492 float angle = 2 * PI - mHSV[0] / (180 / 3.1415927f); | |
493 float radius = mHSV[1] * PALETTE_RADIUS; | |
457
105815cce146
minimum version android 5, target and compile version api 28
Carl Byington <carl@five-ten-sg.com>
parents:
438
diff
changeset
|
494 mCoord[0] = (int)(Math.cos(angle) * radius); |
105815cce146
minimum version android 5, target and compile version api 28
Carl Byington <carl@five-ten-sg.com>
parents:
438
diff
changeset
|
495 mCoord[1] = (int)(Math.sin(angle) * radius); |
0 | 496 mCoord[2] = PALETTE_DIM - (int)(mHSV[2] * PALETTE_DIM); |
497 } | |
498 | |
499 //NEW_METHOD_WORK_NEEDED_HERE | |
500 //To add a new method, replicate and extend the set functions below, one per UI controller in the new method | |
501 /** | |
502 * Adjust a Paint which, when painted, dims its underlying object to show the effects of varying value (brightness). | |
503 */ | |
504 private void setOvalValDimmer() { | |
505 float[] hsv = new float[3]; | |
506 hsv[0] = mHSV[0]; | |
507 hsv[1] = 0; | |
508 hsv[2] = mHSV[2]; | |
509 int gray = Color.HSVToColor(hsv); | |
510 mValDimmer.setColor(gray); | |
511 } | |
512 | |
513 /** | |
514 * Create a linear gradient shader to show variations in value. | |
515 */ | |
516 private void setVerValSlider() { | |
517 float[] hsv = new float[3]; | |
518 hsv[0] = mHSV[0]; | |
519 hsv[1] = mHSV[1]; | |
520 hsv[2] = 1; | |
521 int col = Color.HSVToColor(hsv); | |
522 int colors[] = new int[2]; | |
523 colors[0] = col; | |
524 colors[1] = 0xFF000000; | |
525 GradientDrawable gradDraw = new GradientDrawable(Orientation.TOP_BOTTOM, colors); | |
526 gradDraw.setDither(true); | |
527 gradDraw.setLevel(10000); | |
528 gradDraw.setBounds(0, 0, SLIDER_THICKNESS, PALETTE_DIM); | |
529 gradDraw.draw(mVerSliderCv); | |
530 } | |
531 | |
532 /** | |
533 * Report the correct tightly bounded dimensions of the view. | |
534 */ | |
535 @Override | |
536 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | |
537 setMeasuredDimension(VIEW_DIM_X, VIEW_DIM_Y); | |
538 } | |
539 | |
540 /** | |
541 * Wrap Math.round(). I'm not a Java expert. Is this the only way to avoid writing "(int)Math.round" everywhere? | |
542 * @param x | |
543 * @return | |
544 */ | |
545 private int round(double x) { | |
546 return (int)Math.round(x); | |
547 } | |
548 | |
549 /** | |
550 * Limit a value to the range [0,1]. | |
551 * @param n | |
552 * @return | |
553 */ | |
554 private float pinToUnit(float n) { | |
555 if (n < 0) { | |
556 n = 0; | |
557 } | |
558 else if (n > 1) { | |
559 n = 1; | |
560 } | |
561 | |
562 return n; | |
563 } | |
564 | |
565 /** | |
566 * Limit a value to the range [0,max]. | |
567 * @param n | |
568 * @param max | |
569 * @return | |
570 */ | |
571 private float pin(float n, float max) { | |
572 if (n < 0) { | |
573 n = 0; | |
574 } | |
575 else if (n > max) { | |
576 n = max; | |
577 } | |
578 | |
579 return n; | |
580 } | |
581 | |
582 /** | |
583 * Limit a value to the range [min,max]. | |
584 * @param n | |
585 * @param min | |
586 * @param max | |
587 * @return | |
588 */ | |
589 private float pin(float n, float min, float max) { | |
590 if (n < min) { | |
591 n = min; | |
592 } | |
593 else if (n > max) { | |
594 n = max; | |
595 } | |
596 | |
597 return n; | |
598 } | |
599 | |
600 /** | |
601 * No clue what this does (some sort of average/mean I presume). It came with the original UberColorPickerDialog | |
602 * in the API Demos and wasn't documented. I don't feel like spending any time figuring it out, I haven't looked at it at all. | |
603 * @param s | |
604 * @param d | |
605 * @param p | |
606 * @return | |
607 */ | |
608 private int ave(int s, int d, float p) { | |
609 return s + round(p * (d - s)); | |
610 } | |
611 | |
612 /** | |
613 * Came with the original UberColorPickerDialog in the API Demos, wasn't documented. I believe it takes an array of | |
614 * colors and a value in the range [0,1] and interpolates a resulting color in a seemingly predictable manner. | |
615 * I haven't looked at it at all. | |
616 * @param colors | |
617 * @param unit | |
618 * @return | |
619 */ | |
620 private int interpColor(int colors[], float unit) { | |
621 if (unit <= 0) { | |
622 return colors[0]; | |
623 } | |
624 | |
625 if (unit >= 1) { | |
626 return colors[colors.length - 1]; | |
627 } | |
628 | |
629 float p = unit * (colors.length - 1); | |
630 int i = (int)p; | |
631 p -= i; | |
632 // now p is just the fractional part [0...1) and i is the index | |
633 int c0 = colors[i]; | |
634 int c1 = colors[i + 1]; | |
635 int a = ave(Color.alpha(c0), Color.alpha(c1), p); | |
636 int r = ave(Color.red(c0), Color.red(c1), p); | |
637 int g = ave(Color.green(c0), Color.green(c1), p); | |
638 int b = ave(Color.blue(c0), Color.blue(c1), p); | |
639 return Color.argb(a, r, g, b); | |
640 } | |
641 | |
642 /** | |
643 * A standard point-in-rect routine. | |
644 * @param x | |
645 * @param y | |
646 * @param r | |
647 * @return true if point x,y is in rect r | |
648 */ | |
649 public boolean ptInRect(int x, int y, Rect r) { | |
650 return x > r.left && x < r.right && y > r.top && y < r.bottom; | |
651 } | |
652 | |
653 /** | |
654 * Process trackball events. Used mainly for fine-tuned color adjustment, or alternatively to switch between slider controls. | |
655 */ | |
656 @Override | |
657 public boolean dispatchTrackballEvent(MotionEvent event) { | |
658 float x = event.getX(); | |
659 float y = event.getY(); | |
660 //A longer event history implies faster trackball movement. | |
661 //Use it to infer a larger jump and therefore faster palette/slider adjustment. | |
662 int jump = event.getHistorySize() + 1; | |
663 | |
664 switch (event.getAction()) { | |
665 case MotionEvent.ACTION_DOWN: { | |
666 } | |
667 break; | |
668 | |
669 case MotionEvent.ACTION_MOVE: { | |
670 //NEW_METHOD_WORK_NEEDED_HERE | |
671 //To add a new method, replicate and extend the appropriate entry in this list, | |
672 //depending on whether you use 1D or 2D controllers | |
673 switch (mMethod) { | |
674 case METHOD_HS_V_PALETTE: | |
675 if (mFocusedControl == 0) { | |
676 changeHSPalette(x, y, jump); | |
677 } | |
678 else if (mFocusedControl == 1) { | |
679 if (y < 0) | |
680 changeSlider(mFocusedControl, true, jump); | |
681 else if (y > 0) | |
682 changeSlider(mFocusedControl, false, jump); | |
683 } | |
684 | |
685 break; | |
686 } | |
687 } | |
688 break; | |
689 | |
690 case MotionEvent.ACTION_UP: { | |
691 } | |
692 break; | |
693 } | |
694 | |
695 return true; | |
696 } | |
697 | |
698 //NEW_METHOD_WORK_NEEDED_HERE | |
699 //To add a new method, replicate and extend the appropriate functions below, | |
700 //one per UI controller in the new method | |
701 /** | |
702 * Effect a trackball change to a 2D palette. | |
703 * @param x -1: negative x change, 0: no x change, +1: positive x change. | |
704 * @param y -1: negative y change, 0, no y change, +1: positive y change. | |
705 * @param jump the amount by which to change. | |
706 */ | |
707 private void changeHSPalette(float x, float y, int jump) { | |
708 int x2 = 0, y2 = 0; | |
709 | |
710 if (x < 0) | |
711 x2 = -jump; | |
712 else if (x > 0) | |
713 x2 = jump; | |
714 | |
715 if (y < 0) | |
716 y2 = -jump; | |
717 else if (y > 0) | |
718 y2 = jump; | |
719 | |
720 mCoord[0] += x2; | |
721 mCoord[1] += y2; | |
722 | |
723 if (mCoord[0] < -PALETTE_RADIUS) | |
724 mCoord[0] = -PALETTE_RADIUS; | |
725 else if (mCoord[0] > PALETTE_RADIUS) | |
726 mCoord[0] = PALETTE_RADIUS; | |
727 | |
728 if (mCoord[1] < -PALETTE_RADIUS) | |
729 mCoord[1] = -PALETTE_RADIUS; | |
730 else if (mCoord[1] > PALETTE_RADIUS) | |
731 mCoord[1] = PALETTE_RADIUS; | |
732 | |
457
105815cce146
minimum version android 5, target and compile version api 28
Carl Byington <carl@five-ten-sg.com>
parents:
438
diff
changeset
|
733 float radius = (float)Math.sqrt(mCoord[0] * mCoord[0] + mCoord[1] * mCoord[1]); |
0 | 734 |
735 if (radius > PALETTE_RADIUS) | |
736 radius = PALETTE_RADIUS; | |
737 | |
738 float angle = (float)Math.atan2(mCoord[1], mCoord[0]); | |
739 // need to turn angle [-PI ... PI] into unit [0....1] | |
740 float unit = angle / (2 * PI); | |
741 | |
742 if (unit < 0) { | |
743 unit += 1; | |
744 } | |
745 | |
457
105815cce146
minimum version android 5, target and compile version api 28
Carl Byington <carl@five-ten-sg.com>
parents:
438
diff
changeset
|
746 mCoord[0] = round(Math.cos(angle) * radius); |
105815cce146
minimum version android 5, target and compile version api 28
Carl Byington <carl@five-ten-sg.com>
parents:
438
diff
changeset
|
747 mCoord[1] = round(Math.sin(angle) * radius); |
0 | 748 int c = interpColor(mSpectrumColorsRev, unit); |
749 float[] hsv = new float[3]; | |
750 Color.colorToHSV(c, hsv); | |
751 mHSV[0] = hsv[0]; | |
752 mHSV[1] = radius / PALETTE_RADIUS; | |
753 updateAllFromHSV(); | |
754 mSwatchNew.setColor(Color.HSVToColor(mHSV)); | |
755 setVerValSlider(); | |
756 invalidate(); | |
757 } | |
758 | |
759 /** | |
760 * Effect a trackball change to a 1D slider. | |
761 * @param slider id of the slider to be effected | |
762 * @param increase true if the change is an increase, false if a decrease | |
763 * @param jump the amount by which to change in units of the range [0,255] | |
764 */ | |
765 private void changeSlider(int slider, boolean increase, int jump) { | |
766 //NEW_METHOD_WORK_NEEDED_HERE | |
767 //It is only necessary to add an entry here for a new method if the new method uses a 1D slider. | |
768 //Note, some sliders are horizontal and others are vertical. | |
769 //They differ a bit, especially in a sign flip on the vertical axis. | |
770 if (mMethod == METHOD_HS_V_PALETTE) { | |
771 //slider *must* equal 1 | |
772 mHSV[2] += (increase ? jump : -jump) / 256.0f; | |
773 mHSV[2] = pinToUnit(mHSV[2]); | |
774 updateAllFromHSV(); | |
775 mCoord[2] = PALETTE_DIM - (int)(mHSV[2] * PALETTE_DIM); | |
776 mSwatchNew.setColor(Color.HSVToColor(mHSV)); | |
777 setOvalValDimmer(); | |
778 invalidate(); | |
779 } | |
780 } | |
781 | |
782 /** | |
783 * Keep all colorspace representations in sync. | |
784 */ | |
785 private void updateRGBfromHSV() { | |
786 int color = Color.HSVToColor(mHSV); | |
787 mRGB[0] = Color.red(color); | |
788 mRGB[1] = Color.green(color); | |
789 mRGB[2] = Color.blue(color); | |
790 } | |
791 | |
792 /** | |
793 * Keep all colorspace representations in sync. | |
794 */ | |
795 private void updateYUVfromRGB() { | |
796 float r = mRGB[0] / 255.0f; | |
797 float g = mRGB[1] / 255.0f; | |
798 float b = mRGB[2] / 255.0f; | |
799 ColorMatrix cm = new ColorMatrix(); | |
800 cm.setRGB2YUV(); | |
801 final float[] a = cm.getArray(); | |
802 mYUV[0] = a[0] * r + a[1] * g + a[2] * b; | |
803 mYUV[0] = pinToUnit(mYUV[0]); | |
804 mYUV[1] = a[5] * r + a[6] * g + a[7] * b; | |
805 mYUV[1] = pin(mYUV[1], -.5f, .5f); | |
806 mYUV[2] = a[10] * r + a[11] * g + a[12] * b; | |
807 mYUV[2] = pin(mYUV[2], -.5f, .5f); | |
808 } | |
809 | |
810 /** | |
811 * Keep all colorspace representations in sync. | |
812 */ | |
813 private void updateHexFromHSV() { | |
814 //For now, assume 100% opacity | |
815 mHexStr = Integer.toHexString(Color.HSVToColor(mHSV)).toUpperCase(); | |
816 mHexStr = mHexStr.substring(2, mHexStr.length()); | |
817 } | |
818 | |
819 /** | |
820 * Keep all colorspace representations in sync. | |
821 */ | |
822 private void updateAllFromHSV() { | |
823 //Update mRGB | |
824 if (mRGBenabled || mYUVenabled) | |
825 updateRGBfromHSV(); | |
826 | |
827 //Update mYUV | |
828 if (mYUVenabled) | |
829 updateYUVfromRGB(); | |
830 | |
831 //Update mHexStr | |
832 if (mRGBenabled) | |
833 updateHexFromHSV(); | |
834 } | |
835 | |
836 /** | |
837 * Process touch events: down, move, and up | |
838 */ | |
839 @Override | |
840 public boolean onTouchEvent(MotionEvent event) { | |
841 float x = event.getX(); | |
842 float y = event.getY(); | |
843 //Generate coordinates which are palette=local with the origin at the upper left of the main 2D palette | |
844 int y2 = (int)(pin(round(y - PALETTE_POS_Y), PALETTE_DIM)); | |
845 //Generate coordinates which are palette-local with the origin at the center of the main 2D palette | |
846 float circlePinnedX = x - PALETTE_POS_X - PALETTE_CENTER_X; | |
847 float circlePinnedY = y - PALETTE_POS_Y - PALETTE_CENTER_Y; | |
848 //Is the event in a swatch? | |
849 boolean inSwatchOld = ptInRect(round(x), round(y), mOldSwatchRect); | |
850 boolean inSwatchNew = ptInRect(round(x), round(y), mNewSwatchRect); | |
851 //Get the event's distance from the center of the main 2D palette | |
457
105815cce146
minimum version android 5, target and compile version api 28
Carl Byington <carl@five-ten-sg.com>
parents:
438
diff
changeset
|
852 float radius = (float)Math.sqrt(circlePinnedX * circlePinnedX + circlePinnedY * circlePinnedY); |
0 | 853 //Is the event in a circle-pinned 2D palette? |
854 boolean inOvalPalette = radius <= PALETTE_RADIUS; | |
855 | |
856 //Pin the radius | |
857 if (radius > PALETTE_RADIUS) | |
858 radius = PALETTE_RADIUS; | |
859 | |
860 //Is the event in a vertical slider to the right of the main 2D palette | |
861 boolean inVerSlider = ptInRect(round(x), round(y), mVerSliderRect); | |
862 | |
863 switch (event.getAction()) { | |
864 case MotionEvent.ACTION_DOWN: | |
865 mTracking = TRACKED_NONE; | |
866 | |
867 if (inSwatchOld) | |
868 mTracking = TRACK_SWATCH_OLD; | |
869 else if (inSwatchNew) | |
870 mTracking = TRACK_SWATCH_NEW; | |
871 //NEW_METHOD_WORK_NEEDED_HERE | |
872 //To add a new method, replicate and extend the last entry in this list | |
873 else if (mMethod == METHOD_HS_V_PALETTE) { | |
874 if (inOvalPalette) { | |
875 mTracking = TRACK_HS_PALETTE; | |
876 mFocusedControl = 0; | |
877 } | |
878 else if (inVerSlider) { | |
879 mTracking = TRACK_VER_VALUE_SLIDER; | |
880 mFocusedControl = 1; | |
881 } | |
882 } | |
883 | |
884 case MotionEvent.ACTION_MOVE: | |
885 | |
886 //NEW_METHOD_WORK_NEEDED_HERE | |
887 //To add a new method, replicate and extend the entries in this list, | |
888 //one per UI controller the new method requires. | |
889 if (mTracking == TRACK_HS_PALETTE) { | |
890 float angle = (float)java.lang.Math.atan2(circlePinnedY, circlePinnedX); | |
891 // need to turn angle [-PI ... PI] into unit [0....1] | |
892 float unit = angle / (2 * PI); | |
893 | |
894 if (unit < 0) { | |
895 unit += 1; | |
896 } | |
897 | |
457
105815cce146
minimum version android 5, target and compile version api 28
Carl Byington <carl@five-ten-sg.com>
parents:
438
diff
changeset
|
898 mCoord[0] = round(Math.cos(angle) * radius); |
105815cce146
minimum version android 5, target and compile version api 28
Carl Byington <carl@five-ten-sg.com>
parents:
438
diff
changeset
|
899 mCoord[1] = round(Math.sin(angle) * radius); |
0 | 900 int c = interpColor(mSpectrumColorsRev, unit); |
901 float[] hsv = new float[3]; | |
902 Color.colorToHSV(c, hsv); | |
903 mHSV[0] = hsv[0]; | |
904 mHSV[1] = radius / PALETTE_RADIUS; | |
905 updateAllFromHSV(); | |
906 mSwatchNew.setColor(Color.HSVToColor(mHSV)); | |
907 setVerValSlider(); | |
908 invalidate(); | |
909 } | |
910 else if (mTracking == TRACK_VER_VALUE_SLIDER) { | |
911 if (mCoord[2] != y2) { | |
912 mCoord[2] = y2; | |
913 float value = 1.0f - (float)y2 / (float)PALETTE_DIM; | |
914 mHSV[2] = value; | |
915 updateAllFromHSV(); | |
916 mSwatchNew.setColor(Color.HSVToColor(mHSV)); | |
917 setOvalValDimmer(); | |
918 invalidate(); | |
919 } | |
920 } | |
921 | |
922 break; | |
923 | |
924 case MotionEvent.ACTION_UP: | |
925 | |
926 //NEW_METHOD_WORK_NEEDED_HERE | |
927 //To add a new method, replicate and extend the last entry in this list. | |
928 if (mTracking == TRACK_SWATCH_OLD && inSwatchOld) { | |
929 Color.colorToHSV(mOriginalColor, mHSV); | |
930 mSwatchNew.setColor(mOriginalColor); | |
931 initUI(); | |
932 invalidate(); | |
933 } | |
934 else if (mTracking == TRACK_SWATCH_NEW && inSwatchNew) { | |
935 mListener.colorChanged(mSwatchNew.getColor()); | |
936 invalidate(); | |
937 } | |
938 | |
939 mTracking = TRACKED_NONE; | |
940 break; | |
941 } | |
942 | |
943 return true; | |
944 } | |
945 } | |
946 } |