From b0a9c38dd9eac137330b5c7c97503bd19ade22e4 Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Wed, 3 Jun 2020 23:01:07 +0200 Subject: [PATCH 01/31] Gradle upgrade --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- supla-android.iml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 6b80ead54..4dc1795ee 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.3' + classpath 'com.android.tools.build:gradle:4.0.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 815a3ac3a..fca5ea799 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed May 06 22:01:10 CEST 2020 +#Wed Jun 03 23:00:13 CEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/supla-android.iml b/supla-android.iml index 0b3e8c1cf..afac9ee08 100644 --- a/supla-android.iml +++ b/supla-android.iml @@ -1,5 +1,5 @@ - + From df83f8e9ffdac7f3816a820035d83702f3eaddaa Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Thu, 4 Jun 2020 22:38:42 +0200 Subject: [PATCH 02/31] ColorBrightnessPicker - Unused functionalities have been removed. Added new ones not yet fully implemented. --- .../android/SuplaColorBrightnessPicker.java | 284 ++++++------------ 1 file changed, 100 insertions(+), 184 deletions(-) diff --git a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java index 7f4d75dc9..7302090c5 100644 --- a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java +++ b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java @@ -10,11 +10,9 @@ import android.graphics.RectF; import android.graphics.Shader; import android.graphics.SweepGradient; -import android.graphics.Typeface; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; - import java.util.ArrayList; /* @@ -33,10 +31,6 @@ of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - Fragments of code based on: - https://github.com/chiralcode/Android-Color-Picker/blob/master/src/com/chiralcode/colorpicker/ColorPicker.java - https://github.com/LarsWerkman/HoloColorPicker/blob/master/libary/src/main/java/com/larswerkman/holocolorpicker/ColorPicker.java */ public class SuplaColorBrightnessPicker extends View { @@ -60,8 +54,6 @@ public class SuplaColorBrightnessPicker extends View { private RectF rectF = new RectF(); private float centerX; private float centerY; - private float wheelWidth; - private float arrowHeight; private float outerWheelWidth; private double outerArrowHeight_a; private double outerArrowHeight_b; @@ -74,7 +66,6 @@ public class SuplaColorBrightnessPicker extends View { private double innerWheelPointerAngle; private int selectedColor; private double selectedBrightness; - private int selectedBrightnessColor; private Path outerArrowPath; private Paint outerArrowPaint; private Path innerArrowPath; @@ -86,17 +77,16 @@ public class SuplaColorBrightnessPicker extends View { private Shader bwShader; // brightness wheel shader private Matrix gradientRotationMatrix; private boolean colorWheelVisible; - private boolean bwBrightnessWheelVisible; - private boolean colorBrightnessWheelVisible; - private boolean percentVisible; private boolean colorWheelMove; private boolean brightnessWheelMove; + private boolean colorfulBrightnessWheel; + private boolean circleInsteadArrow; private double lastTouchedAngle; private OnColorBrightnessChangeListener mOnChangeListener; private Rect bounds; - private Paint textPaint; private ArrayList ColorMarkers; private ArrayList BrightnessMarkers; + public SuplaColorBrightnessPicker(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); @@ -115,8 +105,6 @@ public SuplaColorBrightnessPicker(Context context) { private void init() { colorWheelVisible = true; - bwBrightnessWheelVisible = false; - colorBrightnessWheelVisible = false; paint = new Paint(); @@ -141,7 +129,6 @@ private void init() { innerArrowPaint = new Paint(); innerWheelPointerAngle = Math.toRadians(-90); - selectedBrightnessColor = calculateColor((float) (innerWheelPointerAngle - m90d), BW); selectedBrightness = 0; gradientRotationMatrix = new Matrix(); @@ -150,18 +137,12 @@ private void init() { colorWheelMove = false; brightnessWheelMove = false; - - percentVisible = true; + colorfulBrightnessWheel = true; + circleInsteadArrow = false; bounds = new Rect(); - textPaint = new Paint(); - - textPaint.setAntiAlias(true); - textPaint.setColor(Color.BLACK); - - setWheelWidth(100); - setArrowHeight(100); + setBWcolor(); } private int ave(int s, int d, float p) { @@ -252,81 +233,91 @@ private void drawMarkers(Canvas canvas, float radius, float markerSize, ArrayLis } + private void drawCirclePointer(Canvas canvas, double angle, + float wheelRadius, float wheelWidth) { + + float x = (float)Math.cos(angle) * wheelRadius; + float y = (float)Math.sin(angle) * wheelRadius; + + paint.setStyle(Paint.Style.STROKE); + paint.setColor(Color.BLACK); + + canvas.drawCircle(x, y, wheelWidth * 0.47f, paint); + } + @Override protected void onDraw(Canvas canvas) { canvas.translate(centerX, centerY); - if (colorWheelVisible) { cwPaint.setShader(cwShader); rectF.set(-outerWheelRadius, -outerWheelRadius, outerWheelRadius, outerWheelRadius); canvas.drawOval(rectF, cwPaint); - drawOuterPointerArrow(canvas, outerTop, - outerWheelPointerAngle, - outerWheelRadius, - outerWheelWidth, - -(outerArrowHeight_a / 4), - outerArrowHeight_a, - outerArrowHeight_b, - selectedColor, - outerArrowPath, - outerArrowPaint); + + if (circleInsteadArrow) { + drawCirclePointer(canvas, outerWheelPointerAngle, + outerWheelRadius, outerWheelWidth); + } else { + drawArrow(canvas, outerTop, + outerWheelPointerAngle, + outerWheelRadius, + outerWheelWidth, + -(outerArrowHeight_a / 4), + outerArrowHeight_a, + outerArrowHeight_b, + selectedColor, + outerArrowPath, + outerArrowPaint); + } + drawMarkers(canvas, outerWheelRadius, outerWheelWidth / 6, ColorMarkers, false); } - if (bwBrightnessWheelVisible - || colorBrightnessWheelVisible) { + bwPaint.setShader(bwShader); + rectF.set(-innerWheelRadius, -innerWheelRadius, innerWheelRadius, innerWheelRadius); + canvas.drawOval(rectF, bwPaint); - bwPaint.setShader(bwShader); - rectF.set(-innerWheelRadius, -innerWheelRadius, innerWheelRadius, innerWheelRadius); - canvas.drawOval(rectF, bwPaint); + int negative; + double arrowOffset; - int negative; - double arrowOffset; - - if (colorWheelVisible) { - negative = -1; - arrowOffset = innerArrowHeight_a / 4 - innerWheelWidth; - } else { - negative = 1; - arrowOffset = -(innerArrowHeight_a / 4); - } + if (colorWheelVisible) { + negative = -1; + arrowOffset = innerArrowHeight_a / 4 - innerWheelWidth; + } else { + negative = 1; + arrowOffset = -(innerArrowHeight_a / 4); + } - drawOuterPointerArrow(canvas, innerTop, + if (circleInsteadArrow) { + drawCirclePointer(canvas, innerWheelPointerAngle, + innerWheelRadius, innerWheelWidth); + } else { + drawArrow(canvas, innerTop, innerWheelPointerAngle, innerWheelRadius, innerWheelWidth, arrowOffset, negative * innerArrowHeight_a, negative * innerArrowHeight_b, - selectedBrightnessColor, + calculateColor((float) (innerWheelPointerAngle - m90d), BW), innerArrowPath, innerArrowPaint); - - if (percentVisible) { - - String text = Integer.toString((int) selectedBrightness) + "%"; - - textPaint.getTextBounds(text, 0, text.length(), bounds); - canvas.drawText(text, -(bounds.width() / 2), bounds.height() / 2, textPaint); - } - - drawMarkers(canvas, innerWheelRadius, innerWheelWidth / 6, - BrightnessMarkers, true); - } + drawMarkers(canvas, innerWheelRadius, innerWheelWidth / 6, + BrightnessMarkers, true); + } - private void drawOuterPointerArrow(Canvas canvas, PointerTop top, double topAngle, - float wheelRadius, float wheelWidth, double arrowOffset, - double arrowHeight_a, double arrowHeight_b, - int color, Path arrowPath, Paint arrowPaint) { + private void drawArrow(Canvas canvas, PointerTop top, double topAngle, + float wheelRadius, float wheelWidth, double arrowOffset, + double arrowHeight_a, double arrowHeight_b, + int color, Path arrowPath, Paint arrowPaint) { top.X = Math.cos(topAngle) * (wheelRadius + wheelWidth / 2 + arrowOffset); top.Y = Math.sin(topAngle) * (wheelRadius + wheelWidth / 2 + arrowOffset); @@ -370,7 +361,7 @@ private void drawOuterPointerArrow(Canvas canvas, PointerTop top, double topAngl } private void setBWcolor() { - int color = colorBrightnessWheelVisible ? selectedColor : Color.WHITE; + int color = colorWheelVisible && colorfulBrightnessWheel ? selectedColor : Color.WHITE; if (BW[1] != color) { BW[1] = color; @@ -383,6 +374,16 @@ private void setBWcolor() { private void _onSizeChanged() { + float arrowHeight = 0; + float wheelWidth = this.getWidth() > this.getHeight() ? this.getHeight() : this.getWidth(); + + if (circleInsteadArrow) { + wheelWidth /= 7.0f; + } else { + wheelWidth /= 10.0f; + arrowHeight = wheelWidth * 0.9f; + } + outerWheelWidth = wheelWidth / 2; outerArrowHeight_a = arrowHeight; outerArrowHeight_b = outerArrowHeight_a * 0.6; @@ -396,15 +397,15 @@ private void _onSizeChanged() { centerX = this.getWidth() / 2; centerY = this.getHeight() / 2; - if (colorWheelVisible - && (bwBrightnessWheelVisible || colorBrightnessWheelVisible)) { + if (colorWheelVisible && !circleInsteadArrow) { outerWheelWidth = wheelWidth / 2; } else { outerWheelWidth = wheelWidth; } innerWheelWidth = outerWheelWidth; - outerWheelRadius = Math.min(centerX, centerY) - outerWheelWidth / 2 - (int) (arrowHeight); + int margin = circleInsteadArrow ? 0 : (int) (arrowHeight); + outerWheelRadius = Math.min(centerX, centerY) - outerWheelWidth / 2 - margin; if (colorWheelVisible) { innerWheelRadius = outerWheelRadius - innerWheelWidth; @@ -412,7 +413,6 @@ private void _onSizeChanged() { innerWheelRadius = outerWheelRadius; } - textPaint.setTextSize((int) (innerWheelRadius * 0.4)); cwPaint.setStrokeWidth(outerWheelWidth); bwPaint.setStrokeWidth(innerWheelWidth); } @@ -504,8 +504,7 @@ public boolean onTouchEvent(MotionEvent event) { colorWheelMove = true; brightnessWheelMove = false; - } else if ((bwBrightnessWheelVisible || colorBrightnessWheelVisible) - && Math.abs(innerTop.X - x) <= innerTop.Height + } else if (Math.abs(innerTop.X - x) <= innerTop.Height && Math.abs(innerTop.Y - y) <= innerTop.Height && ((colorWheelVisible && sqrt <= innerWheelRadius - innerWheelWidth / 2 @@ -521,7 +520,7 @@ public boolean onTouchEvent(MotionEvent event) { lastTouchedAngle = inRads; - if (!getMoving()) + if (!isMoving()) return super.onTouchEvent(event); break; @@ -536,8 +535,8 @@ public boolean onTouchEvent(MotionEvent event) { if (newColor != selectedColor) { - selectedColor = newColor; setBWcolor(); + selectedColor = newColor; invalidate(); if (mOnChangeListener != null) @@ -584,11 +583,6 @@ public boolean onTouchEvent(MotionEvent event) { } - if ((colorWheelMove || brightnessWheelMove) - && (bwBrightnessWheelVisible || colorBrightnessWheelVisible)) { - selectedBrightnessColor = calculateColor((float) (innerWheelPointerAngle - m90d), BW); - } - lastTouchedAngle = inRads; break; @@ -611,9 +605,7 @@ public void setColor(int color) { color = 0xFFFFFFFF; if (selectedColor != color) { - selectedColor = color; - setBWcolor(); outerWheelPointerAngle = colorToAngle(color); if (color == Color.WHITE) @@ -621,10 +613,8 @@ public void setColor(int color) { else selectedColor = calculateColor((float) outerWheelPointerAngle, Colors); - setBWcolor(); - setBrightnessValue(selectedBrightness); + invalidate(); } - } public boolean getColorWheelVisible() { @@ -632,29 +622,12 @@ public boolean getColorWheelVisible() { } public void setColorWheelVisible(boolean visible) { - if (visible != colorWheelVisible) { - - if (visible) { - bwBrightnessWheelVisible = false; - colorBrightnessWheelVisible = false; - } else { - bwBrightnessWheelVisible = true; - colorBrightnessWheelVisible = false; - } - colorWheelVisible = visible; + setBWcolor(); _onSizeChanged(); invalidate(); } - - } - - public void setPercentVisible(boolean visible) { - if (percentVisible != visible) { - percentVisible = visible; - invalidate(); - } } public double getBrightnessValue() { @@ -664,11 +637,9 @@ public double getBrightnessValue() { public void setBrightnessValue(double value) { innerWheelPointerAngle = brightnessToAngle(value); - selectedBrightnessColor = calculateColor((float) (innerWheelPointerAngle - m90d), BW); selectedBrightness = value; invalidate(); - } private double brightnessToAngle(double value) { @@ -696,82 +667,7 @@ else if (value > 100) return result; } - public float getWheelWidth() { - return wheelWidth; - } - - public void setWheelWidth(float wheelWidth) { - this.wheelWidth = wheelWidth; - _onSizeChanged(); - invalidate(); - } - - public float getArrowHeight() { - return arrowHeight; - } - - public void setArrowHeight(float arrowHeight) { - this.arrowHeight = arrowHeight; - } - - public void setTextTypeface(Typeface typeface) { - - textPaint.setTypeface(typeface); - invalidate(); - } - - public boolean getColorBrightnessWheelVisible() { - return colorBrightnessWheelVisible; - } - - public void setColorBrightnessWheelVisible(boolean visible) { - - if (visible != colorBrightnessWheelVisible) { - - if (visible) { - colorWheelVisible = true; - bwBrightnessWheelVisible = false; - } - - colorBrightnessWheelVisible = visible; - - setBWcolor(); - selectedBrightnessColor = calculateColor((float) (innerWheelPointerAngle - m90d), BW); - - _onSizeChanged(); - invalidate(); - } - - } - - public boolean getBWBrightnessWheelVisible() { - return bwBrightnessWheelVisible; - } - - public void setBWBrightnessWheelVisible(boolean visible) { - - if (visible != bwBrightnessWheelVisible) { - - if (visible) { - colorBrightnessWheelVisible = false; - colorWheelVisible = false; - } else { - colorBrightnessWheelVisible = false; - colorWheelVisible = true; - } - - bwBrightnessWheelVisible = visible; - - setBWcolor(); - selectedBrightnessColor = calculateColor((float) (innerWheelPointerAngle - m90d), BW); - - _onSizeChanged(); - invalidate(); - } - - } - - public boolean getMoving() { + public boolean isMoving() { return colorWheelMove || brightnessWheelMove; } @@ -793,6 +689,26 @@ public void setBrightnessMarkers(ArrayList brightnessMarkers) { invalidate(); } + public boolean isColorfulBrightnessWheel() { + return colorfulBrightnessWheel; + } + + public void setColorfulBrightnessWheel(boolean colorfulBrightnessWheel) { + this.colorfulBrightnessWheel = colorfulBrightnessWheel; + setBWcolor(); + invalidate(); + } + + public boolean isCircleInsteadArrow() { + return circleInsteadArrow; + } + + public void setCircleInsteadArrow(boolean circleInsteadArrow) { + this.circleInsteadArrow = circleInsteadArrow; + _onSizeChanged(); + invalidate(); + } + public interface OnColorBrightnessChangeListener { void onColorChanged(SuplaColorBrightnessPicker scbPicker, int color); From ad738151c4fae4c0a6bb74c85ca5a8c199665ddc Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Fri, 5 Jun 2020 18:19:07 +0200 Subject: [PATCH 03/31] =?UTF-8?q?Realizacja=20wska=C5=BAnik=C3=B3w=20w=20k?= =?UTF-8?q?szta=C5=82cie=20ko=C5=82a?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/SuplaColorBrightnessPicker.java | 354 +++++++++--------- 1 file changed, 171 insertions(+), 183 deletions(-) diff --git a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java index 7302090c5..ac2c3f68c 100644 --- a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java +++ b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java @@ -6,7 +6,7 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; -import android.graphics.Rect; +import android.graphics.PointF; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.SweepGradient; @@ -49,27 +49,26 @@ public class SuplaColorBrightnessPicker extends View { Color.WHITE }; - private PointerTop outerTop; - private PointerTop innerTop; + private PointF colorPointerCenter; + private PointF brightnessPointerCenter; private RectF rectF = new RectF(); private float centerX; private float centerY; - private float outerWheelWidth; - private double outerArrowHeight_a; - private double outerArrowHeight_b; - private float outerWheelRadius; - private double outerWheelPointerAngle; - private double innerArrowHeight_a; - private double innerArrowHeight_b; - private float innerWheelWidth; - private float innerWheelRadius; - private double innerWheelPointerAngle; + private float colorWheelWidth; + private double pointerHeight; + private double arrowHeight_a; + private double arrowHeight_b; + private float colorWheelRadius; + private double colorWheelPointerAngle; + private float brightnessWheelWidth; + private float brightnessWheelRadius; + private double brightnessWheelPointerAngle; private int selectedColor; private double selectedBrightness; - private Path outerArrowPath; - private Paint outerArrowPaint; - private Path innerArrowPath; - private Paint innerArrowPaint; + private Path colorArrowPath; + private Paint colorArrowPaint; + private Path brightnessArrowPath; + private Paint brightnessArrowPaint; private Paint paint; private Paint cwPaint; // color wheel paint private Shader cwShader; // color wheel shader @@ -81,9 +80,8 @@ public class SuplaColorBrightnessPicker extends View { private boolean brightnessWheelMove; private boolean colorfulBrightnessWheel; private boolean circleInsteadArrow; - private double lastTouchedAngle; + private double touchAngleDiff; private OnColorBrightnessChangeListener mOnChangeListener; - private Rect bounds; private ArrayList ColorMarkers; private ArrayList BrightnessMarkers; @@ -116,19 +114,19 @@ private void init() { bwPaint = new Paint(Paint.ANTI_ALIAS_FLAG); bwPaint.setStyle(Paint.Style.STROKE); - outerArrowPath = new Path(); - outerArrowPaint = new Paint(); + colorArrowPath = new Path(); + colorArrowPaint = new Paint(); - outerTop = new PointerTop(); - innerTop = new PointerTop(); + colorPointerCenter = new PointF(); + brightnessPointerCenter = new PointF(); - outerWheelPointerAngle = Math.toRadians(-90); - selectedColor = calculateColor((float) outerWheelPointerAngle, Colors); + colorWheelPointerAngle = Math.toRadians(-90); + selectedColor = calculateColor((float) colorWheelPointerAngle, Colors); - innerArrowPath = new Path(); - innerArrowPaint = new Paint(); + brightnessArrowPath = new Path(); + brightnessArrowPaint = new Paint(); - innerWheelPointerAngle = Math.toRadians(-90); + brightnessWheelPointerAngle = Math.toRadians(-90); selectedBrightness = 0; gradientRotationMatrix = new Matrix(); @@ -140,8 +138,6 @@ private void init() { colorfulBrightnessWheel = true; circleInsteadArrow = false; - bounds = new Rect(); - setBWcolor(); } @@ -191,11 +187,12 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(size, size); } - private void drawMarkers(Canvas canvas, float radius, float markerSize, ArrayList markers, boolean brightness) { + private void drawWheelMarkers(Canvas canvas, float radius, float markerSize, + ArrayList markers, boolean brightness) { double angle; paint.setAntiAlias(true); - paint.setStrokeWidth(markerSize / 4); + paint.setStrokeWidth(markerSize / 5); if (markers == null) { return; @@ -234,51 +231,64 @@ private void drawMarkers(Canvas canvas, float radius, float markerSize, ArrayLis } private void drawCirclePointer(Canvas canvas, double angle, - float wheelRadius, float wheelWidth) { + float wheelRadius, int color, PointF center) { + + center.x = (float) Math.cos(angle) * wheelRadius; + center.y = (float) Math.sin(angle) * wheelRadius; + float lw = (float) pointerHeight * 0.05f; - float x = (float)Math.cos(angle) * wheelRadius; - float y = (float)Math.sin(angle) * wheelRadius; + paint.setAntiAlias(true); + paint.setStyle(Paint.Style.FILL); + paint.setColor(color); + canvas.drawCircle(center.x, center.y, (float) pointerHeight / 2 - lw / 2, paint); + paint.setStrokeWidth(lw); paint.setStyle(Paint.Style.STROKE); - paint.setColor(Color.BLACK); + paint.setColor(Color.WHITE); - canvas.drawCircle(x, y, wheelWidth * 0.47f, paint); + canvas.drawCircle(center.x, center.y, (float) pointerHeight / 2 - lw * 1.5f, paint); } - @Override - protected void onDraw(Canvas canvas) { + private float trimBrightnessColorAngle(float rad) { + if (rad >= 0 && rad <= 0.4f) { + rad = 0.4f; + } else if (rad >= 2.7 || rad < 0) { + rad = 2.7f; + } + return rad; + } - canvas.translate(centerX, centerY); + private void drawWheel(Canvas canvas) { if (colorWheelVisible) { cwPaint.setShader(cwShader); - rectF.set(-outerWheelRadius, -outerWheelRadius, outerWheelRadius, outerWheelRadius); + rectF.set(-colorWheelRadius, -colorWheelRadius, colorWheelRadius, colorWheelRadius); canvas.drawOval(rectF, cwPaint); if (circleInsteadArrow) { - drawCirclePointer(canvas, outerWheelPointerAngle, - outerWheelRadius, outerWheelWidth); + drawCirclePointer(canvas, colorWheelPointerAngle, + colorWheelRadius, selectedColor, colorPointerCenter); } else { - drawArrow(canvas, outerTop, - outerWheelPointerAngle, - outerWheelRadius, - outerWheelWidth, - -(outerArrowHeight_a / 4), - outerArrowHeight_a, - outerArrowHeight_b, + drawArrow(canvas, colorPointerCenter, + colorWheelPointerAngle, + colorWheelRadius, + colorWheelWidth, + -(arrowHeight_a / 4), + arrowHeight_a, + arrowHeight_b, selectedColor, - outerArrowPath, - outerArrowPaint); + colorArrowPath, + colorArrowPaint); } - - drawMarkers(canvas, outerWheelRadius, - outerWheelWidth / 6, ColorMarkers, false); + drawWheelMarkers(canvas, colorWheelRadius, + colorWheelWidth / (circleInsteadArrow ? 9 : 6), + ColorMarkers, false); } bwPaint.setShader(bwShader); - rectF.set(-innerWheelRadius, -innerWheelRadius, innerWheelRadius, innerWheelRadius); + rectF.set(-brightnessWheelRadius, -brightnessWheelRadius, brightnessWheelRadius, brightnessWheelRadius); canvas.drawOval(rectF, bwPaint); int negative; @@ -286,51 +296,65 @@ protected void onDraw(Canvas canvas) { if (colorWheelVisible) { negative = -1; - arrowOffset = innerArrowHeight_a / 4 - innerWheelWidth; + arrowOffset = arrowHeight_a / 4 - brightnessWheelWidth; } else { negative = 1; - arrowOffset = -(innerArrowHeight_a / 4); + arrowOffset = -(arrowHeight_a / 4); } + if (circleInsteadArrow) { - drawCirclePointer(canvas, innerWheelPointerAngle, - innerWheelRadius, innerWheelWidth); + float angle = (float) (brightnessWheelPointerAngle - m90d); + if (!colorfulBrightnessWheel || !colorWheelVisible) { + angle = trimBrightnessColorAngle(angle); + } + drawCirclePointer(canvas, brightnessWheelPointerAngle, + brightnessWheelRadius, calculateColor(angle, BW), brightnessPointerCenter); } else { - drawArrow(canvas, innerTop, - innerWheelPointerAngle, - innerWheelRadius, - innerWheelWidth, + drawArrow(canvas, brightnessPointerCenter, + brightnessWheelPointerAngle, + brightnessWheelRadius, + brightnessWheelWidth, arrowOffset, - negative * innerArrowHeight_a, - negative * innerArrowHeight_b, - calculateColor((float) (innerWheelPointerAngle - m90d), BW), - innerArrowPath, - innerArrowPaint); + negative * arrowHeight_a, + negative * arrowHeight_b, + calculateColor((float) (brightnessWheelPointerAngle - m90d), BW), + brightnessArrowPath, + brightnessArrowPaint); } - drawMarkers(canvas, innerWheelRadius, innerWheelWidth / 6, + drawWheelMarkers(canvas, brightnessWheelRadius, + brightnessWheelWidth / (circleInsteadArrow ? 9 : 6), BrightnessMarkers, true); + } + @Override + protected void onDraw(Canvas canvas) { + canvas.translate(centerX, centerY); + drawWheel(canvas); } - private void drawArrow(Canvas canvas, PointerTop top, double topAngle, + private void drawArrow(Canvas canvas, PointF center, double topAngle, float wheelRadius, float wheelWidth, double arrowOffset, double arrowHeight_a, double arrowHeight_b, int color, Path arrowPath, Paint arrowPaint) { - top.X = Math.cos(topAngle) * (wheelRadius + wheelWidth / 2 + arrowOffset); - top.Y = Math.sin(topAngle) * (wheelRadius + wheelWidth / 2 + arrowOffset); + float hh = (float) (arrowHeight_a + arrowHeight_b) / 2.0f; + double radius = wheelRadius + wheelWidth / 2 + arrowOffset; + float x = (float) (Math.cos(topAngle) * radius); + float y = (float) (Math.sin(topAngle) * radius); - top.Height = Math.abs(arrowHeight_a + arrowHeight_b); + center.x = (float) (Math.cos(topAngle) * (radius + hh)); + center.y = (float) (Math.sin(topAngle) * (radius + hh)); double arrowRad = Math.toRadians(40); - double leftX = top.X + Math.cos(topAngle + arrowRad) * arrowHeight_a; - double leftY = top.Y + Math.sin(topAngle + arrowRad) * arrowHeight_a; + double leftX = x + Math.cos(topAngle + arrowRad) * arrowHeight_a; + double leftY = y + Math.sin(topAngle + arrowRad) * arrowHeight_a; - double rightX = top.X + Math.cos(topAngle - arrowRad) * arrowHeight_a; - double rightY = top.Y + Math.sin(topAngle - arrowRad) * arrowHeight_a; + double rightX = x + Math.cos(topAngle - arrowRad) * arrowHeight_a; + double rightY = y + Math.sin(topAngle - arrowRad) * arrowHeight_a; double backLeftX = leftX + Math.cos(topAngle) * arrowHeight_b; double backLeftY = leftY + Math.sin(topAngle) * arrowHeight_b; @@ -339,11 +363,11 @@ private void drawArrow(Canvas canvas, PointerTop top, double topAngle, double backRightY = rightY + Math.sin(topAngle) * arrowHeight_b; arrowPath.reset(); - arrowPath.moveTo((float) top.X, (float) top.Y); + arrowPath.moveTo(x, y); arrowPath.lineTo((float) leftX, (float) leftY); arrowPath.lineTo((float) backLeftX, (float) backLeftY); - arrowPath.moveTo((float) top.X, (float) top.Y); + arrowPath.moveTo(x, y); arrowPath.lineTo((float) rightX, (float) rightY); arrowPath.lineTo((float) backRightX, (float) backRightY); arrowPath.lineTo((float) backLeftX, (float) backLeftY); @@ -374,47 +398,44 @@ private void setBWcolor() { private void _onSizeChanged() { - float arrowHeight = 0; float wheelWidth = this.getWidth() > this.getHeight() ? this.getHeight() : this.getWidth(); if (circleInsteadArrow) { wheelWidth /= 7.0f; + pointerHeight = wheelWidth; } else { wheelWidth /= 10.0f; - arrowHeight = wheelWidth * 0.9f; + pointerHeight = wheelWidth * 0.9f; } - outerWheelWidth = wheelWidth / 2; - outerArrowHeight_a = arrowHeight; - outerArrowHeight_b = outerArrowHeight_a * 0.6; - outerArrowHeight_a -= outerArrowHeight_b; + colorWheelWidth = wheelWidth / 2f; + arrowHeight_a = pointerHeight; + arrowHeight_b = arrowHeight_a * 0.6; + arrowHeight_a -= arrowHeight_b; - innerWheelWidth = outerWheelWidth; - innerArrowHeight_a = arrowHeight; - innerArrowHeight_b = innerArrowHeight_a * 0.6; - innerArrowHeight_a -= innerArrowHeight_b; + brightnessWheelWidth = colorWheelWidth; - centerX = this.getWidth() / 2; - centerY = this.getHeight() / 2; + centerX = this.getWidth() / 2f; + centerY = this.getHeight() / 2f; if (colorWheelVisible && !circleInsteadArrow) { - outerWheelWidth = wheelWidth / 2; + colorWheelWidth = wheelWidth / 2f; } else { - outerWheelWidth = wheelWidth; + colorWheelWidth = wheelWidth; } - innerWheelWidth = outerWheelWidth; - int margin = circleInsteadArrow ? 0 : (int) (arrowHeight); - outerWheelRadius = Math.min(centerX, centerY) - outerWheelWidth / 2 - margin; + brightnessWheelWidth = colorWheelWidth; + int margin = circleInsteadArrow ? 0 : (int) (pointerHeight); + colorWheelRadius = Math.min(centerX, centerY) - colorWheelWidth / 2 - margin; if (colorWheelVisible) { - innerWheelRadius = outerWheelRadius - innerWheelWidth; + brightnessWheelRadius = colorWheelRadius - brightnessWheelWidth; } else { - innerWheelRadius = outerWheelRadius; + brightnessWheelRadius = colorWheelRadius; } - cwPaint.setStrokeWidth(outerWheelWidth); - bwPaint.setStrokeWidth(innerWheelWidth); + cwPaint.setStrokeWidth(colorWheelWidth); + bwPaint.setStrokeWidth(brightnessWheelWidth); } @Override @@ -424,40 +445,9 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); } - private double calculateAngle(double pointerAngle, double inRads) { - - double delta; - - if (Math.abs(lastTouchedAngle - inRads) > Math.PI) { - - delta = 2 * Math.PI - Math.abs(lastTouchedAngle) - Math.abs(inRads); - - if (lastTouchedAngle > 0 && inRads < 0) { - delta *= -1; - } - - } else { - delta = lastTouchedAngle - inRads; - } - - double result = (pointerAngle - delta); - - if (Math.abs(result) > Math.PI) { - - result = Math.PI - (Math.abs(result) % Math.PI); - - if (lastTouchedAngle < inRads) { - result *= -1; - } - } - - - return result; - } - private void calculateBrightness() { - double d = Math.toDegrees(innerWheelPointerAngle) + 90; + double d = Math.toDegrees(brightnessWheelPointerAngle) + 90; if (d < 0) d = d + 360; @@ -468,13 +458,21 @@ private void calculateBrightness() { selectedBrightness = (d / 360) * 100; } + private boolean touchOverPointer(PointF touchPoint, PointF pointerCenter, + double pointerHeight) { + return Math.sqrt(Math.pow(pointerCenter.x - touchPoint.x, 2) + + Math.pow(pointerCenter.y - touchPoint.y, 2)) <= pointerHeight / 2; + } + + public double pointToRadians(PointF point) { + return Math.atan2(point.y, point.x); + } + @Override public boolean onTouchEvent(MotionEvent event) { - float x = event.getX() - centerX; - float y = event.getY() - centerY; - - double inRads = (float) Math.atan2(y, x); + PointF touchPoint = new PointF(event.getX() - centerX, event.getY() - centerY); + double inRads = pointToRadians(touchPoint); int action = event.getAction(); @@ -492,34 +490,16 @@ public boolean onTouchEvent(MotionEvent event) { case MotionEvent.ACTION_DOWN: - double sqrt = Math.sqrt(x * x + y * y); - - - if (colorWheelVisible - && Math.abs(outerTop.X - x) <= outerTop.Height - && Math.abs(outerTop.Y - y) <= outerTop.Height - && sqrt >= outerWheelRadius - outerWheelWidth / 2 - && sqrt <= outerWheelRadius + (outerArrowHeight_a + outerArrowHeight_b) * 2) { - + if (colorWheelVisible && touchOverPointer(touchPoint, colorPointerCenter, pointerHeight)) { colorWheelMove = true; brightnessWheelMove = false; - - } else if (Math.abs(innerTop.X - x) <= innerTop.Height - && Math.abs(innerTop.Y - y) <= innerTop.Height - && ((colorWheelVisible - && sqrt <= innerWheelRadius - innerWheelWidth / 2 - && sqrt >= innerWheelRadius - (innerArrowHeight_a + innerArrowHeight_b) * 2) - || (!colorWheelVisible - && sqrt <= innerWheelRadius + (innerArrowHeight_a + innerArrowHeight_b) * 2 - && sqrt >= innerWheelRadius - innerWheelWidth / 2))) { - + touchAngleDiff = pointToRadians(colorPointerCenter) - inRads; + } else if (touchOverPointer(touchPoint, brightnessPointerCenter, pointerHeight)) { colorWheelMove = false; brightnessWheelMove = true; + touchAngleDiff = pointToRadians(brightnessPointerCenter) - inRads; } - - lastTouchedAngle = inRads; - if (!isMoving()) return super.onTouchEvent(event); @@ -529,9 +509,9 @@ public boolean onTouchEvent(MotionEvent event) { if (colorWheelMove) { - outerWheelPointerAngle = calculateAngle(outerWheelPointerAngle, inRads); + colorWheelPointerAngle = inRads + touchAngleDiff; //calculateAngle(colorWheelPointerAngle, inRads); - int newColor = calculateColor((float) outerWheelPointerAngle, Colors); + int newColor = calculateColor((float) colorWheelPointerAngle, Colors); if (newColor != selectedColor) { @@ -546,21 +526,21 @@ public boolean onTouchEvent(MotionEvent event) { } else if (brightnessWheelMove) { - double newAngle = calculateAngle(innerWheelPointerAngle, inRads); + double newAngle = inRads + touchAngleDiff; if (newAngle >= m160d && newAngle <= m20d) { - if (innerWheelPointerAngle > newAngle) { + if (brightnessWheelPointerAngle > newAngle) { - if (innerWheelPointerAngle >= m90d + if (brightnessWheelPointerAngle >= m90d && newAngle < m90d) { newAngle = m90d; } - } else if (innerWheelPointerAngle < newAngle) { + } else if (brightnessWheelPointerAngle < newAngle) { - if (innerWheelPointerAngle <= m90_01d + if (brightnessWheelPointerAngle <= m90_01d && newAngle > m90_01d) { newAngle = m90_01d; } @@ -569,9 +549,9 @@ public boolean onTouchEvent(MotionEvent event) { } - if (innerWheelPointerAngle != newAngle) { + if (brightnessWheelPointerAngle != newAngle) { - innerWheelPointerAngle = newAngle; + brightnessWheelPointerAngle = newAngle; calculateBrightness(); invalidate(); @@ -583,8 +563,6 @@ public boolean onTouchEvent(MotionEvent event) { } - lastTouchedAngle = inRads; - break; } @@ -606,12 +584,12 @@ public void setColor(int color) { if (selectedColor != color) { selectedColor = color; - outerWheelPointerAngle = colorToAngle(color); + colorWheelPointerAngle = colorToAngle(color); if (color == Color.WHITE) selectedColor = color; else - selectedColor = calculateColor((float) outerWheelPointerAngle, Colors); + selectedColor = calculateColor((float) colorWheelPointerAngle, Colors); invalidate(); } @@ -636,7 +614,7 @@ public double getBrightnessValue() { public void setBrightnessValue(double value) { - innerWheelPointerAngle = brightnessToAngle(value); + brightnessWheelPointerAngle = brightnessToAngle(value); selectedBrightness = value; invalidate(); @@ -671,12 +649,28 @@ public boolean isMoving() { return colorWheelMove || brightnessWheelMove; } - public ArrayList getColorMarkers() { - return new ArrayList<>(ColorMarkers); + public ArrayList getColorMarkers() { + if (ColorMarkers != null && ColorMarkers.size() > 0) { + ArrayListresult = new ArrayList<>(); + for(Double color: ColorMarkers) { + result.add(color.intValue()); + } + return result; + } + + return null; } - public void setColorMarkers(ArrayList colorMarkers) { - ColorMarkers = colorMarkers == null ? null : new ArrayList<>(colorMarkers); + public void setColorMarkers(ArrayList colorMarkers) { + ColorMarkers = null; + + if (colorMarkers != null && colorMarkers.size() > 0) { + ColorMarkers = new ArrayList<>(); + for(Integer color: colorMarkers) { + ColorMarkers.add(color.doubleValue()); + } + } + invalidate(); } @@ -717,10 +711,4 @@ public interface OnColorBrightnessChangeListener { void onChangeFinished(); } - private class PointerTop { - double X; - double Y; - double Height; - } - } From 989a7fc998860046607bf18f5150ba44fa91ed80 Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Sat, 6 Jun 2020 21:30:53 +0200 Subject: [PATCH 04/31] SuplaColorBrightnessPicker - slider added --- .../android/SuplaColorBrightnessPicker.java | 287 ++++++++++++------ 1 file changed, 198 insertions(+), 89 deletions(-) diff --git a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java index ac2c3f68c..13bbf67a2 100644 --- a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java +++ b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java @@ -13,6 +13,7 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; + import java.util.ArrayList; /* @@ -43,6 +44,7 @@ public class SuplaColorBrightnessPicker extends View { final static private double m90d = Math.toRadians(-90); final static private double m90_01d = Math.toRadians(-90.01); final static private double m20d = Math.toRadians(-20); + final static private double p40d = Math.toRadians(40); private int[] BW = new int[]{ Color.BLACK, Color.WHITE, @@ -74,16 +76,17 @@ public class SuplaColorBrightnessPicker extends View { private Shader cwShader; // color wheel shader private Paint bwPaint; // brightness wheel paint private Shader bwShader; // brightness wheel shader - private Matrix gradientRotationMatrix; private boolean colorWheelVisible; - private boolean colorWheelMove; - private boolean brightnessWheelMove; + private boolean colorPointerMoving; + private boolean brightnessWheelPointerMoving; private boolean colorfulBrightnessWheel; private boolean circleInsteadArrow; - private double touchAngleDiff; + private double touchDiff; private OnColorBrightnessChangeListener mOnChangeListener; private ArrayList ColorMarkers; - private ArrayList BrightnessMarkers; + private ArrayList brightnessMarkers; + private boolean sliderVisible; + private RectF sliderRect; public SuplaColorBrightnessPicker(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); @@ -110,7 +113,6 @@ private void init() { cwPaint = new Paint(Paint.ANTI_ALIAS_FLAG); cwPaint.setStyle(Paint.Style.STROKE); - bwShader = new SweepGradient(0, 0, BW, null); bwPaint = new Paint(Paint.ANTI_ALIAS_FLAG); bwPaint.setStyle(Paint.Style.STROKE); @@ -129,12 +131,8 @@ private void init() { brightnessWheelPointerAngle = Math.toRadians(-90); selectedBrightness = 0; - gradientRotationMatrix = new Matrix(); - gradientRotationMatrix.preRotate(-90); - bwShader.setLocalMatrix(gradientRotationMatrix); - - colorWheelMove = false; - brightnessWheelMove = false; + colorPointerMoving = false; + brightnessWheelPointerMoving = false; colorfulBrightnessWheel = true; circleInsteadArrow = false; @@ -187,17 +185,28 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(size, size); } - private void drawWheelMarkers(Canvas canvas, float radius, float markerSize, - ArrayList markers, boolean brightness) { - - double angle; + private void drawMarker(Canvas canvas, float x, float y, float markerSize) { paint.setAntiAlias(true); paint.setStrokeWidth(markerSize / 5); + paint.setColor(Color.WHITE); + paint.setStyle(Paint.Style.FILL); + + canvas.drawCircle(x, y, markerSize, paint); + + paint.setStyle(Paint.Style.STROKE); + paint.setColor(Color.BLACK); + + canvas.drawCircle(x, y, markerSize, paint); + } + private void drawWheelMarkers(Canvas canvas, float radius, float markerSize, + ArrayList markers, boolean brightness) { if (markers == null) { return; } + double angle; + for (int a = 0; a < markers.size(); a++) { double v = markers.get(a); @@ -213,28 +222,32 @@ private void drawWheelMarkers(Canvas canvas, float radius, float markerSize, angle = colorToAngle((int) v); } - float x = (float) Math.cos(angle) * radius; - float y = (float) Math.sin(angle) * radius; - - paint.setColor(Color.WHITE); - paint.setStyle(Paint.Style.FILL); + drawMarker(canvas, + (float) Math.cos(angle) * radius, + (float) Math.sin(angle) * radius, + markerSize); - canvas.drawCircle(x, y, markerSize, paint); - paint.setStyle(Paint.Style.STROKE); - paint.setColor(Color.BLACK); + } - canvas.drawCircle(x, y, markerSize, paint); + } + private void drawSliderMarkers(Canvas canvas, float markerSize) { + if (brightnessMarkers == null) { + return; } - } + float h = sliderRect.height() - (float) pointerHeight; - private void drawCirclePointer(Canvas canvas, double angle, - float wheelRadius, int color, PointF center) { + for (int a = 0; a < brightnessMarkers.size(); a++) { + drawMarker(canvas, + 0, + h / 2 - h * brightnessMarkers.get(a).floatValue() / 100f, + markerSize); + } + } - center.x = (float) Math.cos(angle) * wheelRadius; - center.y = (float) Math.sin(angle) * wheelRadius; + private void drawCirclePointer(Canvas canvas, int color, PointF center) { float lw = (float) pointerHeight * 0.05f; paint.setAntiAlias(true); @@ -249,6 +262,15 @@ private void drawCirclePointer(Canvas canvas, double angle, canvas.drawCircle(center.x, center.y, (float) pointerHeight / 2 - lw * 1.5f, paint); } + private void drawCirclePointer(Canvas canvas, double angle, + float wheelRadius, int color, PointF center) { + + center.x = (float) Math.cos(angle) * wheelRadius; + center.y = (float) Math.sin(angle) * wheelRadius; + + drawCirclePointer(canvas, color, center); + } + private float trimBrightnessColorAngle(float rad) { if (rad >= 0 && rad <= 0.4f) { rad = 0.4f; @@ -287,6 +309,7 @@ private void drawWheel(Canvas canvas) { ColorMarkers, false); } + bwPaint.setStyle(Paint.Style.STROKE); bwPaint.setShader(bwShader); rectF.set(-brightnessWheelRadius, -brightnessWheelRadius, brightnessWheelRadius, brightnessWheelRadius); canvas.drawOval(rectF, bwPaint); @@ -325,14 +348,61 @@ private void drawWheel(Canvas canvas) { drawWheelMarkers(canvas, brightnessWheelRadius, brightnessWheelWidth / (circleInsteadArrow ? 9 : 6), - BrightnessMarkers, true); + brightnessMarkers, true); } + private void drawSlider(Canvas canvas) { + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(2); + + float height = getHeight() - (float) pointerHeight / 2; + float x = (float) pointerHeight / -2; + float y = height / -2; + + Path path = new Path(); + sliderRect = new RectF(x, y, x + (float) pointerHeight, y + height); + path.addRoundRect(sliderRect, 90, 90, Path.Direction.CW); + canvas.clipPath(path); + + float[] hsv = new float[3]; + Color.colorToHSV(Color.WHITE, hsv); + + for (float a = 0; a < height; a++) { + hsv[2] = 1 - 1 * (a * 100f / height) / 100f; + paint.setColor(Color.HSVToColor(hsv)); + canvas.drawLine(x, y + a, x + (float) pointerHeight, y + a, paint); + } + + + height -= pointerHeight; + + brightnessPointerCenter.x = 0; + brightnessPointerCenter.y = height / 2 - height * (float) selectedBrightness / 100f; + + float percent = (float) selectedBrightness; + + if (percent > 85f) { + percent = 85f; + } else if (percent < 15f) { + percent = 15f; + } + + hsv[2] = 1 * percent / 100f; + drawCirclePointer(canvas, Color.HSVToColor(hsv), brightnessPointerCenter); + drawSliderMarkers(canvas, (float) pointerHeight / 10f); + } + @Override protected void onDraw(Canvas canvas) { canvas.translate(centerX, centerY); - drawWheel(canvas); + if (sliderVisible) { + drawSlider(canvas); + } else { + drawWheel(canvas); + } } private void drawArrow(Canvas canvas, PointF center, double topAngle, @@ -385,30 +455,36 @@ private void drawArrow(Canvas canvas, PointF center, double topAngle, } private void setBWcolor() { - int color = colorWheelVisible && colorfulBrightnessWheel ? selectedColor : Color.WHITE; + int color = colorWheelVisible + && colorfulBrightnessWheel && !sliderVisible ? selectedColor : Color.WHITE; if (BW[1] != color) { BW[1] = color; BW[2] = color; bwShader = new SweepGradient(0, 0, BW, null); + Matrix gradientRotationMatrix = new Matrix(); + gradientRotationMatrix.preRotate(-90); bwShader.setLocalMatrix(gradientRotationMatrix); + } } private void _onSizeChanged() { - float wheelWidth = this.getWidth() > this.getHeight() ? this.getHeight() : this.getWidth(); + float w = this.getWidth() > this.getHeight() ? this.getHeight() : this.getWidth(); - if (circleInsteadArrow) { - wheelWidth /= 7.0f; - pointerHeight = wheelWidth; + if (sliderVisible) { + pointerHeight = w / 6.5f; + } else if (circleInsteadArrow) { + w /= 7.0f; + pointerHeight = w; } else { - wheelWidth /= 10.0f; - pointerHeight = wheelWidth * 0.9f; + w /= 10.0f; + pointerHeight = w * 0.9f; } - colorWheelWidth = wheelWidth / 2f; + colorWheelWidth = w / 2f; arrowHeight_a = pointerHeight; arrowHeight_b = arrowHeight_a * 0.6; arrowHeight_a -= arrowHeight_b; @@ -419,9 +495,9 @@ private void _onSizeChanged() { centerY = this.getHeight() / 2f; if (colorWheelVisible && !circleInsteadArrow) { - colorWheelWidth = wheelWidth / 2f; + colorWheelWidth = w / 2f; } else { - colorWheelWidth = wheelWidth; + colorWheelWidth = w; } brightnessWheelWidth = colorWheelWidth; @@ -445,19 +521,6 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); } - private void calculateBrightness() { - - double d = Math.toDegrees(brightnessWheelPointerAngle) + 90; - - if (d < 0) - d = d + 360; - - if (d >= 359.99) - d = 360; - - selectedBrightness = (d / 360) * 100; - } - private boolean touchOverPointer(PointF touchPoint, PointF pointerCenter, double pointerHeight) { return Math.sqrt(Math.pow(pointerCenter.x - touchPoint.x, 2) @@ -472,7 +535,7 @@ public double pointToRadians(PointF point) { public boolean onTouchEvent(MotionEvent event) { PointF touchPoint = new PointF(event.getX() - centerX, event.getY() - centerY); - double inRads = pointToRadians(touchPoint); + double touchAngle = pointToRadians(touchPoint); int action = event.getAction(); @@ -480,8 +543,8 @@ public boolean onTouchEvent(MotionEvent event) { case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: - colorWheelMove = false; - brightnessWheelMove = false; + colorPointerMoving = false; + brightnessWheelPointerMoving = false; if (mOnChangeListener != null) mOnChangeListener.onChangeFinished(); @@ -490,26 +553,52 @@ public boolean onTouchEvent(MotionEvent event) { case MotionEvent.ACTION_DOWN: - if (colorWheelVisible && touchOverPointer(touchPoint, colorPointerCenter, pointerHeight)) { - colorWheelMove = true; - brightnessWheelMove = false; - touchAngleDiff = pointToRadians(colorPointerCenter) - inRads; + colorPointerMoving = false; + brightnessWheelPointerMoving = false; + + if (!sliderVisible + && colorWheelVisible + && touchOverPointer(touchPoint, colorPointerCenter, pointerHeight)) { + colorPointerMoving = true; + touchDiff = pointToRadians(colorPointerCenter) - touchAngle; } else if (touchOverPointer(touchPoint, brightnessPointerCenter, pointerHeight)) { - colorWheelMove = false; - brightnessWheelMove = true; - touchAngleDiff = pointToRadians(brightnessPointerCenter) - inRads; + brightnessWheelPointerMoving = true; + if (sliderVisible) { + touchDiff = brightnessPointerCenter.y - touchPoint.y; + } else { + touchDiff = pointToRadians(brightnessPointerCenter) - touchAngle; + } } - if (!isMoving()) + if (!isMoving()) { return super.onTouchEvent(event); - + } break; case MotionEvent.ACTION_MOVE: - if (colorWheelMove) { + if (sliderVisible) { + if (brightnessWheelPointerMoving) { + float h = sliderRect.height() - (float) pointerHeight; + float brightness = 100 - ((h / 2) + (float) touchDiff + touchPoint.y) * 100 / h; + + if (brightness > 100) { + brightness = 100; + } else if (brightness < 0) { + brightness = 0; + } + + if (selectedBrightness != brightness) { + setBrightnessValue(brightness); + + if (mOnChangeListener != null) + mOnChangeListener.onBrightnessChanged(this, selectedBrightness); + } + + } + } else if (colorPointerMoving) { - colorWheelPointerAngle = inRads + touchAngleDiff; //calculateAngle(colorWheelPointerAngle, inRads); + colorWheelPointerAngle = touchAngle + touchDiff; int newColor = calculateColor((float) colorWheelPointerAngle, Colors); @@ -524,43 +613,45 @@ public boolean onTouchEvent(MotionEvent event) { } - } else if (brightnessWheelMove) { + } else if (brightnessWheelPointerMoving) { - double newAngle = inRads + touchAngleDiff; + double newAngle = touchAngle + touchDiff; + if (brightnessWheelPointerAngle >= m160d + && brightnessWheelPointerAngle <= m20d) { - if (newAngle >= m160d - && newAngle <= m20d) { + if (Math.abs(brightnessWheelPointerAngle - newAngle) > p40d) { + newAngle = brightnessWheelPointerAngle; + } if (brightnessWheelPointerAngle > newAngle) { - if (brightnessWheelPointerAngle >= m90d && newAngle < m90d) { newAngle = m90d; } - } else if (brightnessWheelPointerAngle < newAngle) { - if (brightnessWheelPointerAngle <= m90_01d && newAngle > m90_01d) { newAngle = m90_01d; } - } - } if (brightnessWheelPointerAngle != newAngle) { + double d = Math.toDegrees(newAngle) + 90; - brightnessWheelPointerAngle = newAngle; + if (d < 0) { + d += 360; + } - calculateBrightness(); - invalidate(); + if (d >= 359.99) { + d = 360; + } + + setBrightnessValue((d / 360) * 100); if (mOnChangeListener != null) mOnChangeListener.onBrightnessChanged(this, selectedBrightness); - } - } break; @@ -614,6 +705,12 @@ public double getBrightnessValue() { public void setBrightnessValue(double value) { + if (value > 100) { + value = 100; + } else if (value < 0) { + value = 0; + } + brightnessWheelPointerAngle = brightnessToAngle(value); selectedBrightness = value; invalidate(); @@ -646,13 +743,13 @@ else if (value > 100) } public boolean isMoving() { - return colorWheelMove || brightnessWheelMove; + return colorPointerMoving || brightnessWheelPointerMoving; } public ArrayList getColorMarkers() { if (ColorMarkers != null && ColorMarkers.size() > 0) { - ArrayListresult = new ArrayList<>(); - for(Double color: ColorMarkers) { + ArrayList result = new ArrayList<>(); + for (Double color : ColorMarkers) { result.add(color.intValue()); } return result; @@ -666,7 +763,7 @@ public void setColorMarkers(ArrayList colorMarkers) { if (colorMarkers != null && colorMarkers.size() > 0) { ColorMarkers = new ArrayList<>(); - for(Integer color: colorMarkers) { + for (Integer color : colorMarkers) { ColorMarkers.add(color.doubleValue()); } } @@ -675,11 +772,12 @@ public void setColorMarkers(ArrayList colorMarkers) { } public ArrayList getBrightnessMarkers() { - return new ArrayList<>(BrightnessMarkers); + return new ArrayList<>(brightnessMarkers); } public void setBrightnessMarkers(ArrayList brightnessMarkers) { - BrightnessMarkers = brightnessMarkers == null ? null : new ArrayList<>(brightnessMarkers); + this.brightnessMarkers = brightnessMarkers == null ? + null : new ArrayList<>(brightnessMarkers); invalidate(); } @@ -703,6 +801,17 @@ public void setCircleInsteadArrow(boolean circleInsteadArrow) { invalidate(); } + public boolean isSliderVisible() { + return sliderVisible; + } + + public void setSliderVisible(boolean sliderVisible) { + this.sliderVisible = sliderVisible; + _onSizeChanged(); + setBWcolor(); + invalidate(); + } + public interface OnColorBrightnessChangeListener { void onColorChanged(SuplaColorBrightnessPicker scbPicker, int color); From 7e418378fa787a88c0f008663d42cb452da4626b Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Sat, 6 Jun 2020 22:32:35 +0200 Subject: [PATCH 05/31] SuplaColorBrightnessPicker - power button implementation added --- .../android/SuplaColorBrightnessPicker.java | 101 +++++++++++++++++- 1 file changed, 98 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java index 13bbf67a2..264d1fcf9 100644 --- a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java +++ b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java @@ -87,6 +87,13 @@ public class SuplaColorBrightnessPicker extends View { private ArrayList brightnessMarkers; private boolean sliderVisible; private RectF sliderRect; + private boolean powerButtonVisible; + private boolean powerButtonEnabled; + private boolean powerButtonOn; + private int powerButtonColorOn; + private int powerButtonColorOff; + private float powerButtonRadius; + private boolean powerButtonTouched; public SuplaColorBrightnessPicker(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); @@ -135,6 +142,10 @@ private void init() { brightnessWheelPointerMoving = false; colorfulBrightnessWheel = true; circleInsteadArrow = false; + powerButtonColorOn = Color.parseColor("#f7f0dc"); + powerButtonColorOff = Color.parseColor("#404040"); + + powerButtonEnabled = true; setBWcolor(); } @@ -280,6 +291,27 @@ private float trimBrightnessColorAngle(float rad) { return rad; } + private void drawPowerButton(Canvas canvas, float wheelRadius) { + powerButtonRadius = wheelRadius * 0.3f; + + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setStyle(Paint.Style.STROKE); + paint.setColor(powerButtonOn ? powerButtonColorOn : powerButtonColorOff); + paint.setStrokeWidth(powerButtonRadius * 0.2f); + + Path path = new Path(); + paint.setStrokeCap(Paint.Cap.ROUND); + RectF rect = new RectF(-powerButtonRadius, -powerButtonRadius, + powerButtonRadius, powerButtonRadius); + path.addArc(rect, -60f, 300f); + path.moveTo(0f, powerButtonRadius * -1f - powerButtonRadius * 0.15f); + path.lineTo(0f, powerButtonRadius * -1f + powerButtonRadius * 0.6f); + + canvas.drawPath(path, paint); + + } + private void drawWheel(Canvas canvas) { if (colorWheelVisible) { @@ -350,6 +382,9 @@ private void drawWheel(Canvas canvas) { brightnessWheelWidth / (circleInsteadArrow ? 9 : 6), brightnessMarkers, true); + if (powerButtonVisible) { + drawPowerButton(canvas, brightnessWheelRadius * 0.8f); + } } private void drawSlider(Canvas canvas) { @@ -546,15 +581,24 @@ public boolean onTouchEvent(MotionEvent event) { colorPointerMoving = false; brightnessWheelPointerMoving = false; + if (powerButtonTouched) { + setPowerButtonOn(!isPowerButtonOn()); + + if (mOnChangeListener != null) + mOnChangeListener.onPowerButtonClick(this); + } + if (mOnChangeListener != null) - mOnChangeListener.onChangeFinished(); + mOnChangeListener.onChangeFinished(this); + powerButtonTouched = false; break; case MotionEvent.ACTION_DOWN: colorPointerMoving = false; brightnessWheelPointerMoving = false; + powerButtonTouched = false; if (!sliderVisible && colorWheelVisible @@ -568,9 +612,14 @@ && touchOverPointer(touchPoint, colorPointerCenter, pointerHeight)) { } else { touchDiff = pointToRadians(brightnessPointerCenter) - touchAngle; } + } else if (powerButtonVisible + && powerButtonEnabled + && touchOverPointer(touchPoint, new PointF(0, 0), + powerButtonRadius * 2.2)) { + powerButtonTouched = true; } - if (!isMoving()) { + if (!isMoving() && !powerButtonTouched) { return super.onTouchEvent(event); } break; @@ -812,12 +861,58 @@ public void setSliderVisible(boolean sliderVisible) { invalidate(); } + public boolean isPowerButtonVisible() { + return powerButtonVisible; + } + + public void setPowerButtonVisible(boolean powerButtonVisible) { + this.powerButtonVisible = powerButtonVisible; + invalidate(); + } + + public boolean isPowerButtonEnabled() { + return powerButtonEnabled; + } + + public void setPowerButtonEnabled(boolean powerButtonEnabled) { + this.powerButtonEnabled = powerButtonEnabled; + } + + public boolean isPowerButtonOn() { + return powerButtonOn; + } + + public void setPowerButtonOn(boolean powerButtonOn) { + this.powerButtonOn = powerButtonOn; + invalidate(); + } + + public int getPowerButtonColorOn() { + return powerButtonColorOn; + } + + public void setPowerButtonColorOn(int powerButtonColorOn) { + this.powerButtonColorOn = powerButtonColorOn; + invalidate(); + } + + public int getPowerButtonColorOff() { + return powerButtonColorOff; + } + + public void setPowerButtonColorOff(int powerButtonColorOff) { + this.powerButtonColorOff = powerButtonColorOff; + invalidate(); + } + public interface OnColorBrightnessChangeListener { void onColorChanged(SuplaColorBrightnessPicker scbPicker, int color); void onBrightnessChanged(SuplaColorBrightnessPicker scbPicker, double brightness); - void onChangeFinished(); + void onChangeFinished(SuplaColorBrightnessPicker scbPicker); + + void onPowerButtonClick(SuplaColorBrightnessPicker scbPicker); } } From 18bb88bf13dafb3a54fa30064e2295bf8520ad9b Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Sun, 7 Jun 2020 17:32:18 +0200 Subject: [PATCH 06/31] Color as Double. "get" replaced with "is" --- .../android/SuplaColorBrightnessPicker.java | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java index 264d1fcf9..75067da4d 100644 --- a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java +++ b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java @@ -83,7 +83,7 @@ public class SuplaColorBrightnessPicker extends View { private boolean circleInsteadArrow; private double touchDiff; private OnColorBrightnessChangeListener mOnChangeListener; - private ArrayList ColorMarkers; + private ArrayList colorMarkers; private ArrayList brightnessMarkers; private boolean sliderVisible; private RectF sliderRect; @@ -338,7 +338,7 @@ private void drawWheel(Canvas canvas) { drawWheelMarkers(canvas, colorWheelRadius, colorWheelWidth / (circleInsteadArrow ? 9 : 6), - ColorMarkers, false); + colorMarkers, false); } bwPaint.setStyle(Paint.Style.STROKE); @@ -735,7 +735,7 @@ public void setColor(int color) { } } - public boolean getColorWheelVisible() { + public boolean isColorWheelVisible() { return colorWheelVisible; } @@ -795,27 +795,13 @@ public boolean isMoving() { return colorPointerMoving || brightnessWheelPointerMoving; } - public ArrayList getColorMarkers() { - if (ColorMarkers != null && ColorMarkers.size() > 0) { - ArrayList result = new ArrayList<>(); - for (Double color : ColorMarkers) { - result.add(color.intValue()); - } - return result; - } - - return null; + public ArrayList getColorMarkers() { + return new ArrayList<>(colorMarkers); } - public void setColorMarkers(ArrayList colorMarkers) { - ColorMarkers = null; - - if (colorMarkers != null && colorMarkers.size() > 0) { - ColorMarkers = new ArrayList<>(); - for (Integer color : colorMarkers) { - ColorMarkers.add(color.doubleValue()); - } - } + public void setColorMarkers(ArrayList colorMarkers) { + this.colorMarkers = colorMarkers == null ? + null : new ArrayList<>(colorMarkers); invalidate(); } From 1fe7dc847165f86e6817f7f6f888f3614727f3e0 Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Sun, 7 Jun 2020 17:33:13 +0200 Subject: [PATCH 07/31] rgbPicker renamed to cbPicker. rgb_wheel_width, rgb_wheel_arrow_height removed --- app/src/main/res/layout/detail_rgb.xml | 2 +- app/src/main/res/values-sw600dp/dimens.xml | 2 -- app/src/main/res/values/dimens.xml | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/main/res/layout/detail_rgb.xml b/app/src/main/res/layout/detail_rgb.xml index be0af4d42..1533de884 100644 --- a/app/src/main/res/layout/detail_rgb.xml +++ b/app/src/main/res/layout/detail_rgb.xml @@ -31,7 +31,7 @@ android:layout_alignParentTop="false"> 100dp 250dp 15dp - 60dp - 55dp 150dp 30dp 40dp diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 47105622d..284f3b431 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -27,8 +27,6 @@ 50dp 150dp 15dp - 35dp - 30dp 80dp 15dp 25dp From 8f6eeb1aeb910a93fdb5569a814f453ea5aaab42 Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Sun, 7 Jun 2020 17:34:28 +0200 Subject: [PATCH 08/31] ChannelDetailRGB has been adapted to the new version of the picker --- .../org/supla/android/ChannelDetailRGB.java | 110 +++++++++--------- 1 file changed, 53 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/org/supla/android/ChannelDetailRGB.java b/app/src/main/java/org/supla/android/ChannelDetailRGB.java index 084823cda..314952446 100644 --- a/app/src/main/java/org/supla/android/ChannelDetailRGB.java +++ b/app/src/main/java/org/supla/android/ChannelDetailRGB.java @@ -47,11 +47,13 @@ of the License, or (at your option) any later version. import java.util.Timer; import java.util.TimerTask; -public class ChannelDetailRGB extends DetailLayout implements View.OnClickListener, SuplaColorBrightnessPicker.OnColorBrightnessChangeListener, SuplaColorListPicker.OnColorListTouchListener { +public class ChannelDetailRGB extends DetailLayout implements View.OnClickListener, + SuplaColorBrightnessPicker.OnColorBrightnessChangeListener, + SuplaColorListPicker.OnColorListTouchListener { final static private long MIN_REMOTE_UPDATE_PERIOD = 250; final static private long MIN_UPDATE_DELAY = 2000; - private SuplaColorBrightnessPicker rgbPicker; + private SuplaColorBrightnessPicker cbPicker; private SuplaColorListPicker clPicker; private Button tabRGB; private Button tabDimmer; @@ -108,12 +110,8 @@ protected void init() { clPicker.addItem(); clPicker.setOnTouchListener(this); - rgbPicker = findViewById(R.id.rgbPicker); - rgbPicker.setPercentVisible(false); - rgbPicker.setWheelWidth(r.getDimensionPixelSize(R.dimen.rgb_wheel_width)); - rgbPicker.setArrowHeight(r.getDimensionPixelSize(R.dimen.rgb_wheel_arrow_height)); - - rgbPicker.setOnChangeListener(this); + cbPicker = findViewById(R.id.cbPicker); + cbPicker.setOnChangeListener(this); tabRGB = findViewById(R.id.rgbTabBtn_RGB); tabDimmer = findViewById(R.id.rgbTabBtn_Dimmer); @@ -149,9 +147,7 @@ protected void init() { private void showRGB() { - - rgbPicker.setColorWheelVisible(true); - rgbPicker.setColorBrightnessWheelVisible(true); + cbPicker.setColorWheelVisible(true); clPicker.setVisibility(View.VISIBLE); channelDataToViews(); @@ -159,7 +155,7 @@ private void showRGB() { private void showDimmer() { - rgbPicker.setBWBrightnessWheelVisible(true); + cbPicker.setColorWheelVisible(false); clPicker.setVisibility(View.GONE); channelDataToViews(); @@ -217,8 +213,7 @@ public void setData(ChannelBase channel) { break; case SuplaConst.SUPLA_CHANNELFNC_DIMMERANDRGBLIGHTING: - rgbPicker.setColorWheelVisible(true); - rgbPicker.setColorBrightnessWheelVisible(true); + cbPicker.setColorWheelVisible(true); onClick(tabRGB); tabs.setVisibility(View.VISIBLE); @@ -233,8 +228,8 @@ public void setData(ChannelBase channel) { private void channelDataToViews() { int id = 0; - rgbPicker.setColorMarkers(null); - rgbPicker.setBrightnessMarkers(null); + cbPicker.setColorMarkers(null); + cbPicker.setBrightnessMarkers(null); if (isGroup()) { ChannelGroup cgroup = (ChannelGroup) getChannelFromDatabase(); @@ -248,34 +243,30 @@ private void channelDataToViews() { ArrayList markers; - if (rgbPicker.getColorBrightnessWheelVisible() - || rgbPicker.getBWBrightnessWheelVisible()) { + markers = cbPicker.isColorWheelVisible() ? cgroup.getColorBrightness() + : cgroup.getBrightness(); - markers = rgbPicker.getColorBrightnessWheelVisible() ? cgroup.getColorBrightness() - : cgroup.getBrightness(); - - if (markers != null) { - if (markers.size() == 1) { - if (markers.get(0).intValue() != (int) rgbPicker.getBrightnessValue()) { - rgbPicker.setBrightnessValue(markers.get(0)); - } - } else { - rgbPicker.setBrightnessMarkers(markers); + if (markers != null) { + if (markers.size() == 1) { + if (markers.get(0).intValue() != (int) cbPicker.getBrightnessValue()) { + cbPicker.setBrightnessValue(markers.get(0)); } + } else { + cbPicker.setBrightnessMarkers(markers); } } - if (rgbPicker.getColorWheelVisible()) { + if (cbPicker.isColorWheelVisible()) { markers = cgroup.getColors(); if (markers != null) { if (markers.size() == 1) { - if (markers.get(0).intValue() != rgbPicker.getColor()) { - rgbPicker.setColor(markers.get(0).intValue()); + if (markers.get(0).intValue() != cbPicker.getColor()) { + cbPicker.setColor(markers.get(0).intValue()); } } else { - rgbPicker.setColorMarkers(markers); + cbPicker.setColorMarkers(markers); } } } @@ -290,16 +281,17 @@ private void channelDataToViews() { stateImage.setVisibility(View.VISIBLE); tvStateCaption.setVisibility(View.VISIBLE); - if (rgbPicker.getColorBrightnessWheelVisible() - && (int) rgbPicker.getBrightnessValue() != (int) channel.getColorBrightness()) - rgbPicker.setBrightnessValue(channel.getColorBrightness()); + if (cbPicker.isColorWheelVisible() + && (int) cbPicker.getBrightnessValue() != (int) channel.getColorBrightness()) { + cbPicker.setBrightnessValue(channel.getColorBrightness()); - if (rgbPicker.getBWBrightnessWheelVisible() - && (int) rgbPicker.getBrightnessValue() != (int) channel.getBrightness()) - rgbPicker.setBrightnessValue(channel.getBrightness()); + } else if (!cbPicker.isColorWheelVisible() + && (int) cbPicker.getBrightnessValue() != (int) channel.getBrightness()) { + cbPicker.setBrightnessValue(channel.getBrightness()); + } - if (rgbPicker.getColorWheelVisible()) - rgbPicker.setColor(channel.getColor()); + if (cbPicker.isColorWheelVisible()) + cbPicker.setColor(channel.getColor()); } @@ -340,12 +332,12 @@ private void setBtnBackground(Button btn, int id) { @SuppressLint("SetTextI18n") private void pickerToInfoPanel() { - lastColor = rgbPicker.getColor(); + lastColor = cbPicker.getColor(); - int brightness = (int) rgbPicker.getBrightnessValue(); + int brightness = (int) cbPicker.getBrightnessValue(); stateImage.setImageResource(brightness > 0 ? R.drawable.poweron : R.drawable.poweroff); - if (rgbPicker.getColorWheelVisible()) + if (cbPicker.isColorWheelVisible()) lastColorBrightness = brightness; else lastBrightness = brightness; @@ -423,10 +415,10 @@ public void onClick(View v) { tabRGB.setTextColor(Color.BLACK); tabDimmer.setTextColor(getResources().getColor(R.color.detail_rgb_gb)); } else if (v == stateImage) { - rgbPicker.setBrightnessValue(rgbPicker.getBrightnessValue() > 0 ? 0 : 100); + cbPicker.setBrightnessValue(cbPicker.getBrightnessValue() > 0 ? 0 : 100); pickerToInfoPanel(); sendNewValues(true, true); - onChangeFinished(); + onChangeFinished(cbPicker); } else if (v == btnSettings && vlCalibrationTool != null) { vlCalibrationTool.Show(); @@ -450,6 +442,11 @@ public void onBrightnessChanged(SuplaColorBrightnessPicker scbPicker, double bri sendNewValues(); } + @Override + public void onPowerButtonClick(SuplaColorBrightnessPicker scbPicker) { + + } + private void updateDelayed() { if (delayTimer2 != null) { @@ -458,7 +455,7 @@ private void updateDelayed() { } if (!isDetailVisible() - || rgbPicker.getMoving()) + || cbPicker.isMoving()) return; if (System.currentTimeMillis() - changeFinishedTime >= MIN_UPDATE_DELAY) { @@ -498,8 +495,7 @@ public void run() { } @Override - public void onChangeFinished() { - + public void onChangeFinished(SuplaColorBrightnessPicker scbPicker) { changeFinishedTime = System.currentTimeMillis(); updateDelayed(); } @@ -517,11 +513,11 @@ public void OnChannelDataChanged() { @Override public void onColorTouched(SuplaColorListPicker sclPicker, int color, short percent) { - if (color != Color.TRANSPARENT && rgbPicker.getColorBrightnessWheelVisible()) { - rgbPicker.setColor(color); - rgbPicker.setBrightnessValue(percent); + if (color != Color.TRANSPARENT && cbPicker.isColorWheelVisible()) { + cbPicker.setColor(color); + cbPicker.setBrightnessValue(percent); - onColorChanged(rgbPicker, color); + onColorChanged(cbPicker, color); } } @@ -529,9 +525,9 @@ public void onColorTouched(SuplaColorListPicker sclPicker, int color, short perc @Override public void onEdit(SuplaColorListPicker sclPicker, int idx) { - if (idx > 0 && rgbPicker.getColorBrightnessWheelVisible()) { - sclPicker.setItemColor(idx, rgbPicker.getColor()); - sclPicker.setItemPercent(idx, (short) rgbPicker.getBrightnessValue()); + if (idx > 0 && cbPicker.isColorWheelVisible()) { + sclPicker.setItemColor(idx, cbPicker.getColor()); + sclPicker.setItemPercent(idx, (short) cbPicker.getBrightnessValue()); if (getRemoteId() != 0) { @@ -539,8 +535,8 @@ public void onEdit(SuplaColorListPicker sclPicker, int idx) { cli.setRemoteId(getRemoteId()); cli.setGroup(isGroup()); cli.setIdx(idx); - cli.setColor(rgbPicker.getColor()); - cli.setBrightness((short) rgbPicker.getBrightnessValue()); + cli.setColor(cbPicker.getColor()); + cli.setBrightness((short) cbPicker.getBrightnessValue()); DBH.updateColorListItemValue(cli); } From ed440eeca7470d48829250089d77b823c763fae8 Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Sun, 7 Jun 2020 17:42:45 +0200 Subject: [PATCH 09/31] build-tools change to 29.0.3 --- .travis.yml | 2 +- app/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2475bcb7f..6ead9d428 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,6 @@ notifications: android: components: - - build-tools-28.0.3 + - build-tools-29.0.3 - android-28 - extra diff --git a/app/build.gradle b/app/build.gradle index 8e64d4554..5a131651a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -39,7 +39,7 @@ android { lintOptions { checkReleaseBuilds false } testOptions { unitTests.returnDefaultValues = true } - buildToolsVersion '28.0.3' + buildToolsVersion '29.0.3' } repositories { From 725669bed53da836637a5906ed5c847b4dab6f3f Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Sun, 7 Jun 2020 17:48:06 +0200 Subject: [PATCH 10/31] SuplaColorBrightnessPickerTest class skeleton --- .../SuplaColorBrightnessPickerTest.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java diff --git a/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java b/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java new file mode 100644 index 000000000..6058fdc0e --- /dev/null +++ b/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java @@ -0,0 +1,19 @@ +package org.supla.android; + +import junit.framework.TestCase; + +import org.junit.After; +import org.junit.Before; + +import static org.junit.Assert.*; + +public class SuplaColorBrightnessPickerTest extends TestCase { + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } +} \ No newline at end of file From b928145c6f24169bdd330bfd650c2ceea9faf230 Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Sun, 7 Jun 2020 17:50:23 +0200 Subject: [PATCH 11/31] License header --- .../java/org/supla/android/EncryptionTest.java | 17 +++++++++++++++++ .../android/SuplaColorBrightnessPickerTest.java | 17 +++++++++++++++++ .../org/supla/android/SuplaCurtainsTest.java | 17 ++++++++--------- .../android/SuplaRangeCalibrationWheelTest.java | 3 +-- 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/app/src/test/java/org/supla/android/EncryptionTest.java b/app/src/test/java/org/supla/android/EncryptionTest.java index 8357439b2..ab554b7d0 100644 --- a/app/src/test/java/org/supla/android/EncryptionTest.java +++ b/app/src/test/java/org/supla/android/EncryptionTest.java @@ -1,4 +1,21 @@ package org.supla.android; +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ import org.junit.Test; diff --git a/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java b/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java index 6058fdc0e..236832dc9 100644 --- a/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java +++ b/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java @@ -1,4 +1,21 @@ package org.supla.android; +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ import junit.framework.TestCase; diff --git a/app/src/test/java/org/supla/android/SuplaCurtainsTest.java b/app/src/test/java/org/supla/android/SuplaCurtainsTest.java index 5109b8d9a..eac54c7c2 100644 --- a/app/src/test/java/org/supla/android/SuplaCurtainsTest.java +++ b/app/src/test/java/org/supla/android/SuplaCurtainsTest.java @@ -1,13 +1,4 @@ package org.supla.android; - -import android.content.Context; -import android.view.MotionEvent; - -import junit.framework.TestCase; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - /* Copyright (C) AC SOFTWARE SP. Z O.O. @@ -26,6 +17,14 @@ of the License, or (at your option) any later version. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +import android.content.Context; +import android.view.MotionEvent; + +import junit.framework.TestCase; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + public class SuplaCurtainsTest extends TestCase { private SuplaCurtains curtains; diff --git a/app/src/test/java/org/supla/android/SuplaRangeCalibrationWheelTest.java b/app/src/test/java/org/supla/android/SuplaRangeCalibrationWheelTest.java index ee633b017..492804790 100644 --- a/app/src/test/java/org/supla/android/SuplaRangeCalibrationWheelTest.java +++ b/app/src/test/java/org/supla/android/SuplaRangeCalibrationWheelTest.java @@ -1,5 +1,4 @@ package org.supla.android; - /* Copyright (C) AC SOFTWARE SP. Z O.O. @@ -16,7 +15,7 @@ of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ +*/ import junit.framework.TestCase; From 0e4c2f1619b1cb0003b6702d60210e682a53132c Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Sun, 7 Jun 2020 20:13:46 +0200 Subject: [PATCH 12/31] Small modifications --- .../android/SuplaColorBrightnessPicker.java | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java index 75067da4d..3822f313b 100644 --- a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java +++ b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java @@ -129,25 +129,20 @@ private void init() { colorPointerCenter = new PointF(); brightnessPointerCenter = new PointF(); - colorWheelPointerAngle = Math.toRadians(-90); - selectedColor = calculateColor((float) colorWheelPointerAngle, Colors); - brightnessArrowPath = new Path(); brightnessArrowPaint = new Paint(); - brightnessWheelPointerAngle = Math.toRadians(-90); - selectedBrightness = 0; - colorPointerMoving = false; brightnessWheelPointerMoving = false; colorfulBrightnessWheel = true; circleInsteadArrow = false; - powerButtonColorOn = Color.parseColor("#f7f0dc"); - powerButtonColorOff = Color.parseColor("#404040"); + powerButtonColorOn = 0xfff7f0dc; + powerButtonColorOff = 0xff404040; powerButtonEnabled = true; - setBWcolor(); + setBrightnessValue(0); + setColor(0xff00ff00); } private int ave(int s, int d, float p) { @@ -718,19 +713,10 @@ public int getColor() { } public void setColor(int color) { - - if ((color & 0xFFFFFF) == 0xFFFFFF) - color = 0xFFFFFFFF; - if (selectedColor != color) { selectedColor = color; colorWheelPointerAngle = colorToAngle(color); - - if (color == Color.WHITE) - selectedColor = color; - else - selectedColor = calculateColor((float) colorWheelPointerAngle, Colors); - + setBWcolor(); invalidate(); } } @@ -796,7 +782,7 @@ public boolean isMoving() { } public ArrayList getColorMarkers() { - return new ArrayList<>(colorMarkers); + return colorMarkers == null ? null : new ArrayList<>(colorMarkers); } public void setColorMarkers(ArrayList colorMarkers) { @@ -807,7 +793,7 @@ public void setColorMarkers(ArrayList colorMarkers) { } public ArrayList getBrightnessMarkers() { - return new ArrayList<>(brightnessMarkers); + return brightnessMarkers == null ? null : new ArrayList<>(brightnessMarkers); } public void setBrightnessMarkers(ArrayList brightnessMarkers) { From 9338809d513aada497b4687b4e9b907d386dde08 Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Sun, 7 Jun 2020 20:14:08 +0200 Subject: [PATCH 13/31] SuplaColorBrightnessPicker tests --- .../SuplaColorBrightnessPickerTest.java | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java b/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java index 236832dc9..9caafd164 100644 --- a/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java +++ b/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java @@ -17,20 +17,160 @@ of the License, or (at your option) any later version. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +import android.graphics.Color; + import junit.framework.TestCase; import org.junit.After; +import org.junit.Assert; import org.junit.Before; +import java.util.ArrayList; + import static org.junit.Assert.*; public class SuplaColorBrightnessPickerTest extends TestCase { + private SuplaColorBrightnessPicker picker; + @Before public void setUp() throws Exception { + picker = new SuplaColorBrightnessPicker(null); } @After public void tearDown() throws Exception { + picker = null; + } + + public void testColorWheelVisibleSetterAndGetter() { + Assert.assertTrue(picker.isColorWheelVisible()); + picker.setColorWheelVisible(false); + Assert.assertFalse(picker.isColorWheelVisible()); + picker.setColorWheelVisible(true); + Assert.assertTrue(picker.isColorWheelVisible()); + } + + public void testCircleInsteadArrowSetterAndGetter() { + Assert.assertFalse(picker.isCircleInsteadArrow()); + picker.setCircleInsteadArrow(true); + Assert.assertTrue(picker.isCircleInsteadArrow()); + picker.setCircleInsteadArrow(false); + Assert.assertFalse(picker.isCircleInsteadArrow()); + } + + public void testColorfulBrightnessWheelSetterAndGetter() { + Assert.assertTrue(picker.isColorfulBrightnessWheel()); + picker.setColorfulBrightnessWheel(false); + Assert.assertFalse(picker.isColorfulBrightnessWheel()); + picker.setColorfulBrightnessWheel(true); + Assert.assertTrue(picker.isColorfulBrightnessWheel()); + } + + public void testSliderVisibleSetterAndGetter() { + Assert.assertFalse(picker.isSliderVisible()); + picker.setSliderVisible(true); + Assert.assertTrue(picker.isSliderVisible()); + picker.setSliderVisible(false); + Assert.assertFalse(picker.isSliderVisible()); + } + + public void testPowerButtonVisibleSetterAndGetter() { + Assert.assertFalse(picker.isPowerButtonVisible()); + picker.setPowerButtonVisible(true); + Assert.assertTrue(picker.isPowerButtonVisible()); + picker.setPowerButtonVisible(false); + Assert.assertFalse(picker.isPowerButtonVisible()); + } + + public void testPowerButtonEnabledSetterAndGetter() { + Assert.assertTrue(picker.isPowerButtonEnabled()); + picker.setPowerButtonEnabled(false); + Assert.assertFalse(picker.isPowerButtonEnabled()); + picker.setPowerButtonEnabled(true); + Assert.assertTrue(picker.isPowerButtonEnabled()); + } + + public void testPowerButtonOnSetterAndGetter() { + Assert.assertFalse(picker.isPowerButtonOn()); + picker.setPowerButtonOn(true); + Assert.assertTrue(picker.isPowerButtonOn()); + picker.setPowerButtonOn(false); + Assert.assertFalse(picker.isPowerButtonOn()); + } + + public void testPowerButtonColorOnSetterAndGetter() { + Assert.assertEquals(0xfff7f0dc, picker.getPowerButtonColorOn()); + picker.setPowerButtonColorOn(Color.BLUE); + Assert.assertEquals(Color.BLUE, picker.getPowerButtonColorOn()); + } + + public void testPowerButtonColorOffSetterAndGetter() { + Assert.assertEquals(0xff404040, picker.getPowerButtonColorOff()); + picker.setPowerButtonColorOff(Color.BLUE); + Assert.assertEquals(Color.BLUE, picker.getPowerButtonColorOff()); + } + + public void testColorSetterAndGetter() { + Assert.assertEquals(0xff00ff00, picker.getColor()); + picker.setColor(Color.BLUE); + Assert.assertEquals(Color.BLUE, picker.getColor()); + } + + public void testBrightnessSetterAndGetter() { + Assert.assertEquals(0f, picker.getBrightnessValue(), 0); + picker.setBrightnessValue(55.54); + Assert.assertEquals(55.54, picker.getBrightnessValue(), 0.001); + picker.setBrightnessValue(-1); + Assert.assertEquals(0, picker.getBrightnessValue(), 0); + picker.setBrightnessValue(80.88); + Assert.assertEquals(80.88, picker.getBrightnessValue(), 0.001); + picker.setBrightnessValue(110); + Assert.assertEquals(100, picker.getBrightnessValue(), 0); + } + + public void testMovingGetter() { + Assert.assertFalse(picker.isMoving()); + } + + public void testBrightnessMarkersSetterAndGetter() { + Assert.assertNull(picker.getBrightnessMarkers()); + + ArrayList brigtness = new ArrayList<>(); + brigtness.add(0.0); + brigtness.add(10.0); + brigtness.add(50.0); + brigtness.add(90.0); + brigtness.add(100.0); + picker.setBrightnessMarkers(brigtness); + + Assert.assertNotNull(picker.getBrightnessMarkers()); + Assert.assertEquals(5, picker.getBrightnessMarkers().size(), 0); + picker.setBrightnessMarkers(null); + Assert.assertNull(picker.getBrightnessMarkers()); + } + + public void testColorMarkersSetterAndGetter() { + Assert.assertNull(picker.getColorMarkers()); + + ArrayList colors = new ArrayList<>(); + colors.add(new Double(Color.RED)); + colors.add(new Double(Color.GREEN)); + colors.add(new Double(Color.BLUE)); + picker.setColorMarkers(colors); + + Assert.assertNotNull(picker.getColorMarkers()); + Assert.assertEquals(3, picker.getColorMarkers().size(), 0); + picker.setColorMarkers(null); + Assert.assertNull(picker.getColorMarkers()); + + + Double d = new Double(Color.WHITE); + Assert.assertEquals(Color.WHITE, d, 0); + Assert.assertEquals(Color.WHITE, d.intValue(), 0); + + d = new Double(Color.BLACK); + Assert.assertEquals(Color.BLACK, d, 0); + Assert.assertEquals(Color.BLACK, d.intValue(), 0); } } \ No newline at end of file From 0c55c81c90b74257b0cf85e3011b2f4cb31f086b Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Sun, 7 Jun 2020 20:31:19 +0200 Subject: [PATCH 14/31] powerButtonVisible change the default value to true --- .../java/org/supla/android/SuplaColorBrightnessPicker.java | 1 + .../org/supla/android/SuplaColorBrightnessPickerTest.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java index 3822f313b..169027bc8 100644 --- a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java +++ b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java @@ -139,6 +139,7 @@ private void init() { powerButtonColorOn = 0xfff7f0dc; powerButtonColorOff = 0xff404040; + powerButtonVisible = true; powerButtonEnabled = true; setBrightnessValue(0); diff --git a/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java b/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java index 9caafd164..01263e90f 100644 --- a/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java +++ b/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java @@ -76,11 +76,11 @@ public void testSliderVisibleSetterAndGetter() { } public void testPowerButtonVisibleSetterAndGetter() { - Assert.assertFalse(picker.isPowerButtonVisible()); - picker.setPowerButtonVisible(true); Assert.assertTrue(picker.isPowerButtonVisible()); picker.setPowerButtonVisible(false); Assert.assertFalse(picker.isPowerButtonVisible()); + picker.setPowerButtonVisible(true); + Assert.assertTrue(picker.isPowerButtonVisible()); } public void testPowerButtonEnabledSetterAndGetter() { From ff8c9da7b7dbd30675e992216a906802b9b836c6 Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Sun, 7 Jun 2020 20:32:34 +0200 Subject: [PATCH 15/31] circleInsteadArrow change the default value to true --- .../java/org/supla/android/SuplaColorBrightnessPicker.java | 2 +- .../org/supla/android/SuplaColorBrightnessPickerTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java index 169027bc8..0b9171709 100644 --- a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java +++ b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java @@ -135,7 +135,7 @@ private void init() { colorPointerMoving = false; brightnessWheelPointerMoving = false; colorfulBrightnessWheel = true; - circleInsteadArrow = false; + circleInsteadArrow = true; powerButtonColorOn = 0xfff7f0dc; powerButtonColorOff = 0xff404040; diff --git a/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java b/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java index 01263e90f..bbce087ff 100644 --- a/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java +++ b/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java @@ -52,11 +52,11 @@ public void testColorWheelVisibleSetterAndGetter() { } public void testCircleInsteadArrowSetterAndGetter() { - Assert.assertFalse(picker.isCircleInsteadArrow()); - picker.setCircleInsteadArrow(true); Assert.assertTrue(picker.isCircleInsteadArrow()); picker.setCircleInsteadArrow(false); Assert.assertFalse(picker.isCircleInsteadArrow()); + picker.setCircleInsteadArrow(true); + Assert.assertTrue(picker.isCircleInsteadArrow()); } public void testColorfulBrightnessWheelSetterAndGetter() { From 07e61d8a6989c5f2e7f6016c1e55788eb7c82fed Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Sun, 7 Jun 2020 22:38:03 +0200 Subject: [PATCH 16/31] Trim when white color selected --- .../java/org/supla/android/SuplaColorBrightnessPicker.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java index 0b9171709..9e2c77e14 100644 --- a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java +++ b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java @@ -356,7 +356,8 @@ private void drawWheel(Canvas canvas) { if (circleInsteadArrow) { float angle = (float) (brightnessWheelPointerAngle - m90d); - if (!colorfulBrightnessWheel || !colorWheelVisible) { + if (!colorfulBrightnessWheel || !colorWheelVisible + || (colorWheelVisible && (selectedColor & 0xffffff) == 0xffffff )) { angle = trimBrightnessColorAngle(angle); } drawCirclePointer(canvas, brightnessWheelPointerAngle, From cd6bc4381d11f8bfa38fa2cac14603085ce6bdc2 Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Mon, 8 Jun 2020 19:44:08 +0200 Subject: [PATCH 17/31] powerButtonColorOn change the default value to white --- .../main/java/org/supla/android/SuplaColorBrightnessPicker.java | 2 +- .../java/org/supla/android/SuplaColorBrightnessPickerTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java index 9e2c77e14..065022b2c 100644 --- a/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java +++ b/app/src/main/java/org/supla/android/SuplaColorBrightnessPicker.java @@ -136,7 +136,7 @@ private void init() { brightnessWheelPointerMoving = false; colorfulBrightnessWheel = true; circleInsteadArrow = true; - powerButtonColorOn = 0xfff7f0dc; + powerButtonColorOn = 0xffffffff; powerButtonColorOff = 0xff404040; powerButtonVisible = true; diff --git a/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java b/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java index bbce087ff..0719603ad 100644 --- a/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java +++ b/app/src/test/java/org/supla/android/SuplaColorBrightnessPickerTest.java @@ -100,7 +100,7 @@ public void testPowerButtonOnSetterAndGetter() { } public void testPowerButtonColorOnSetterAndGetter() { - Assert.assertEquals(0xfff7f0dc, picker.getPowerButtonColorOn()); + Assert.assertEquals(0xffffffff, picker.getPowerButtonColorOn()); picker.setPowerButtonColorOn(Color.BLUE); Assert.assertEquals(Color.BLUE, picker.getPowerButtonColorOn()); } From da2ab3d76814ba107a8165f774a873ff2b39d0aa Mon Sep 17 00:00:00 2001 From: Przemek Zygmunt Date: Mon, 8 Jun 2020 19:57:30 +0200 Subject: [PATCH 18/31] New RGBW controller layout --- ...lDetailRGB.java => ChannelDetailRGBW.java} | 184 ++++++++++------ .../java/org/supla/android/Preferences.java | 15 ++ .../supla/android/SuplaColorListPicker.java | 14 +- .../org/supla/android/VLCalibrationTool.java | 4 +- .../android/listview/ChannelListView.java | 6 +- app/src/main/res/drawable/rgbwinfo.png | Bin 0 -> 15184 bytes app/src/main/res/drawable/rgbwpoweroff.png | Bin 0 -> 18882 bytes app/src/main/res/drawable/rgbwpoweron.png | Bin 0 -> 14089 bytes app/src/main/res/drawable/rgbwsettings.png | Bin 0 -> 22190 bytes .../res/drawable/rounded_rgb_left_btn.xml | 20 -- .../res/drawable/rounded_rgb_left_sel_btn.xml | 20 -- .../res/drawable/rounded_rgb_right_btn.xml | 20 -- .../drawable/rounded_rgb_right_sel_btn.xml | 20 -- .../res/drawable/rounded_rgbw_sel_btn.xml | 16 ++ .../drawable/rounded_rgbw_tab_background.xml | 16 ++ app/src/main/res/layout/detail_rgb.xml | 202 ------------------ app/src/main/res/layout/detail_rgbw.xml | 165 ++++++++++++++ app/src/main/res/values-pl/strings.xml | 2 + app/src/main/res/values-sw600dp/dimens.xml | 4 +- app/src/main/res/values/colors.xml | 2 +- app/src/main/res/values/dimens.xml | 4 +- app/src/main/res/values/strings.xml | 2 + 22 files changed, 351 insertions(+), 365 deletions(-) rename app/src/main/java/org/supla/android/{ChannelDetailRGB.java => ChannelDetailRGBW.java} (74%) create mode 100644 app/src/main/res/drawable/rgbwinfo.png create mode 100644 app/src/main/res/drawable/rgbwpoweroff.png create mode 100644 app/src/main/res/drawable/rgbwpoweron.png create mode 100644 app/src/main/res/drawable/rgbwsettings.png delete mode 100644 app/src/main/res/drawable/rounded_rgb_left_btn.xml delete mode 100644 app/src/main/res/drawable/rounded_rgb_left_sel_btn.xml delete mode 100644 app/src/main/res/drawable/rounded_rgb_right_btn.xml delete mode 100644 app/src/main/res/drawable/rounded_rgb_right_sel_btn.xml create mode 100644 app/src/main/res/drawable/rounded_rgbw_sel_btn.xml create mode 100644 app/src/main/res/drawable/rounded_rgbw_tab_background.xml delete mode 100644 app/src/main/res/layout/detail_rgb.xml create mode 100644 app/src/main/res/layout/detail_rgbw.xml diff --git a/app/src/main/java/org/supla/android/ChannelDetailRGB.java b/app/src/main/java/org/supla/android/ChannelDetailRGBW.java similarity index 74% rename from app/src/main/java/org/supla/android/ChannelDetailRGB.java rename to app/src/main/java/org/supla/android/ChannelDetailRGBW.java index 314952446..39fc03937 100644 --- a/app/src/main/java/org/supla/android/ChannelDetailRGB.java +++ b/app/src/main/java/org/supla/android/ChannelDetailRGBW.java @@ -30,9 +30,7 @@ of the License, or (at your option) any later version. import android.view.View; import android.view.ViewGroup; import android.widget.Button; -import android.widget.ImageView; import android.widget.RelativeLayout; -import android.widget.TextView; import org.supla.android.db.Channel; import org.supla.android.db.ChannelBase; @@ -47,7 +45,7 @@ of the License, or (at your option) any later version. import java.util.Timer; import java.util.TimerTask; -public class ChannelDetailRGB extends DetailLayout implements View.OnClickListener, +public class ChannelDetailRGBW extends DetailLayout implements View.OnClickListener, SuplaColorBrightnessPicker.OnColorBrightnessChangeListener, SuplaColorListPicker.OnColorListTouchListener { @@ -57,13 +55,15 @@ public class ChannelDetailRGB extends DetailLayout implements View.OnClickListen private SuplaColorListPicker clPicker; private Button tabRGB; private Button tabDimmer; + private Button tabWheel; + private Button tabSlider; private ViewGroup tabs; - private TextView tvTitle; + private ViewGroup pickerTypeTabs; + private ViewGroup llExtraButtons; private Button btnSettings; + private Button btnInfo; private RelativeLayout rlMain; private VLCalibrationTool vlCalibrationTool = null; - private TextView tvStateCaption; - private ImageView stateImage; private long remoteUpdateTime; private long changeFinishedTime; private Timer delayTimer1; @@ -72,20 +72,21 @@ public class ChannelDetailRGB extends DetailLayout implements View.OnClickListen private int lastColor; private int lastColorBrightness; private int lastBrightness; + private Button btnPowerOnOff; - public ChannelDetailRGB(Context context, ChannelListView cLV) { + public ChannelDetailRGBW(Context context, ChannelListView cLV) { super(context, cLV); } - public ChannelDetailRGB(Context context, AttributeSet attrs) { + public ChannelDetailRGBW(Context context, AttributeSet attrs) { super(context, attrs); } - public ChannelDetailRGB(Context context, AttributeSet attrs, int defStyleAttr) { + public ChannelDetailRGBW(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } - public ChannelDetailRGB(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + public ChannelDetailRGBW(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @@ -93,11 +94,12 @@ protected void init() { super.init(); - tabs = findViewById(R.id.rlTabs); + tabs = findViewById(R.id.llTabs); + pickerTypeTabs = findViewById(R.id.llPickerTypeTabs); Resources r = getResources(); - status = findViewById(R.id.rgbstatus); + status = findViewById(R.id.rgbwstatus); status.setOnlineColor(getResources().getColor(R.color.channel_dot_on)); status.setOfflineColor(getResources().getColor(R.color.channel_dot_off)); @@ -115,29 +117,33 @@ protected void init() { tabRGB = findViewById(R.id.rgbTabBtn_RGB); tabDimmer = findViewById(R.id.rgbTabBtn_Dimmer); + tabWheel = findViewById(R.id.rgbTabBtn_Wheel); + tabSlider = findViewById(R.id.rgbTabBtn_Slider); tabRGB.setOnClickListener(this); tabDimmer.setOnClickListener(this); + tabWheel.setOnClickListener(this); + tabSlider.setOnClickListener(this); - btnSettings = findViewById(R.id.rgbBtnSettings); + llExtraButtons = findViewById(R.id.llExtraButtons); + llExtraButtons.setVisibility(GONE); + + btnInfo = findViewById(R.id.rgbwBtnInfo); + btnSettings = findViewById(R.id.rgbwBtnSettings); + btnInfo.setOnClickListener(this); btnSettings.setOnClickListener(this); - btnSettings.setVisibility(GONE); - rlMain = findViewById(R.id.rlRgbMain); + rlMain = findViewById(R.id.rlRgbwMain); rlMain.setVisibility(VISIBLE); + btnPowerOnOff = findViewById(R.id.rgbwBtnPowerOnOff); + btnPowerOnOff.setOnClickListener(this); + Typeface type = SuplaApp.getApp().getTypefaceOpenSansBold(); tabRGB.setTypeface(type); tabDimmer.setTypeface(type); - - tvStateCaption = findViewById(R.id.rgbDetailStateCaption); - tvStateCaption.setTypeface(type); - - tvTitle = findViewById(R.id.rgbDetailTitle); - tvTitle.setTypeface(SuplaApp.getApp().getTypefaceQuicksandRegular()); - - stateImage = findViewById(R.id.rgbDetailStateImage); - stateImage.setOnClickListener(this); + tabWheel.setTypeface(type); + tabSlider.setTypeface(type); remoteUpdateTime = 0; changeFinishedTime = 0; @@ -148,7 +154,11 @@ protected void init() { private void showRGB() { cbPicker.setColorWheelVisible(true); + cbPicker.setSliderVisible(false); clPicker.setVisibility(View.VISIBLE); + pickerTypeTabs.setVisibility(GONE); + llExtraButtons.setVisibility(GONE); + btnPowerOnOff.setVisibility(GONE); channelDataToViews(); } @@ -157,7 +167,31 @@ private void showDimmer() { cbPicker.setColorWheelVisible(false); clPicker.setVisibility(View.GONE); + pickerTypeTabs.setVisibility(VISIBLE); + + boolean varilight = false; + + if (getChannelBase() instanceof Channel) { + Channel c = (Channel) getChannelBase(); + if (c.getManufacturerID() == SuplaConst.SUPLA_MFR_DOYLETRATT + && c.getProductID() == 1) { + varilight = true; + } + } + if (varilight) { + vlCalibrationTool = new VLCalibrationTool(this); + llExtraButtons.setVisibility(VISIBLE); + } + + Preferences prefs = new Preferences(getContext()); + + Boolean typeSlider = prefs.isBrightnessPickerTypeSlider(); + if (typeSlider == null) { + typeSlider = varilight; + } + + onClick(typeSlider ? tabSlider : tabWheel); channelDataToViews(); } @@ -184,27 +218,19 @@ public void onDetailShow() { public void setData(ChannelBase channel) { super.setData(channel); - btnSettings.setVisibility(GONE); + llExtraButtons.setVisibility(GONE); + pickerTypeTabs.setVisibility(GONE); + + if (vlCalibrationTool != null) { + vlCalibrationTool.Hide(); + vlCalibrationTool = null; + } switch (channel.getFunc()) { case SuplaConst.SUPLA_CHANNELFNC_DIMMER: showDimmer(); tabs.setVisibility(View.GONE); - - if (vlCalibrationTool != null) { - vlCalibrationTool.Hide(); - vlCalibrationTool = null; - } - - if (channel instanceof Channel) { - Channel c = (Channel) channel; - if (c.getManufacturerID() == SuplaConst.SUPLA_MFR_DOYLETRATT - && c.getProductID() == 1) { - vlCalibrationTool = new VLCalibrationTool(this); - btnSettings.setVisibility(VISIBLE); - } - } break; case SuplaConst.SUPLA_CHANNELFNC_RGBLIGHTING: @@ -233,10 +259,6 @@ private void channelDataToViews() { if (isGroup()) { ChannelGroup cgroup = (ChannelGroup) getChannelFromDatabase(); - tvTitle.setText(cgroup.getNotEmptyCaption(getContext())); - - stateImage.setVisibility(View.GONE); - tvStateCaption.setVisibility(View.GONE); status.setVisibility(View.VISIBLE); status.setPercent(cgroup.getOnLinePercent()); @@ -275,12 +297,8 @@ private void channelDataToViews() { } else { Channel channel = (Channel) getChannelFromDatabase(); - tvTitle.setText(channel.getNotEmptyCaption(getContext())); status.setVisibility(View.GONE); - stateImage.setVisibility(View.VISIBLE); - tvStateCaption.setVisibility(View.VISIBLE); - if (cbPicker.isColorWheelVisible() && (int) cbPicker.getBrightnessValue() != (int) channel.getColorBrightness()) { cbPicker.setBrightnessValue(channel.getColorBrightness()); @@ -309,18 +327,18 @@ private void channelDataToViews() { } - pickerToInfoPanel(); + pickerToUI(); } @Override public View inflateContentView() { - return inflateLayout(R.layout.detail_rgb); + return inflateLayout(R.layout.detail_rgbw); } private void setBtnBackground(Button btn, int id) { - Drawable d = getResources().getDrawable(id); + Drawable d = id == 0 ? null : getResources().getDrawable(id); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { btn.setBackground(d); @@ -329,13 +347,17 @@ private void setBtnBackground(Button btn, int id) { } } + private void setPowerBtnOn(boolean on) { + cbPicker.setPowerButtonOn(on); + setBtnBackground(btnPowerOnOff, on ? R.drawable.rgbwpoweron : R.drawable.rgbwpoweroff); + } + @SuppressLint("SetTextI18n") - private void pickerToInfoPanel() { + private void pickerToUI() { lastColor = cbPicker.getColor(); - int brightness = (int) cbPicker.getBrightnessValue(); - stateImage.setImageResource(brightness > 0 ? R.drawable.poweron : R.drawable.poweroff); + setPowerBtnOn(brightness > 0); if (cbPicker.isColorWheelVisible()) lastColorBrightness = brightness; @@ -397,54 +419,78 @@ private void sendNewValues() { @Override public void onClick(View v) { - if (v == tabRGB) { showRGB(); - setBtnBackground(tabRGB, R.drawable.rounded_rgb_left_sel_btn); - setBtnBackground(tabDimmer, R.drawable.rounded_rgb_right_btn); + setBtnBackground(tabRGB, R.drawable.rounded_rgbw_sel_btn); + setBtnBackground(tabDimmer, 0); - tabRGB.setTextColor(getResources().getColor(R.color.detail_rgb_gb)); + tabRGB.setTextColor(Color.WHITE); tabDimmer.setTextColor(Color.BLACK); } else if (v == tabDimmer) { showDimmer(); - setBtnBackground(tabRGB, R.drawable.rounded_rgb_left_btn); - setBtnBackground(tabDimmer, R.drawable.rounded_rgb_right_sel_btn); + setBtnBackground(tabDimmer, R.drawable.rounded_rgbw_sel_btn); + setBtnBackground(tabRGB, 0); tabRGB.setTextColor(Color.BLACK); - tabDimmer.setTextColor(getResources().getColor(R.color.detail_rgb_gb)); - } else if (v == stateImage) { - cbPicker.setBrightnessValue(cbPicker.getBrightnessValue() > 0 ? 0 : 100); - pickerToInfoPanel(); - sendNewValues(true, true); - onChangeFinished(cbPicker); + tabDimmer.setTextColor(Color.WHITE); + } else if (v == tabWheel) { + setBtnBackground(tabWheel, R.drawable.rounded_rgbw_sel_btn); + setBtnBackground(tabSlider, 0); + + tabWheel.setTextColor(Color.WHITE); + tabSlider.setTextColor(Color.BLACK); + btnPowerOnOff.setVisibility(GONE); + + } else if (v == tabSlider) { + setBtnBackground(tabWheel, 0); + setBtnBackground(tabSlider, R.drawable.rounded_rgbw_sel_btn); + + tabWheel.setTextColor(Color.BLACK); + tabSlider.setTextColor(Color.WHITE); + btnPowerOnOff.setVisibility(VISIBLE); } else if (v == btnSettings - && vlCalibrationTool != null) { + && vlCalibrationTool != null) { vlCalibrationTool.Show(); + } else if (v == btnPowerOnOff) { + cbPicker.setPowerButtonOn(!cbPicker.isPowerButtonOn()); + onPowerButtonClick(cbPicker); + } else if (v == btnInfo) { + } if (v == tabDimmer || v == tabRGB) { channelDataToViews(); } + if (v == tabWheel || v == tabSlider) { + cbPicker.setSliderVisible(v == tabSlider); + + Preferences prefs = new Preferences(getContext()); + prefs.setBrightnessPickerTypeSlider(cbPicker.isSliderVisible()); + } + } @Override public void onColorChanged(SuplaColorBrightnessPicker scbPicker, int color) { - pickerToInfoPanel(); + pickerToUI(); sendNewValues(); } @Override public void onBrightnessChanged(SuplaColorBrightnessPicker scbPicker, double brightness) { - pickerToInfoPanel(); + pickerToUI(); sendNewValues(); } @Override public void onPowerButtonClick(SuplaColorBrightnessPicker scbPicker) { - + scbPicker.setBrightnessValue(scbPicker.isPowerButtonOn() ? 100 : 0); + pickerToUI(); + sendNewValues(true, true); + onChangeFinished(scbPicker); } private void updateDelayed() { diff --git a/app/src/main/java/org/supla/android/Preferences.java b/app/src/main/java/org/supla/android/Preferences.java index 6c5b2932f..7de52127d 100644 --- a/app/src/main/java/org/supla/android/Preferences.java +++ b/app/src/main/java/org/supla/android/Preferences.java @@ -47,6 +47,8 @@ public class Preferences { private static final String pref_wizard_selected_wifi = "pref_wizard_selected_wifi"; private static final String pref_hp_turbo_time = "pref_hp_turbo_time"; private static final String pref_hp_eco_reduction = "pref_hp_eco_reduction"; + private static final String pref_brightness_picker_type_slider + = "pref_brightness_picker_type_slider"; private SharedPreferences _prefs; private Context _context; @@ -249,4 +251,17 @@ public void wizardSetSelectedWifi(String SSID) { editor.putString(pref_wizard_selected_wifi, SSID); editor.apply(); } + + public void setBrightnessPickerTypeSlider(boolean slider) { + SharedPreferences.Editor editor = _prefs.edit(); + editor.putBoolean(pref_brightness_picker_type_slider, slider); + editor.apply(); + } + + public Boolean isBrightnessPickerTypeSlider() { + if (_prefs.contains(pref_brightness_picker_type_slider)) { + return _prefs.getBoolean(pref_brightness_picker_type_slider, false); + } + return null; + } } diff --git a/app/src/main/java/org/supla/android/SuplaColorListPicker.java b/app/src/main/java/org/supla/android/SuplaColorListPicker.java index 12ae69c34..f9c0dd313 100644 --- a/app/src/main/java/org/supla/android/SuplaColorListPicker.java +++ b/app/src/main/java/org/supla/android/SuplaColorListPicker.java @@ -38,7 +38,8 @@ public class SuplaColorListPicker extends View { private ArrayList Items = null; private float Space = 0; private float BorderWidth = 0; - private int BorderColor = Color.BLACK; + private int BorderColor = Color.WHITE; + private int BrightnessLevelColor = Color.BLACK; private int BorderColorSelected = Color.YELLOW; private ListItem TouchedItem = null; private OnColorListTouchListener mOnTouchListener; @@ -203,6 +204,15 @@ public void setBorderColorSelected(int borderColorSelected) { invalidate(); } + public int getBrightnessLevelColor() { + return BrightnessLevelColor; + } + + public void setBrightnessLevelColor(int brightnessLevelColor) { + BrightnessLevelColor = brightnessLevelColor; + invalidate(); + } + @Override protected void onDraw(Canvas canvas) { @@ -239,7 +249,7 @@ protected void onDraw(Canvas canvas) { if (i.getPercent() > 0) { - p.setColor(BorderColor); + p.setColor(BrightnessLevelColor); double rl_margin = width * 0.05; double b_margin = width * 0.1; diff --git a/app/src/main/java/org/supla/android/VLCalibrationTool.java b/app/src/main/java/org/supla/android/VLCalibrationTool.java index 287b4a720..329aab53a 100644 --- a/app/src/main/java/org/supla/android/VLCalibrationTool.java +++ b/app/src/main/java/org/supla/android/VLCalibrationTool.java @@ -32,7 +32,7 @@ public class VLCalibrationTool implements View.OnClickListener, SuplaRangeCalibr private final static int UI_REFRESH_LOCK_TIME = 2000; private final static int MIN_SEND_DELAY_TIME = 500; private final static int DISPLAY_DELAY_TIME = 1000; - private ChannelDetailRGB detailRGB; + private ChannelDetailRGBW detailRGB; private Button btnOK; private Button btnRestore; private Button btnCancel; @@ -58,7 +58,7 @@ public class VLCalibrationTool implements View.OnClickListener, SuplaRangeCalibr private long lastCalCfgTime = 0; private Handler _sc_msg_handler = null; - public VLCalibrationTool(ChannelDetailRGB detailRGB) { + public VLCalibrationTool(ChannelDetailRGBW detailRGB) { this.detailRGB = detailRGB; mainView = (RelativeLayout) detailRGB.inflateLayout(R.layout.vl_calibration); diff --git a/app/src/main/java/org/supla/android/listview/ChannelListView.java b/app/src/main/java/org/supla/android/listview/ChannelListView.java index 13034afc7..558ee48c7 100644 --- a/app/src/main/java/org/supla/android/listview/ChannelListView.java +++ b/app/src/main/java/org/supla/android/listview/ChannelListView.java @@ -36,7 +36,7 @@ of the License, or (at your option) any later version. import org.supla.android.ChannelDetailEM; import org.supla.android.ChannelDetailIC; -import org.supla.android.ChannelDetailRGB; +import org.supla.android.ChannelDetailRGBW; import org.supla.android.ChannelDetailRS; import org.supla.android.ChannelDetailTempHumidity; import org.supla.android.ChannelDetailTemperature; @@ -112,7 +112,7 @@ private DetailLayout getDetailLayout(ChannelBase cbase) { case SuplaConst.SUPLA_CHANNELFNC_DIMMERANDRGBLIGHTING: case SuplaConst.SUPLA_CHANNELFNC_RGBLIGHTING: - if (!(mDetailLayout instanceof ChannelDetailRGB)) + if (!(mDetailLayout instanceof ChannelDetailRGBW)) mDetailLayout = null; break; @@ -176,7 +176,7 @@ private DetailLayout getDetailLayout(ChannelBase cbase) { case SuplaConst.SUPLA_CHANNELFNC_DIMMER: case SuplaConst.SUPLA_CHANNELFNC_DIMMERANDRGBLIGHTING: case SuplaConst.SUPLA_CHANNELFNC_RGBLIGHTING: - mDetailLayout = new ChannelDetailRGB(getContext(), this); + mDetailLayout = new ChannelDetailRGBW(getContext(), this); break; case SuplaConst.SUPLA_CHANNELFNC_CONTROLLINGTHEROLLERSHUTTER: mDetailLayout = new ChannelDetailRS(getContext(), this); diff --git a/app/src/main/res/drawable/rgbwinfo.png b/app/src/main/res/drawable/rgbwinfo.png new file mode 100644 index 0000000000000000000000000000000000000000..2d59c98f1bd9555c92205aaafe76216b01263a55 GIT binary patch literal 15184 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 LQKDANIOP8UH1oW7 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/rgbwpoweron.png b/app/src/main/res/drawable/rgbwpoweron.png new file mode 100644 index 0000000000000000000000000000000000000000..2c09aae766cf67f217991cd0aee8a6b9bb376fb9 GIT binary patch literal 14089 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/rgbwsettings.png b/app/src/main/res/drawable/rgbwsettings.png new file mode 100644 index 0000000000000000000000000000000000000000..4cc435742b70ccc870779c3d77a7a994494a544f GIT binary patch literal 22190 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,C5Z%r$}-k$+Lzp8O#`5+OQP? zY{VymVKhV?r{I4=x#TEpCkdzq4dZ>5_5;Y-pz- zc^N7eAv;Mx)g@y}ZC(b4`&8GHe8cc%TT;AGGE!ij=9y6wbqq7wX7e=CyTEgcVVvJ2 z={6xU$@>joc#-6)gyC`$sR{2R@S_o@K^OIE1}rJ4R>@fEvJ*vEXP#O{RPavA#D z6Lvb(OoNlIBU=-Ks@rD@jPHOf$(u=DFnsy;oNnxfnhue@L>*DecEh+{Ao;sNSP3*= zDy5G3gs*%9z}g&!ql8UBlG!Wl0?Jw$JDqG*N*#|o;OwI$|I0x436d*wrm@OFr^6(s z!Gy|688e;ylH_$kISFjxE2S<0SPgTwb02=7T*`em%N*^dGI ziBjrvl7ms9SzaW$C`Ve$HovEnJgFHdi)4&U^2M~?^iiLRJxy{>j;F&w_gLo$syQ+? zjCI>4qP=y%r-F~?JnYxDfGB)M9a*}CU#25=d`){GmoqX>69_Dxg*$1JGF+Q18ue=4wtsT8P4lWn|NkBGC?vn;V=y-M7S|Rq zI4f&?_WsEFwIq)q&qDI^KA|?vAvfIx7?I+swp;Y`d$9%Vr2yA}INtnZcM_4PEm&6h z1nD)TM)D8PN<#8>l1D8Xe^KJDy9!{e4nA!02`6sIF_O2U6@}!DB!5CX*&X-MvYeMe zOD#p|^Bd91LQ;|ZE6ca6aVWO00O_g|%FkH#-9^^ZXDhUeki3)RTb3PkO57KvHU_FB zeQ?UM&)y<=C)!a+&Lg>#_Ht0t>U6ddP@U4|g8}+!C&_tecOm&O$#VfZ?y$VQOIjGH zCDO*j0s7?}$%j$MRdCHG`Gkc-b#~U1?~`27LO`vMHk}X950^;(Bgy#%NqWHgNdAn$ zWshut_vR3$Ua9+@5bgdM$-aV|VAJCXhLdqw4P|+gyI<-!9bJcwL-cc&Zv*%T06XMtXQm}rjx&-U zMLp;E-vaoQ!$%(ha2WN>F-HKrBbx0r>KTvE0yru7sdITA7u8~#>}CL863FtBJ7T}U^skc5rzNCiHzy^Z(_K=Qi!q&cdnt>B-BMnT zJv~qXR_vg431oi=;IjZeEen8BOkY_8@M|gm4d9mmUQtS&X@~x9pKuC2a3ullQviNX zR(csIUy^>836A#{d-zm0>1|EKT4@uJNN7V0s1VEb!MLW?EnA(07*qo IM6N<$g4cAd%m4rY literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/rounded_rgb_left_btn.xml b/app/src/main/res/drawable/rounded_rgb_left_btn.xml deleted file mode 100644 index c4429e028..000000000 --- a/app/src/main/res/drawable/rounded_rgb_left_btn.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_rgb_left_sel_btn.xml b/app/src/main/res/drawable/rounded_rgb_left_sel_btn.xml deleted file mode 100644 index b174d8783..000000000 --- a/app/src/main/res/drawable/rounded_rgb_left_sel_btn.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_rgb_right_btn.xml b/app/src/main/res/drawable/rounded_rgb_right_btn.xml deleted file mode 100644 index 9733d32a7..000000000 --- a/app/src/main/res/drawable/rounded_rgb_right_btn.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_rgb_right_sel_btn.xml b/app/src/main/res/drawable/rounded_rgb_right_sel_btn.xml deleted file mode 100644 index b1299882a..000000000 --- a/app/src/main/res/drawable/rounded_rgb_right_sel_btn.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_rgbw_sel_btn.xml b/app/src/main/res/drawable/rounded_rgbw_sel_btn.xml new file mode 100644 index 000000000..7d68f357f --- /dev/null +++ b/app/src/main/res/drawable/rounded_rgbw_sel_btn.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_rgbw_tab_background.xml b/app/src/main/res/drawable/rounded_rgbw_tab_background.xml new file mode 100644 index 000000000..032d11df0 --- /dev/null +++ b/app/src/main/res/drawable/rounded_rgbw_tab_background.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/detail_rgb.xml b/app/src/main/res/layout/detail_rgb.xml deleted file mode 100644 index 1533de884..000000000 --- a/app/src/main/res/layout/detail_rgb.xml +++ /dev/null @@ -1,202 +0,0 @@ - - - -