From 0eb3c8838868ea2449698d32dfa77f575e1acb37 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Mon, 2 Mar 2020 08:25:59 -0600 Subject: [PATCH] Auto desaturate palette colors in dark mode --- .../activities/BaseListSettingsActivity.java | 3 +- .../org/tasks/dialogs/ColorPalettePicker.kt | 40 +++--- .../org/tasks/dialogs/ColorPickerAdapter.kt | 1 + .../preferences/fragments/ScrollableWidget.kt | 8 +- .../java/org/tasks/themes/ThemeCache.java | 2 +- .../java/org/tasks/themes/ThemeColor.java | 127 +++++++++++++----- .../java/org/tasks/widget/TasksWidget.java | 2 +- app/src/main/res/values-night/colors.xml | 22 --- app/src/main/res/values/colors.xml | 41 ------ 9 files changed, 118 insertions(+), 128 deletions(-) diff --git a/app/src/main/java/org/tasks/activities/BaseListSettingsActivity.java b/app/src/main/java/org/tasks/activities/BaseListSettingsActivity.java index 55c3db125..8f7c06fc0 100644 --- a/app/src/main/java/org/tasks/activities/BaseListSettingsActivity.java +++ b/app/src/main/java/org/tasks/activities/BaseListSettingsActivity.java @@ -21,6 +21,7 @@ import butterknife.OnClick; import javax.inject.Inject; import org.tasks.R; import org.tasks.dialogs.ColorPalettePicker; +import org.tasks.dialogs.ColorPickerAdapter.Palette; import org.tasks.dialogs.ColorWheelPicker; import org.tasks.dialogs.DialogBuilder; import org.tasks.dialogs.IconPickerDialog.IconPickerCallback; @@ -123,7 +124,7 @@ public abstract class BaseListSettingsActivity extends ThemedInjectingAppCompatA @OnClick(R.id.color_row) protected void showThemePicker() { - ColorPalettePicker.Companion.newColorPalette(null, 0, selectedColor) + ColorPalettePicker.Companion.newColorPalette(null, 0, selectedColor, Palette.COLORS) .show(getSupportFragmentManager(), FRAG_TAG_COLOR_PICKER); } diff --git a/app/src/main/java/org/tasks/dialogs/ColorPalettePicker.kt b/app/src/main/java/org/tasks/dialogs/ColorPalettePicker.kt index 2d55545c0..a13e33aee 100644 --- a/app/src/main/java/org/tasks/dialogs/ColorPalettePicker.kt +++ b/app/src/main/java/org/tasks/dialogs/ColorPalettePicker.kt @@ -17,6 +17,7 @@ import org.tasks.Callback import org.tasks.R import org.tasks.billing.Inventory import org.tasks.billing.PurchaseDialog +import org.tasks.dialogs.ColorPickerAdapter.Palette import org.tasks.dialogs.ColorWheelPicker.Companion.newColorWheel import org.tasks.injection.DialogFragmentComponent import org.tasks.injection.InjectingDialogFragment @@ -36,15 +37,7 @@ class ColorPalettePicker : InjectingDialogFragment() { fun newColorPalette( target: Fragment?, rc: Int, - selected: Int - ): ColorPalettePicker { - return newColorPalette(target, rc, selected, ColorPickerAdapter.Palette.COLORS) - } - - fun newColorPalette( - target: Fragment?, - rc: Int, - palette: ColorPickerAdapter.Palette + palette: Palette ): ColorPalettePicker { return newColorPalette(target, rc, 0, palette) } @@ -53,7 +46,7 @@ class ColorPalettePicker : InjectingDialogFragment() { target: Fragment?, rc: Int, selected: Int, - palette: ColorPickerAdapter.Palette + palette: Palette = Palette.COLORS ): ColorPalettePicker { val args = Bundle() args.putSerializable(EXTRA_PALETTE, palette) @@ -81,26 +74,29 @@ class ColorPalettePicker : InjectingDialogFragment() { @BindView(R.id.icons) lateinit var recyclerView: RecyclerView - lateinit var colors: List - lateinit var palette: ColorPickerAdapter.Palette + private lateinit var colors: List + lateinit var palette: Palette var callback: ColorPickedCallback? = null override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val inflater = LayoutInflater.from(context) val view = inflater.inflate(R.layout.dialog_icon_picker, null) ButterKnife.bind(this, view) - palette = arguments!!.getSerializable(EXTRA_PALETTE) as ColorPickerAdapter.Palette + palette = arguments!!.getSerializable(EXTRA_PALETTE) as Palette colors = when (palette) { - ColorPickerAdapter.Palette.COLORS -> ThemeColor.COLORS.mapIndexed { index, color -> - ThemeColor(context, index, ContextCompat.getColor(context!!, color)) + Palette.COLORS -> ThemeColor.COLORS.mapIndexed { index, color -> + ThemeColor(context, index, ContextCompat.getColor(context!!, color), true) } - ColorPickerAdapter.Palette.ACCENTS -> ThemeAccent.ACCENTS.mapIndexed { index, _ -> + Palette.ACCENTS -> ThemeAccent.ACCENTS.mapIndexed { index, _ -> ThemeAccent(context, index) } - ColorPickerAdapter.Palette.LAUNCHERS -> ThemeColor.LAUNCHER_COLORS.mapIndexed { index, color -> - ThemeColor(context, index, ContextCompat.getColor(context!!, color)) + Palette.LAUNCHERS -> ThemeColor.LAUNCHER_COLORS.mapIndexed { index, color -> + ThemeColor(context, index, ContextCompat.getColor(context!!, color), false) + } + Palette.WIDGET -> ThemeColor.COLORS.mapIndexed { index, color -> + ThemeColor(context, index, ContextCompat.getColor(context!!, color), false) } - ColorPickerAdapter.Palette.WIDGET_BACKGROUND -> themeCache.widgetThemes + Palette.WIDGET_BACKGROUND -> themeCache.widgetThemes } val iconPickerAdapter = ColorPickerAdapter( @@ -114,7 +110,7 @@ class ColorPalettePicker : InjectingDialogFragment() { dialogBuilder .newDialog() .setView(view) - if (palette == ColorPickerAdapter.Palette.COLORS) { + if (palette == Palette.COLORS || palette == Palette.WIDGET) { builder.setNeutralButton(R.string.color_wheel) { _, _ -> val selected = arguments?.getInt(EXTRA_SELECTED) ?: 0 newColorWheel(targetFragment, targetRequestCode, selected) @@ -141,8 +137,8 @@ class ColorPalettePicker : InjectingDialogFragment() { private fun onSelected(index: Int) { val result = when (palette) { - ColorPickerAdapter.Palette.COLORS -> - (colors.find { it.index == index } as ThemeColor).primaryColor + Palette.COLORS, Palette.WIDGET -> + (colors.find { it.index == index } as ThemeColor).originalColor else -> index } dialog?.dismiss() diff --git a/app/src/main/java/org/tasks/dialogs/ColorPickerAdapter.kt b/app/src/main/java/org/tasks/dialogs/ColorPickerAdapter.kt index aad1cbc7d..830f632dd 100644 --- a/app/src/main/java/org/tasks/dialogs/ColorPickerAdapter.kt +++ b/app/src/main/java/org/tasks/dialogs/ColorPickerAdapter.kt @@ -18,6 +18,7 @@ class ColorPickerAdapter( COLORS, ACCENTS, LAUNCHERS, + WIDGET, WIDGET_BACKGROUND } diff --git a/app/src/main/java/org/tasks/preferences/fragments/ScrollableWidget.kt b/app/src/main/java/org/tasks/preferences/fragments/ScrollableWidget.kt index 36b4d016c..9c1388343 100644 --- a/app/src/main/java/org/tasks/preferences/fragments/ScrollableWidget.kt +++ b/app/src/main/java/org/tasks/preferences/fragments/ScrollableWidget.kt @@ -11,10 +11,10 @@ import com.todoroo.astrid.api.Filter import org.tasks.LocalBroadcastManager import org.tasks.R import org.tasks.activities.FilterSelectionActivity -import org.tasks.dialogs.ColorWheelPicker import org.tasks.dialogs.ColorPalettePicker import org.tasks.dialogs.ColorPalettePicker.Companion.newColorPalette -import org.tasks.dialogs.ColorPickerAdapter +import org.tasks.dialogs.ColorPickerAdapter.Palette +import org.tasks.dialogs.ColorWheelPicker import org.tasks.injection.FragmentComponent import org.tasks.injection.InjectingPreferenceFragment import org.tasks.locale.Locale @@ -80,7 +80,7 @@ class ScrollableWidget : InjectingPreferenceFragment() { findPreference(R.string.p_widget_theme) .setOnPreferenceClickListener { - newColorPalette(this, REQUEST_THEME_SELECTION, ColorPickerAdapter.Palette.WIDGET_BACKGROUND) + newColorPalette(this, REQUEST_THEME_SELECTION, Palette.WIDGET_BACKGROUND) .show(parentFragmentManager, FRAG_TAG_COLOR_PICKER) false } @@ -88,7 +88,7 @@ class ScrollableWidget : InjectingPreferenceFragment() { val colorPreference = findPreference(R.string.p_widget_color_v2) colorPreference.dependency = showHeader.key colorPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener { - newColorPalette(this, REQUEST_COLOR_SELECTION, widgetPreferences.color) + newColorPalette(this, REQUEST_COLOR_SELECTION, widgetPreferences.color, Palette.WIDGET) .show(parentFragmentManager, FRAG_TAG_COLOR_PICKER) false } diff --git a/app/src/main/java/org/tasks/themes/ThemeCache.java b/app/src/main/java/org/tasks/themes/ThemeCache.java index ec7c9d8d9..61a0b48d6 100644 --- a/app/src/main/java/org/tasks/themes/ThemeCache.java +++ b/app/src/main/java/org/tasks/themes/ThemeCache.java @@ -116,7 +116,7 @@ public class ThemeCache { public ThemeColor getLauncherColor(int index) { return new ThemeColor( - context, index, ContextCompat.getColor(context, ThemeColor.LAUNCHER_COLORS[index])); + context, index, ContextCompat.getColor(context, ThemeColor.LAUNCHER_COLORS[index]), false); } public ThemeColor getUntaggedColor() { diff --git a/app/src/main/java/org/tasks/themes/ThemeColor.java b/app/src/main/java/org/tasks/themes/ThemeColor.java index 565069b4a..163a342b0 100644 --- a/app/src/main/java/org/tasks/themes/ThemeColor.java +++ b/app/src/main/java/org/tasks/themes/ThemeColor.java @@ -1,5 +1,6 @@ package org.tasks.themes; +import static com.google.common.collect.Maps.newHashMap; import static com.todoroo.andlib.utility.AndroidUtilities.atLeastLollipop; import static com.todoroo.andlib.utility.AndroidUtilities.atLeastMarshmallow; import static com.todoroo.andlib.utility.AndroidUtilities.atLeastOreo; @@ -21,8 +22,10 @@ import androidx.annotation.RequiresApi; import androidx.appcompat.widget.Toolbar; import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; +import androidx.core.os.ParcelCompat; import androidx.drawerlayout.widget.DrawerLayout; import com.google.android.material.appbar.CollapsingToolbarLayout; +import java.util.Map; import org.tasks.R; import org.tasks.dialogs.ColorPalettePicker.Pickable; import timber.log.Timber; @@ -79,27 +82,27 @@ public class ThemeColor implements Pickable { public static final int[] COLORS = new int[] { - R.color.theme_blue_grey, - R.color.theme_dark_grey, - R.color.theme_red, - R.color.theme_pink, - R.color.theme_purple, - R.color.theme_deep_purple, - R.color.theme_indigo, - R.color.theme_blue, - R.color.theme_light_blue, - R.color.theme_cyan, - R.color.theme_teal, - R.color.theme_green, - R.color.theme_light_green, - R.color.theme_lime, - R.color.theme_yellow, - R.color.theme_amber, - R.color.theme_orange, - R.color.theme_deep_orange, - R.color.theme_brown, - R.color.theme_grey, - R.color.theme_day_night + R.color.blue_grey_500, + R.color.grey_900, + R.color.red_500, + R.color.pink_500, + R.color.purple_500, + R.color.deep_purple_500, + R.color.indigo_500, + R.color.blue_500, + R.color.light_blue_500, + R.color.cyan_500, + R.color.teal_500, + R.color.green_500, + R.color.light_green_500, + R.color.lime_500, + R.color.yellow_500, + R.color.amber_500, + R.color.orange_500, + R.color.deep_orange_500, + R.color.brown_500, + R.color.grey_500, + R.color.white_100 }; public static final int[] LAUNCHER_COLORS = @@ -138,26 +141,72 @@ public class ThemeColor implements Pickable { return new ThemeColor[size]; } }; + + private static final int WHITE = -1; + private static final int BLACK = -16777216; + + private static final Map colorMap = newHashMap(); + + static { + colorMap.put(-10453621, -5194043); // blue_grey + colorMap.put(-12434878, -14606047); // grey + colorMap.put(-769226, -1074534); // red + colorMap.put(-1499549, -749647); // pink + colorMap.put(-6543440, -3238952); // purple + colorMap.put(-10011977, -5005861); // deep purple + colorMap.put(-12627531, -6313766); // indigo + colorMap.put(-14575885, -7288071); // blue + colorMap.put(-16537100, -8268550); // light blue + colorMap.put(-16728876, -8331542); // cyan + colorMap.put(-16738680, -8336444); // teal + colorMap.put(-11751600, -5908825); // green + colorMap.put(-7617718, -3808859); // light green + colorMap.put(-3285959, -1642852); // lime + colorMap.put(-5317, -2659); // yellow + colorMap.put(-16121, -8062); // amber + colorMap.put(-26624, -13184); // orange + colorMap.put(-43230, -21615); // deep orange + colorMap.put(-8825528, -4412764); // brown + colorMap.put(-6381922, -1118482); // grey + colorMap.put(-1, -16777216); // white & black + } + private final int index; + private final int original; private final int colorOnPrimary; private final int colorPrimary; private final int colorPrimaryVariant; private final boolean isDark; public ThemeColor(Context context, int color) { - this(context, -1, color == 0 ? ContextCompat.getColor(context, R.color.blue_500) : color); + this(context, -1, color == 0 ? ContextCompat.getColor(context, R.color.blue_500) : color, true); } - public ThemeColor(Context context, int index, int colorPrimary) { + public ThemeColor(Context context, int index, int color, boolean adjustColor) { this.index = index; - colorPrimary |= 0xFF000000; // remove alpha - this.colorPrimary = colorPrimary; - this.colorPrimaryVariant = ColorUtil.darken(colorPrimary, 12); - - int whiteText = context.getResources().getColor(R.color.white_100); - double contrast = ColorUtils.calculateContrast(whiteText, colorPrimary); - this.isDark = contrast < 3; - colorOnPrimary = isDark ? context.getResources().getColor(R.color.black_87) : whiteText; + color |= 0xFF000000; // remove alpha + original = color; + if (adjustColor && context.getResources().getBoolean(R.bool.is_dark)) { + colorPrimary = desaturate(color); + } else { + colorPrimary = color; + } + colorPrimaryVariant = ColorUtil.darken(colorPrimary, 12); + + double contrast = ColorUtils.calculateContrast(WHITE, colorPrimary); + isDark = contrast < 3; + colorOnPrimary = isDark ? context.getResources().getColor(R.color.black_87) : WHITE; + } + + private int desaturate(int color) { + if (colorMap.containsKey(color)) { + //noinspection ConstantConditions + return colorMap.get(color); + } else if (color == WHITE) { + return BLACK; // white -> black + } else { + return color; + } } private ThemeColor(Parcel source) { @@ -165,7 +214,8 @@ public class ThemeColor implements Pickable { colorOnPrimary = source.readInt(); colorPrimary = source.readInt(); colorPrimaryVariant = source.readInt(); - isDark = source.readInt() == 1; + isDark = ParcelCompat.readBoolean(source); + original = source.readInt(); } public static ThemeColor newThemeColor(Context context, int color) { @@ -283,7 +333,7 @@ public class ThemeColor implements Pickable { @Override public boolean isFree() { - switch (colorPrimary) { + switch (original) { case -14575885: // blue_500 case -10453621: // blue_grey_500 case -14606047: // grey_900 @@ -298,6 +348,10 @@ public class ThemeColor implements Pickable { return index; } + public int getOriginalColor() { + return original; + } + public int getPrimaryColor() { return colorPrimary; } @@ -324,7 +378,8 @@ public class ThemeColor implements Pickable { dest.writeInt(colorOnPrimary); dest.writeInt(colorPrimary); dest.writeInt(colorPrimaryVariant); - dest.writeInt(isDark ? 1 : 0); + ParcelCompat.writeBoolean(dest, isDark); + dest.writeInt(original); } public void colorMenu(Menu menu) { @@ -342,11 +397,11 @@ public class ThemeColor implements Pickable { ThemeColor that = (ThemeColor) o; - return colorPrimary == that.colorPrimary; + return original == that.original; } @Override public int hashCode() { - return colorPrimary; + return original; } } diff --git a/app/src/main/java/org/tasks/widget/TasksWidget.java b/app/src/main/java/org/tasks/widget/TasksWidget.java index 4d56b09ab..5904cd983 100644 --- a/app/src/main/java/org/tasks/widget/TasksWidget.java +++ b/app/src/main/java/org/tasks/widget/TasksWidget.java @@ -75,7 +75,7 @@ public class TasksWidget extends InjectingAppWidgetProvider { rvIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id); rvIntent.setData(Uri.parse(rvIntent.toUri(Intent.URI_INTENT_SCHEME))); WidgetTheme theme = themeCache.getWidgetTheme(widgetPreferences.getThemeIndex()); - ThemeColor color = ThemeColor.newThemeColor(context, widgetPreferences.getColor()); + ThemeColor color = new ThemeColor(context, -1, widgetPreferences.getColor(), false); RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.scrollable_widget); if (atLeastJellybeanMR1()) { remoteViews.setInt(R.id.widget, "setLayoutDirection", locale.getDirectionality()); diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index a85135b8a..571b7e92c 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -10,26 +10,4 @@ @color/white_60 @color/white_38 @color/overdue_87 - - @color/blue_grey_200 - @color/grey_900 - @color/red_200 - @color/pink_200 - @color/purple_200 - @color/deep_purple_200 - @color/indigo_200 - @color/blue_200 - @color/light_blue_200 - @color/cyan_200 - @color/teal_200 - @color/green_200 - @color/light_green_200 - @color/lime_200 - @color/yellow_200 - @color/amber_200 - @color/orange_200 - @color/deep_orange_200 - @color/brown_200 - @color/grey_200 - @color/black_100 \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 43c70c670..9c06e6621 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -6,118 +6,77 @@ --> - @color/blue_grey_500 - @color/grey_800 - @color/red_500 - @color/pink_500 - @color/purple_500 - @color/deep_purple_500 - @color/indigo_500 - @color/blue_500 - @color/light_blue_500 - @color/cyan_500 - @color/teal_500 - @color/green_500 - @color/light_green_500 - @color/lime_500 - @color/yellow_500 - @color/amber_500 - @color/orange_500 - @color/deep_orange_500 - @color/brown_500 - @color/grey_500 - @color/white_100 - - #EF9A9A #f44336 #ff1744 #FF8A80 - #F48FB1 #e91e63 #f50057 #FF80AB - #CE93D8 #9c27b0 #d500f9 #EA80FC - #B39DDB #673ab7 #651fff #B388FF - #9FA8DA #3f51b5 #3d5afe #8C9EFF - #90CAF9 #2196f3 #2979ff #82B1FF - #81D4FA #03a9f4 #00b0ff #80D8FF - #80DEEA #00bcd4 #00e5ff #84FFFF - #80CBC4 #009688 #1de9b6 #A7FFEB - #A5D6A7 #4caf50 #00e676 #B9F6CA - #C5E1A5 #8bc34a #76ff03 #CCFF90 - #E6EE9C #cddc39 #c6ff00 #F4FF81 - #FFF59D #ffeb3b #ffea00 #FFFF8D - #FFE082 #ffc107 #ffc400 #FFE57F - #FFCC80 #ff9800 #ff9100 #FFD180 - #FFAB91 #ff5722 #ff3d00 #FF9E80 - #BCAAA4 #795548 - #EEEEEE #9e9e9e #424242 #212121 #CFD8DC - #B0BEC5 #78909c #607d8b