]> git.ktnx.net Git - mobile-ledger.git/commitdiff
replace colorAccent with colorSecondary
authorDamyan Ivanov <dam+mobileledger@ktnx.net>
Fri, 10 Jul 2020 06:46:04 +0000 (09:46 +0300)
committerDamyan Ivanov <dam+mobileledger@ktnx.net>
Fri, 10 Jul 2020 06:46:04 +0000 (09:46 +0300)
same color, another name

22 files changed:
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListAdapter.java
app/src/main/java/net/ktnx/mobileledger/utils/Colors.java
app/src/main/res/drawable-anydpi-v21/dashed_border_1dp.xml
app/src/main/res/drawable-anydpi-v21/dashed_border_8dp.xml
app/src/main/res/drawable-anydpi-v21/expand_more_black_24dp.xml
app/src/main/res/drawable-anydpi-v21/ic_assignment_black_24dp.xml
app/src/main/res/drawable-anydpi-v21/ic_clear_accent_24dp.xml
app/src/main/res/drawable-anydpi-v21/ic_error_outline_black_24dp.xml
app/src/main/res/drawable-anydpi-v21/ic_event_note_black_24dp.xml
app/src/main/res/drawable-anydpi-v21/ic_expand_less_black_24dp.xml
app/src/main/res/drawable-anydpi-v21/ic_home_black_24dp.xml
app/src/main/res/drawable-anydpi-v21/ic_mode_edit_black_24dp.xml
app/src/main/res/drawable-anydpi-v21/ic_more_horiz_black_24dp.xml
app/src/main/res/drawable-anydpi-v21/ic_settings_black_24dp.xml
app/src/main/res/drawable-anydpi-v21/ic_unfold_more_black_24dp.xml
app/src/main/res/drawable-anydpi-v21/ic_view_list_black_24dp.xml
app/src/main/res/layout/activity_profile_detail.xml
app/src/main/res/layout/fragment_new_transaction.xml
app/src/main/res/layout/fragment_new_transaction_saving.xml
app/src/main/res/layout/no_profiles.xml
app/src/main/res/layout/transaction_list_fragment.xml
tools/gen-styles

index 6179174f12e574fefe0f28113544bf5ad6770b87..c61b9193d69e5071c781d43e122a4ab648524189 100644 (file)
@@ -202,8 +202,8 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowH
                     if ((boldAccountName != null) && acc.getAccountName()
                                                         .startsWith(boldAccountName))
                     {
-                        accName.setTextColor(Colors.accent);
-                        accAmount.setTextColor(Colors.accent);
+                        accName.setTextColor(Colors.secondary);
+                        accAmount.setTextColor(Colors.secondary);
 
                         SpannableString ss = new SpannableString(acc.getAccountName());
                         ss.setSpan(new StyleSpan(Typeface.BOLD), 0, boldAccountName.length(),
index 283f9ea7796bfb4eaa6c3c4cb7f683e94ff26bc2..eaae572008149fcedaf0d9715f1d6ad302957a0a 100644 (file)
@@ -48,7 +48,7 @@ public class Colors {
     private static final float yellowLightness = 0.350f;
     private static final int[][] EMPTY_STATES = new int[][]{new int[0]};
     public static @ColorInt
-    int accent;
+    int secondary;
     @ColorInt
     public static int tableRowDarkBG;
     public static int profileThemeId = -1;
@@ -78,8 +78,8 @@ public class Colors {
         TypedValue tv = new TypedValue();
         theme.resolveAttribute(R.attr.table_row_dark_bg, tv, true);
         tableRowDarkBG = tv.data;
-        theme.resolveAttribute(R.attr.colorAccent, tv, true);
-        accent = tv.data;
+        theme.resolveAttribute(R.attr.colorSecondary, tv, true);
+        secondary = tv.data;
 
         // trigger theme observers
         themeWatch.postValue(themeWatch.getValue() + 1);
index c9e5e96c51c01ec3f21360a5938f9d56d89d24f0..1360399b7e5185dd96dac82a6644c69c4ffda980 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright © 2019 Damyan Ivanov.
+  ~ Copyright © 2020 Damyan Ivanov.
   ~ This file is part of MoLe.
   ~ MoLe is free software: you can distribute it and/or modify it
   ~ under the term of the GNU General Public License as published by
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="line"
     android:thickness="1dp"
-    android:tint="?colorAccent">
+    android:tint="?colorSecondary"
+    >
     <stroke
         android:width="1dp"
-        android:color="?colorAccent"
+        android:color="?colorSecondary"
         android:dashWidth="2dp"
-        android:dashGap="6dp" />
+        android:dashGap="6dp"
+        />
 
 </shape>
\ No newline at end of file
index 884aed262c8132b0b1a4b7f3375461fcd52aa2fa..aaa93b6080e9036864f85f57a0ccfc84b70dcb8f 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright © 2019 Damyan Ivanov.
+  ~ Copyright © 2020 Damyan Ivanov.
   ~ This file is part of MoLe.
   ~ MoLe is free software: you can distribute it and/or modify it
   ~ under the term of the GNU General Public License as published by
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="line"
     android:thickness="1dp"
-    android:tint="?colorAccent">
+    android:tint="?colorSecondary"
+    >
     <stroke
         android:width="8dp"
-        android:color="?colorAccent"
+        android:color="?colorSecondary"
         android:dashWidth="16dp"
-        android:dashGap="8dp" />
+        android:dashGap="8dp"
+        />
 
 </shape>
\ No newline at end of file
index 91814d4eb6cd038de27ecd9c75cd786875139ca7..8213381c2278d605f2b319208c96c2321372f320 100644 (file)
@@ -1,25 +1,24 @@
 <!--
-  ~ Copyright Google Inc.
+  ~ Copyright © 2020 Damyan Ivanov.
+  ~ This file is part of MoLe.
+  ~ MoLe is free software: you can distribute it and/or modify it
+  ~ under the term of the GNU General Public License as published by
+  ~ the Free Software Foundation, either version 3 of the License, or
+  ~ (at your opinion), any later version.
   ~
-  ~ Licensed under the Apache License, version 2.0 ("the License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the license at:
+  ~ MoLe 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 terms for details.
   ~
-  ~ https://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distribution under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  ~
-  ~ Modified/adapted by Damyan Ivanov for MoLe
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
   -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
-    android:tint="?colorAccent"
+    android:tint="?colorSecondary"
     android:viewportWidth="24.0"
     android:viewportHeight="24.0">
     <path
index 1bb53740d59b60fb2c5fc49a2a825cc64268b733..5da24bc67bbad1e99e035fda68316922fe2d1b24 100644 (file)
@@ -19,7 +19,7 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
-    android:tint="?colorAccent"
+    android:tint="?colorSecondary"
     android:viewportWidth="24.0"
     android:viewportHeight="24.0">
     <path
index 51c798a6e49649f574317e6c292d46cd9b545017..a1cae6e4771701f803d0e30f5c5e4e8d854fcfa4 100644 (file)
@@ -19,7 +19,7 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
-    android:tint="?colorAccent"
+    android:tint="?colorSecondary"
     android:viewportWidth="24.0"
     android:viewportHeight="24.0">
     <path
index 76054bd52a37a3e43df1ba9bdca9d5e7d2ae85fa..d8a883cb4bb3b4f5d655f8e7bcef9fc68d4f6d25 100644 (file)
   ~ Modified/adapted by Damyan Ivanov for MoLe
   -->
 
-<vector android:autoMirrored="true" android:height="24dp"
-    android:tint="?colorAccent" android:viewportHeight="24.0"
-    android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="#FF000000" android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
+<vector android:autoMirrored="true"
+    android:height="24dp"
+    android:tint="?colorSecondary"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24dp"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"
+        />
 </vector>
index 434d09a31fcf548df428acc5afe7a334bf8590ff..59f4e89866149f29c821049387d0566325bbfe78 100644 (file)
@@ -19,7 +19,7 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
-    android:tint="?colorAccent"
+    android:tint="?colorSecondary"
     android:viewportWidth="24.0"
     android:viewportHeight="24.0">
     <path
index 14e4ec1b30de80b4401b976fa2ad8993ea688824..b739c6fff793a56ab28d24aa5de20a0657d72ec8 100644 (file)
   ~ Modified/adapted by Damyan Ivanov for MoLe
   -->
 
-<vector android:height="24dp" android:tint="?colorAccent"
-    android:viewportHeight="24.0" android:viewportWidth="24.0"
-    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="#FF000000" android:pathData="M12,8l-6,6 1.41,1.41L12,10.83l4.59,4.58L18,14z"/>
+<vector android:height="24dp"
+    android:tint="?colorSecondary"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24dp"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,8l-6,6 1.41,1.41L12,10.83l4.59,4.58L18,14z"
+        />
 </vector>
index 3796e519fe8e29223c9d2ca59c03ba5e5b9fb6b7..77cb545885bbc3c08e2888ec837e8e30b5714c87 100644 (file)
@@ -20,7 +20,7 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
-    android:tint="?colorAccent"
+    android:tint="?colorSecondary"
     android:viewportWidth="24.0"
     android:viewportHeight="24.0">
     <path
index bb0a23a7141f674d802c7688d38f14511d15de41..dd133ca95ecde70d581917673569abc4d84e057f 100644 (file)
   ~ Modified/adapted by Damyan Ivanov for MoLe
   -->
 
-<vector android:height="24dp" android:tint="?colorAccent"
-    android:viewportHeight="24.0" android:viewportWidth="24.0"
-    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="#FF000000" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+<vector android:height="24dp"
+    android:tint="?colorSecondary"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24dp"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"
+        />
 </vector>
index f830d8d6c62fde2ac821f1cd9d778bc2a67e1ddc..3d13250be921689d8e1a0dec5244dd41867a792c 100644 (file)
   ~ Modified/adapted by Damyan Ivanov for MoLe
   -->
 
-<vector android:height="24dp" android:tint="?colorAccent"
-    android:viewportHeight="24.0" android:viewportWidth="24.0"
-    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="#FF000000" android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+<vector android:height="24dp"
+    android:tint="?colorSecondary"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24dp"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"
+        />
 </vector>
index 9a43c8ae285f92153ce247e9f1bd87229c2d8416..5e7da9cd3a7a2b38d7b0f40748fcb8b69601f106 100644 (file)
@@ -19,7 +19,7 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
-    android:tint="?colorAccent"
+    android:tint="?colorSecondary"
     android:viewportWidth="24.0"
     android:viewportHeight="24.0">
     <path
index 7d836a1007b01c821dfeb518158a0f410938eeec..ca6b18014e906268e76e87da5fc8aba5e1ff5d5a 100644 (file)
   ~ Modified/adapted by Damyan Ivanov for MoLe
   -->
 
-<vector android:height="24dp" android:tint="?colorAccent"
-    android:viewportHeight="24.0" android:viewportWidth="24.0"
-    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="#FF000000" android:pathData="M12,5.83L15.17,9l1.41,-1.41L12,3 7.41,7.59 8.83,9 12,5.83zM12,18.17L8.83,15l-1.41,1.41L12,21l4.59,-4.59L15.17,15 12,18.17z"/>
+<vector android:height="24dp"
+    android:tint="?colorSecondary"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24dp"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,5.83L15.17,9l1.41,-1.41L12,3 7.41,7.59 8.83,9 12,5.83zM12,18.17L8.83,15l-1.41,1.41L12,21l4.59,-4.59L15.17,15 12,18.17z"
+        />
 </vector>
index a303787fe4759060502e8d10267211abe483d44b..df6bcb0691fe20a901ce6ab64bae076182bf54cc 100644 (file)
@@ -19,7 +19,7 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
-    android:tint="?colorAccent"
+    android:tint="?colorSecondary"
     android:viewportWidth="24.0"
     android:viewportHeight="24.0">
     <path
index 9213f52a5166eb4ff24bc193396823206be9c189..9b8047c77013f70865e4ef11f4fc46c362f468f1 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright © 2019 Damyan Ivanov.
+  ~ Copyright © 2020 Damyan Ivanov.
   ~ This file is part of MoLe.
   ~ MoLe is free software: you can distribute it and/or modify it
   ~ under the term of the GNU General Public License as published by
@@ -62,7 +62,7 @@
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|start"
         android:layout_margin="@dimen/fab_margin"
-        app:backgroundTint="?colorAccent"
+        app:backgroundTint="?colorSecondary"
         app:layout_anchor="@+id/profile_detail_container"
         app:layout_anchorGravity="top|end"
         app:srcCompat="@drawable/ic_save_white_24dp" />
index 73e58459b01ce141237da8694caab028139135a6..1311465aef0f5335e187e47dc938a3891ac86ff7 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright © 2019 Damyan Ivanov.
+  ~ Copyright © 2020 Damyan Ivanov.
   ~ This file is part of MoLe.
   ~ MoLe is free software: you can distribute it and/or modify it
   ~ under the term of the GNU General Public License as published by
@@ -67,7 +67,7 @@
         android:padding="@dimen/fab_margin"
         android:tint="?android:attr/colorBackground"
         android:visibility="visible"
-        app:backgroundTint="?colorAccent"
+        app:backgroundTint="?colorSecondary"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:srcCompat="@drawable/ic_save_white_24dp" />
index 1044d483aa0ad50894e74c06b2497cb2c777b72b..e95267ff2708919ace35660f275662bdd29b67bb 100644 (file)
@@ -40,7 +40,7 @@
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:indeterminate="true"
-        android:progressTint="?attr/colorAccent"
+        android:progressTint="?attr/colorPrimary"
         app:layout_constraintBottom_toTopOf="@id/textView4"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
index 4286e9f45d0ef087699c1b7eacca38fdceca83e9..70f3544994fe7ab597edbb952e27e3600c37deee 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright © 2019 Damyan Ivanov.
+  ~ Copyright © 2020 Damyan Ivanov.
   ~ This file is part of MoLe.
   ~ MoLe is free software: you can distribute it and/or modify it
   ~ under the term of the GNU General Public License as published by
@@ -77,7 +77,7 @@
             android:layout_marginTop="24dp"
             android:layout_marginEnd="8dp"
             android:layout_marginBottom="8dp"
-            android:backgroundTint="?colorAccent"
+            android:backgroundTint="?colorSecondary"
             android:textColor="@color/design_default_color_on_primary"
             android:drawablePadding="16dp"
             android:text="@string/create_profile_label"
index 609e1c224fecf4e486b594736662c4e87f0c74ce..60b1b1e001104c2053f4efb2960de1df7f4b150a 100644 (file)
@@ -51,7 +51,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:background="@drawable/ic_clear_accent_24dp"
-            android:backgroundTint="?colorAccent"
+            android:backgroundTint="?colorSecondary"
             android:clickable="true"
             android:focusable="true" />
 
index 23920c54bd29fcf3ac02d22295894b0aebdcbf1c..25002e743e14d5d71f16e6ea6381453c0e9bdbfb 100644 (file)
@@ -2,6 +2,151 @@
 
 use strict; use warnings; use utf8::all;
 use autodie;
+use Carp;
+
+use Type::Tiny;
+use Types::Standard qw(StrictNum);
+my $colorValue = Type::Tiny->new(
+    parent     => StrictNum,
+    constraint => '($_ >= 0) and ($_ <= 1)'
+);
+
+package Color::HSL;
+use Moo;
+use namespace::clean;
+
+has h => ( is => 'ro', isa => $colorValue );
+has s => ( is => 'ro', isa => $colorValue );
+has l => ( is => 'ro', isa => $colorValue );
+
+package Color::sRGB;
+use Moo;
+use Types::Standard qw(StrictNum);
+use List::MoreUtils qw(minmax);
+
+use namespace::clean;
+
+use overload '""' => 'hexTuple';
+
+has r => ( is => 'ro', isa => $colorValue );
+has g => ( is => 'ro', isa => $colorValue );
+has b => ( is => 'ro', isa => $colorValue );
+
+sub hexTuple {
+    my $self = shift;
+    return sprintf( '%02x%02x%02x',
+        int( $self->r * 255 + 0.5 ),
+        int( $self->g * 255 + 0.5 ),
+        int( $self->b * 255 + 0.5 ) );
+}
+
+# https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
+sub _norm {
+    return $_[0] / 12.92 if $_[0] <= 0.03928;
+    return ( ( $_[0] + 0.055 ) / 1.055 )**2.4;
+}
+
+sub relativeLuminance {
+    my $self = shift;
+
+    return 0.2126 * _norm( $self->r ) + 0.7152 * _norm( $self->g )
+        + 0.0722 * _norm( $self->b );
+}
+
+sub BLACK {
+    shift->new( r => 0, g => 0, b => 0 );
+}
+
+sub WHITE {
+    shift->new( r => 1, g => 1, b => 1 );
+}
+
+my @hexDigit = split //, '0123456789abcdef';
+my %hexValue =
+    map( ( lc( $hexDigit[$_] ) => $_, uc( $hexDigit[$_] ) => $_ ), 0 .. 15 );
+
+sub fromHexTriplet {
+    my ( $class, $triplet ) = @_;
+
+    my @d = $triplet =~ /^#?(.)(.)(.)(.)(.)(.)$/
+        or die "'$triplet' is not a valid colour triplet";
+
+    return $class->new(
+        r => ( 16 * $hexValue{ $d[0] } + $hexValue{ $d[1] } ) / 255.0,
+        g => ( 16 * $hexValue{ $d[2] } + $hexValue{ $d[3] } ) / 255.0,
+        b => ( 16 * $hexValue{ $d[4] } + $hexValue{ $d[5] } ) / 255.0
+    );
+}
+
+# https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
+sub fromHSL {
+    my ( $class, $hsl ) = @_;
+    my $hue = $hsl->h;
+    my $sat = $hsl->s;
+    my $lig = $hsl->l;
+
+    my $h       = ( $hue * 6.0 );
+    my $c       = ( 1 - abs( 2.0 * $lig - 1 ) ) * $sat;
+    my $h_mod_2 = $h - 2.0 * int( $h / 2 );
+    my $x       = $c * ( 1 - abs( $h_mod_2 - 1 ) );
+    my ( $r, $g, $b );
+    my $m = $lig - $c / 2.0;
+
+    return $class->new( r => $c + $m, g => $x + $m, b => 0 + $m )
+        if $h < 1 or $h == 6;
+    return $class->new( r => $x + $m, g => $c + $m, b => 0 + $m )  if $h < 2;
+    return $class->new( r => 0 + $m,  g => $c + $m, b => $x + $m ) if $h < 3;
+    return $class->new( r => 0 + $m,  g => $x + $m, b => $c + $m ) if $h < 4;
+    return $class->new( r => $x + $m, g => 0 + $m,  b => $c + $m ) if $h < 5;
+    return $class->new( r => $c + $m, g => 0 + $m,  b => $x + $m ) if $h < 6;
+
+    die $h;
+}
+
+sub toHSL {
+    my $self = shift;
+
+    my ( $m, $M ) = minmax( $self->r, $self->g, $self->b );
+
+    my $C = $M - $m;
+
+    my $h;
+    if ( $C == 0 ) {
+        $h = 0;
+    }
+    elsif ( $self->r == $M ) {
+        $h = ( $self->g - $self->b ) / $C;
+        $h -= 6 * int( $h / 6.0 );
+    }
+    elsif ( $self->g == $M ) {
+        $h = ( $self->b - $self->r ) / $C + 2;
+    }
+    elsif ( $self->b == $M ) {
+        $h = ( $self->r - $self->g ) / $C + 4;
+    }
+    else { die "$C, $M, $self"; }
+
+    my $H = 60 * $h;
+    my $L = ( $M + $m ) / 2;
+
+    my $S = ( $L <= 0.5 ) ? $C / ( 2 * $L ) : $C / ( 2 - 2 * $L );
+
+    return Color::HSL->new( h => $H/360.0, s => $S, l => $L );
+}
+
+sub contrastWith {
+    my ( $self, $ref ) = @_;
+
+    my $myL = $self->relativeLuminance;
+    my $refL = $ref->relativeLuminance;
+
+    my $ratio = ( $myL + 0.05 ) / ( $refL + 0.05 );
+    $ratio = 1 / $ratio if $ratio < 1;
+    return $ratio;
+}
+
+package MAIN;
+
 use Math::Trig;
 use File::Basename qw(basename dirname);
 use File::Temp qw(tempfile);
@@ -37,149 +182,96 @@ sub hsvHex {
        die $h;
 }
 
-# https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
 sub hslHex {
-       my ($hue, $sat, $lig ) = @_;
-       $hue = $hue / 360.0;
-       my $h = ($hue * 6.0);
-       my $c = (1 - abs(2.0*$lig - 1)) * $sat;
-       my $h_mod_2 = $h - 2.0*int($h/2);
-       my $x = $c * (1 - abs($h_mod_2 - 1));
-       my ($r, $g, $b);
-       my $m = $lig - $c / 2.0;
-
-       return hexTuple($c + $m, $x + $m,  0 + $m) if $h < 1 or $h == 6;
-       return hexTuple($x + $m, $c + $m,  0 + $m) if $h < 2;
-       return hexTuple( 0 + $m, $c + $m, $x + $m) if $h < 3;
-       return hexTuple( 0 + $m, $x + $m, $c + $m) if $h < 4;
-       return hexTuple($x + $m,  0 + $m, $c + $m) if $h < 5;
-       return hexTuple($c + $m,  0 + $m, $x + $m) if $h < 6;
-
-       die $h;
-}
-
-my @hexDigit = split //, '0123456789abcdef';
-my %hexValue = map(
-               (lc($hexDigit[$_]) => $_, uc($hexDigit[$_]) => $_ ),
-               0..15 );
-
-sub min {
-       my $min = shift;
-
-       for (@_) { $min = $_ if $_ < $min }
-
-       return $min;
-}
-
-sub max {
-       my $max = shift;
-
-       for (@_) { $max = $_ if $_ > $max }
-
-       return $max;
+    my ( $h, $s, $l ) = @_;
+    return Color::sRGB->fromHSL(
+        Color::HSL->new( { h => $h / 360.0, s => $s, l => $l } ) )->hexTuple;
 }
 
-sub hexToRGB {
-       my $hexTriplet = shift;
-
-       my @d = $hexTriplet =~ /^#?(.)(.)(.)(.)(.)(.)/;
-
-       return (16 * $hexValue{$d[0]} + $hexValue{$d[1]},
-               16 * $hexValue{$d[2]} + $hexValue{$d[3]},
-               16 * $hexValue{$d[4]} + $hexValue{$d[5]});
-}
-
-sub hexToHSL {
-       my $hexTriplet = shift;
-
-       my ($r,$g,$b) = hexToRGB($hexTriplet);
-        #warn "$hexTriplet -> $r:$g:$b";
-
-       for ($r, $g, $b ) { $_ = $_ / 255.0 }
-
-       my $M = max($r, $g, $b);
-       my $m = min($r, $g, $b);
-       my $C = $M - $m;
-
-       my $h;
-       if ($C == 0) {
-               $h = 0;
-       }
-       elsif ( $r == $M ) {
-               $h = ($g-$b)/$C;
-               $h -= 6*int($h/6.0);
-       }
-       elsif ( $g == $M ) {
-               $h = ($b-$r)/$C + 2;
-       }
-       elsif ( $b == $M ) {
-               $h = ($r-$g)/$C + 4;
-       }
-       else { die "$C, $M, $r, $g, $b"; }
-
-       my $H = 60 * $h;
-       my $L = ($M + $m) / 2;
-
-       my $S = ( $L <= 0.5 ) ? $C/(2*$L) : $C / (2-2*$L);
-
-       return( $H, $S, $L );
-}
+warn sprintf("%s: %2.1f\n", 'white', Color::sRGB->WHITE->relativeLuminance);
+warn sprintf("%s: %2.1f\n", 'black', Color::sRGB->BLACK->relativeLuminance);
+warn sprintf( "%s: %2.1f\n",
+    '50% gray',
+    Color::sRGB->new( r => 0.5, g => 0.5, b => 0.5 )->relativeLuminance );
 
 my $baseColor = '#935ff2';
-my $baseColorHSV = [ hexToHSL($baseColor) ];
-my $baseColorHue = $baseColorHSV->[0];
-warn sprintf( '%s → H:%1.4f S:%1.4f V:%1.4f', $baseColor, @$baseColorHSV );
-my @target = hexToRGB($baseColor);
-my ($best, $min_dist);
-for (my $s = 0.50; $s < 0.90; $s += 0.001) {
-       for ( my $l = 0.50; $l <= 0.80; $l += 0.001 ) {
-               my $hexColor = hslHex($baseColorHue, $s, $l);
-               my ($r,$g,$b) = hexToRGB( $hexColor );
-               my $dist = abs($r-$target[0])
-                        + abs($g-$target[1])
-                        + abs($b-$target[2]);
-               if (not defined($best) or $dist < $min_dist) {
-                       $best = [ $s, $l, $hexColor ];
-                       $min_dist = $dist;
-               }
-       }
-}
-warn sprintf( 's%1.3f, l%1.3f → %s',
-       @$best );
+my $baseColorRGB = Color::sRGB->fromHexTriplet($baseColor);
+my $baseColorHSL = $baseColorRGB->toHSL;
+my $baseColorHue = $baseColorHSL->h;
+warn sprintf(
+    '%s → H:%1.4f S:%1.4f L:%1.4f (luminance: %1.4f; cW: %1.4f, cB: %1.4f)',
+    $baseColor,
+    360 * $baseColorHSL->h,
+    $baseColorHSL->s,
+    $baseColorHSL->l,
+    $baseColorRGB->relativeLuminance,
+    $baseColorRGB->contrastWith( Color::sRGB->WHITE ),
+    $baseColorRGB->contrastWith( Color::sRGB->BLACK ),
+);
+# # find best saturation/lightness for the desired color
+# # test if the above is correct
+# my ($best, $min_dist);
+# for (my $s = 0.50; $s < 0.90; $s += 0.001) {
+#     for ( my $l = 0.50; $l <= 0.80; $l += 0.001 ) {
+#         my $color = Color::sRGB->fromHSL(
+#             Color::HSL->new( h => $baseColorHue, s => $s, l => $l ) );
+#         my $dist =
+#               abs( $color->r - $baseColorRGB->r )
+#             + abs( $color->g - $baseColorRGB->g )
+#             + abs( $color->b - $baseColorRGB->b );
+#         if ( not defined($best) or $dist < $min_dist ) {
+#             $best     = [ $s, $l, $color ];
+#             $min_dist = $dist;
+#         }
+#     }
+# }
+# warn sprintf( 's%1.3f, l%1.3f → %s', @$best );
 
 my $baseTheme = "AppTheme";
 
 use constant STEP_DEGREES => 5;
 
-# # hsb
-# for( my $hue = 0; $hue < 360; $hue += STEP_DEGREES ) {
-#      printf "<style name=\"%s.%03d\" parent=\"%s\">\n",
-#              $baseTheme, $hue, $baseTheme;
-#      printf "  <item name=\"colorPrimary\">#%s</item>\n",
-#                      hsvHex($hue/360.0, 0.61, 0.95);
-#      printf "  <item name=\"colorPrimaryDark\">#%s</item>\n",
-#                      hsvHex($hue/360.0, 0.86, 0.55);
-#      printf "  <item name=\"colorAccent\">#%s</item>\n",
-#                      hsvHex(($hue-4)/360.0, 0.72, 0.82);
-#      printf "  <item name=\"table_row_dark_bg\">#28%s</item>\n",
-#                      hsvHex($hue/360.0, 0.65, 0.83);
-#      printf "  <item name=\"table_row_light_bg\">#28%s</item>\n",
-#                      hsvHex($hue/360.0, 0.20, 1.00);
-#      printf "  <item name=\"header_border\">#80%s</item>\n",
-#                      hsvHex(($hue+6)/360.0, 0.86, 0.55);
-#      printf "</style>\n";
-# }
-
-# HSL
 sub outputThemes {
-       my $out = shift;
-       my $baseIndent = shift;
+    my $out        = shift;
+    my $baseIndent = shift;
+    $out->print("\n");
+    $out->print(
+        hslStyleForHue( $DEFAULT_HUE, $baseTheme, $baseIndent, 'default' ) );
+    for ( my $hue = 0; $hue < 360; $hue += STEP_DEGREES ) {
         $out->print("\n");
-        $out->print(hslStyleForHue($DEFAULT_HUE, $baseTheme, $baseIndent, 'default'));
-       for( my $hue = 0; $hue < 360; $hue += STEP_DEGREES ) {
-               $out->print("\n");
-               $out->print(hslStyleForHue($hue, $baseTheme, $baseIndent));
-       }
+        $out->print( hslStyleForHue( $hue, $baseTheme, $baseIndent ) );
+    }
+}
+
+sub bestLightnessForHue {
+    my ( $h, $s ) = @_;
+    my $targetContrast = 4.07;
+    my $white = Color::sRGB->WHITE;
+    my $bestLightness;
+    my $bestContrast;
+    for ( my $l = 0; $l < 1; $l += 0.002 ) {
+        my $contrast = Color::sRGB->fromHSL(
+            Color::HSL->new( { h => $h, s => $s, l => $l } ) )
+            ->contrastWith($white);
+
+        if ( defined $bestLightness ) {
+            if (abs( $contrast - $targetContrast ) <
+                abs( $bestContrast - $targetContrast ) )
+            {
+                $bestLightness = $l;
+                $bestContrast  = $contrast;
+            }
+        }
+        else {
+            $bestLightness = $l;
+            $bestContrast = $contrast;
+        }
+    }
+
+    warn sprintf(
+        "Found best lightness for hue %1.4f: %1.4f (contrast %1.4f)\n",
+        360 * $h, $bestLightness, $bestContrast );
+    return $bestLightness;
 }
 
 sub hslStyleForHue {
@@ -189,12 +281,12 @@ sub hslStyleForHue {
         my $subTheme = shift // sprintf('%03d', $hue);
 
        my %lQ = (
-               0   => 0.450,   # red
-               60  => 0.400,   # yellow
-               120 => 0.400,   # green
-               180 => 0.390,   # cyan
-               240 => 0.745,   # blue
-               300 => 0.505,   # magenta
+               0   => 0.550,   # red
+               60  => 0.250,   # yellow
+               120 => 0.290,   # green
+               180 => 0.300,   # cyan
+               240 => 0.680,   # blue
+               300 => 0.450,   # magenta
        );
        $lQ{360} = $lQ{0};
 
@@ -204,8 +296,11 @@ sub hslStyleForHue {
        $y0 = $lQ{$x0};
        $y1 = $lQ{$x1};
 
+        my $S = 0.8497;
+
        # linear interpolation
-       my $l1 = $y0 + 1.0 * ( $hue - $x0 ) * ( $y1 - $y0 ) / ( $x1 - $x0 );
+        #my $l1 = $y0 + 1.0 * ( $hue - $x0 ) * ( $y1 - $y0 ) / ( $x1 - $x0 );
+        my $l1 = bestLightnessForHue( $hue / 360.0, $S );
 
        my $l2 = $l1 * 0.80;
        my $l3 = $opt_night ? 0.150 : 0.950;
@@ -225,15 +320,15 @@ sub hslStyleForHue {
 #                $result .= "$indent<item name=\"windowNoTitle\">true</item>\n";
 #                $result .= "$indent<item name=\"textColor\">#757575</item>\n";
         }
-        my $S = 0.845;
+
+        $result .= sprintf "$indent<!-- h: %1.4f s:%1.4f l:%1.4f -->\n", $hue,
+            $S, $l1 if 0;
         $result .= sprintf "$indent<item name=\"%s\">#%s</item>\n",
             'colorPrimary', hslHex( $hue, $S, $l1 );
         $result .= sprintf "$indent<item name=\"%s\">#00%s</item>\n",
             'colorPrimaryTransparent', hslHex( $hue, $S, $l1 );
         $result .= sprintf "$indent<item name=\"%s\">#%s</item>\n",
             'colorSecondary', hslHex( $hue, $S, $l2 );
-        $result .= sprintf "$indent<item name=\"%s\">#%s</item>\n",
-            'colorAccent', hslHex( $hue, $S, $l2 );
         $result .= sprintf "$indent<item name=\"%s\">#%s</item>\n",
             'colorPrimaryDark', hslHex( $hue, $S, $l2 );
         $result .= sprintf "$indent<item name=\"%s\">#%s</item>\n",