diff --git a/lawnchair/res/values/config.xml b/lawnchair/res/values/config.xml
index a73927291a..1db5dca5eb 100644
--- a/lawnchair/res/values/config.xml
+++ b/lawnchair/res/values/config.xml
@@ -48,11 +48,12 @@
gregorian
- default
+ no_default
- app_icon
- launcher_default
+ default
+ default
+ default
system
diff --git a/lawnchair/res/values/strings.xml b/lawnchair/res/values/strings.xml
index 6776b46329..75ec576054 100644
--- a/lawnchair/res/values/strings.xml
+++ b/lawnchair/res/values/strings.xml
@@ -75,6 +75,9 @@
Show Notification Count
Notification Dot Color
+ Notification Count Color
+ Warning: Notification Dot & Counter colors do not have enough contrast with each other
+ Warning: Notification Dot & Counter colors might not always have enough contrast with each other
Notification Access Needed
@@ -156,7 +159,7 @@
Paste
Copied to clipboard.
Invalid Color
- Default
+ Managed by Lawnchair
diff --git a/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt b/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt
index 843319bd95..ff9021500c 100644
--- a/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt
+++ b/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt
@@ -100,6 +100,14 @@ class PreferenceManager2(private val context: Context) : PreferenceManager {
defaultValue = ColorOption.fromString(context.getString(R.string.config_default_notification_dot_color)),
)
+ val notificationDotTextColor = preference(
+ key = stringPreferencesKey(name = "notification_dot_text_color"),
+ parse = ColorOption::fromString,
+ save = ColorOption::toString,
+ onSet = { reloadHelper.reloadGrid() },
+ defaultValue = ColorOption.fromString(context.getString(R.string.config_default_notification_dot_text_color)),
+ )
+
val folderColor = preference(
key = stringPreferencesKey(name = "folder_color"),
parse = ColorOption::fromString,
diff --git a/lawnchair/src/app/lawnchair/theme/color/ColorOption.kt b/lawnchair/src/app/lawnchair/theme/color/ColorOption.kt
index 6e22857e15..de54d3c734 100644
--- a/lawnchair/src/app/lawnchair/theme/color/ColorOption.kt
+++ b/lawnchair/src/app/lawnchair/theme/color/ColorOption.kt
@@ -1,7 +1,6 @@
package app.lawnchair.theme.color
import android.graphics.Color
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import app.lawnchair.ui.preferences.components.colorpreference.ColorPreferenceEntry
import app.lawnchair.ui.theme.getSystemAccent
@@ -61,7 +60,7 @@ sealed class ColorOption {
override fun toString() = "custom|#${String.format("%08x", color)}"
}
- object LauncherDefault : ColorOption() {
+ object Default : ColorOption() {
override val isSupported = false
override val colorPreferenceEntry = ColorPreferenceEntry(
@@ -70,7 +69,7 @@ sealed class ColorOption {
{ 0 }
)
- override fun toString() = "launcher_default"
+ override fun toString() = "default"
}
companion object {
@@ -79,7 +78,7 @@ sealed class ColorOption {
fun fromString(stringValue: String) = when (stringValue) {
"system_accent" -> SystemAccent
"wallpaper_primary" -> WallpaperPrimary
- "launcher_default" -> LauncherDefault
+ "default" -> Default
else -> instantiateCustomColor(stringValue)
}
diff --git a/lawnchair/src/app/lawnchair/ui/preferences/GeneralPreferences.kt b/lawnchair/src/app/lawnchair/ui/preferences/GeneralPreferences.kt
index 8adea1a267..a8573d6099 100644
--- a/lawnchair/src/app/lawnchair/ui/preferences/GeneralPreferences.kt
+++ b/lawnchair/src/app/lawnchair/ui/preferences/GeneralPreferences.kt
@@ -27,6 +27,8 @@ import app.lawnchair.preferences.getAdapter
import app.lawnchair.preferences.preferenceManager
import app.lawnchair.preferences2.asState
import app.lawnchair.preferences2.preferenceManager2
+import app.lawnchair.theme.color.ColorOption
+import app.lawnchair.ui.preferences.components.DividerColumn
import app.lawnchair.ui.preferences.components.ExpandAndShrink
import app.lawnchair.ui.preferences.components.FontPreference
import app.lawnchair.ui.preferences.components.IconShapePreview
@@ -37,6 +39,8 @@ import app.lawnchair.ui.preferences.components.PreferenceLayout
import app.lawnchair.ui.preferences.components.SliderPreference
import app.lawnchair.ui.preferences.components.SwitchPreference
import app.lawnchair.ui.preferences.components.ThemePreference
+import app.lawnchair.ui.preferences.components.WarningPreference
+import app.lawnchair.ui.preferences.components.colorpreference.ColorContrastWarning
import app.lawnchair.ui.preferences.components.colorpreference.ColorPreference
import app.lawnchair.ui.preferences.components.iconShapeEntries
import app.lawnchair.ui.preferences.components.iconShapeGraph
@@ -99,7 +103,7 @@ fun GeneralPreferences() {
)
}
}
-
+
val wrapAdaptiveIcons = prefs.wrapAdaptiveIcons.getAdapter()
PreferenceGroup(
heading = stringResource(id = R.string.icons),
@@ -148,15 +152,52 @@ fun GeneralPreferences() {
val serviceEnabled = notificationServiceEnabled()
NotificationDotsPreference(enabled = enabled, serviceEnabled = serviceEnabled)
if (enabled && serviceEnabled) {
- SwitchPreference(
- adapter = prefs2.showNotificationCount.getAdapter(),
- label = stringResource(id = R.string.show_notification_count),
- )
+ val showNotificationCountAdapter = prefs2.showNotificationCount.getAdapter()
ColorPreference(
preference = prefs2.notificationDotColor,
label = stringResource(id = R.string.notification_dots_color),
)
+ SwitchPreference(
+ adapter = showNotificationCountAdapter,
+ label = stringResource(id = R.string.show_notification_count),
+ )
+ ExpandAndShrink(visible = showNotificationCountAdapter.state.value) {
+ DividerColumn {
+ ColorPreference(
+ preference = prefs2.notificationDotTextColor,
+ label = stringResource(id = R.string.notification_dots_text_color),
+ )
+ NotificationDotColorContrastWarnings(
+ dotColor = prefs2.notificationDotColor.asState().value,
+ dotTextColor = prefs2.notificationDotTextColor.asState().value,
+ )
+ }
+ }
}
}
}
}
+
+@Composable
+private fun NotificationDotColorContrastWarnings(
+ dotColor: ColorOption,
+ dotTextColor: ColorOption,
+) {
+
+ val dotColorIsDynamic = when (dotColor) {
+ is ColorOption.SystemAccent,
+ is ColorOption.WallpaperPrimary,
+ is ColorOption.Default -> true
+ else -> false
+ }
+
+ if (dotColorIsDynamic && dotTextColor !is ColorOption.Default) {
+ WarningPreference(text = stringResource(id = R.string.notification_dots_color_contrast_warning_sometimes))
+ } else {
+ ColorContrastWarning(
+ foregroundColor = dotTextColor,
+ backgroundColor = dotColor,
+ text = stringResource(id = R.string.notification_dots_color_contrast_warning_always),
+ )
+ }
+}
diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/WarningPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/WarningPreference.kt
new file mode 100644
index 0000000000..40cb911e4c
--- /dev/null
+++ b/lawnchair/src/app/lawnchair/ui/preferences/components/WarningPreference.kt
@@ -0,0 +1,33 @@
+package app.lawnchair.ui.preferences.components
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.Warning
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+
+@Composable
+fun WarningPreference(
+ modifier: Modifier = Modifier,
+ text: String,
+) {
+ PreferenceTemplate(
+ modifier = modifier,
+ title = {},
+ description = {
+ Text(
+ text = text,
+ color = MaterialTheme.colorScheme.error,
+ )
+ },
+ startWidget = {
+ Icon(
+ imageVector = Icons.Rounded.Warning,
+ tint = MaterialTheme.colorScheme.error,
+ contentDescription = null,
+ )
+ },
+ )
+}
diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorContrastWarning.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorContrastWarning.kt
new file mode 100644
index 0000000000..04fc385597
--- /dev/null
+++ b/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorContrastWarning.kt
@@ -0,0 +1,63 @@
+package app.lawnchair.ui.preferences.components.colorpreference
+
+import androidx.annotation.ColorInt
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.core.graphics.ColorUtils
+import app.lawnchair.theme.color.ColorOption
+import app.lawnchair.ui.preferences.components.WarningPreference
+
+private const val CONTRAST_THRESHOLD = 1.5
+
+/**
+ * Displays [WarningPreference] when [foregroundColor] & [backgroundColor] have less than optimal contrast with each other.
+ *
+ * @see CONTRAST_THRESHOLD
+ */
+@Composable
+fun ColorContrastWarning(
+ modifier: Modifier = Modifier,
+ foregroundColor: ColorOption,
+ backgroundColor: ColorOption,
+ text: String,
+) {
+ val context = LocalContext.current
+ val foregroundColorInt = foregroundColor.colorPreferenceEntry.lightColor(context)
+ val backgroundColorInt = backgroundColor.colorPreferenceEntry.lightColor(context)
+ ColorContrastWarning(
+ modifier = modifier,
+ foregroundColor = foregroundColorInt,
+ backgroundColor = backgroundColorInt,
+ text = text,
+ )
+}
+
+/**
+ * Displays [WarningPreference] when [foregroundColor] & [backgroundColor] have less than optimal contrast with each other.
+ *
+ * @see CONTRAST_THRESHOLD
+ */
+@Composable
+fun ColorContrastWarning(
+ modifier: Modifier = Modifier,
+ @ColorInt foregroundColor: Int,
+ @ColorInt backgroundColor: Int,
+ text: String,
+) {
+
+ val enoughContrast = if (foregroundColor != 0 && backgroundColor != 0) {
+ ColorUtils.calculateContrast(
+ foregroundColor,
+ backgroundColor,
+ ) >= CONTRAST_THRESHOLD
+ } else true
+
+ if (!enoughContrast) {
+ WarningPreference(
+ modifier = modifier,
+ text = text,
+ )
+ }
+
+}
diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorDot.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorDot.kt
index 5f60281327..265d800b31 100644
--- a/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorDot.kt
+++ b/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorDot.kt
@@ -5,7 +5,11 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.MaterialTheme
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.HdrAuto
+import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
@@ -17,20 +21,27 @@ fun ColorDot(
entry: ColorPreferenceEntry,
modifier: Modifier = Modifier
) {
+ val context = LocalContext.current
+
+ val colorLight = entry.lightColor(context)
+ val colorDark = entry.darkColor(context)
+
val color = if (MaterialTheme.colors.isLight) {
- entry.lightColor(LocalContext.current)
+ colorLight
} else {
- entry.darkColor(LocalContext.current)
+ colorDark
}
- ColorDot(
- color = Color(color),
- modifier = modifier
- )
+ if (colorLight != 0) {
+ ColorDot(
+ color = Color(color),
+ modifier = modifier
+ )
+ } else DefaultColorDot(modifier = modifier)
}
@Composable
-fun ColorDot(
+private fun ColorDot(
color: Color,
modifier: Modifier = Modifier
) {
@@ -38,6 +49,25 @@ fun ColorDot(
modifier = modifier
.size(30.dp)
.clip(CircleShape)
- .background(color = color)
+ .background(color = color),
)
}
+
+@Composable
+fun DefaultColorDot(
+ modifier: Modifier = Modifier,
+) {
+ Box(
+ modifier = modifier
+ .size(30.dp)
+ .clip(CircleShape)
+ .background(color = androidx.compose.material3.MaterialTheme.colorScheme.surfaceVariant),
+ contentAlignment = Alignment.Center,
+ ) {
+ Icon(
+ imageVector = Icons.Rounded.HdrAuto,
+ contentDescription = null,
+ tint = androidx.compose.material3.MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ }
+}
diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorOptions.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorOptions.kt
index f89a2023bd..6808cfe512 100644
--- a/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorOptions.kt
+++ b/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorOptions.kt
@@ -22,4 +22,4 @@ val dynamicColors = listOf(ColorOption.SystemAccent, ColorOption.WallpaperPrimar
.filter(ColorOption::isSupported)
.map(ColorOption::colorPreferenceEntry)
-val dynamicColorsWithDefault = dynamicColors + listOf(ColorOption.LauncherDefault.colorPreferenceEntry)
+val dynamicColorsWithDefault = dynamicColors + listOf(ColorOption.Default.colorPreferenceEntry)
diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorPreference.kt
index 8f6f1f55a5..17d80d5fad 100644
--- a/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorPreference.kt
+++ b/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorPreference.kt
@@ -44,7 +44,9 @@ fun ColorPreference(
val navController = LocalNavController.current
PreferenceTemplate(
title = { Text(text = label) },
- endWidget = { ColorDot(Color(adapter.state.value.colorPreferenceEntry.lightColor(LocalContext.current))) },
+ endWidget = {
+ ColorDot(adapter.state.value.colorPreferenceEntry)
+ },
description = {
Text(text = adapter.state.value.colorPreferenceEntry.label())
},
diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorSelectionPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorSelectionPreference.kt
index c66ee37f09..ef3a91bcd1 100644
--- a/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorSelectionPreference.kt
+++ b/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorSelectionPreference.kt
@@ -45,18 +45,21 @@ fun NavGraphBuilder.colorSelectionGraph(route: String) {
val pref = when (prefKey) {
preferenceManager2.accentColor.key.name -> preferenceManager2.accentColor
preferenceManager2.notificationDotColor.key.name -> preferenceManager2.notificationDotColor
+ preferenceManager2.notificationDotTextColor.key.name -> preferenceManager2.notificationDotTextColor
preferenceManager2.folderColor.key.name -> preferenceManager2.folderColor
else -> return@composable
}
val label = when (prefKey) {
preferenceManager2.accentColor.key.name -> stringResource(id = R.string.accent_color)
preferenceManager2.notificationDotColor.key.name -> stringResource(id = R.string.notification_dots_color)
+ preferenceManager2.notificationDotTextColor.key.name -> stringResource(id = R.string.notification_dots_text_color)
preferenceManager2.folderColor.key.name -> stringResource(id = R.string.folder_preview_bg_color_label)
else -> return@composable
}
val dynamicEntries = when (prefKey) {
preferenceManager2.folderColor.key.name,
- preferenceManager2.notificationDotColor.key.name -> dynamicColorsWithDefault
+ preferenceManager2.notificationDotColor.key.name,
+ preferenceManager2.notificationDotTextColor.key.name -> dynamicColorsWithDefault
else -> dynamicColors
}
ColorSelection(
diff --git a/platform_frameworks_libs_systemui b/platform_frameworks_libs_systemui
index b634f78d6c..6c2c80fa8c 160000
--- a/platform_frameworks_libs_systemui
+++ b/platform_frameworks_libs_systemui
@@ -1 +1 @@
-Subproject commit b634f78d6c0189dd21a9709cd40933978ac417bf
+Subproject commit 6c2c80fa8c7cd9a1caf8182f06d866cece593b5d
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 5fda036e70..45f93dda5e 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -504,14 +504,18 @@ public class DeviceProfile {
}
// Load dot color
- ColorOption colorOption = PreferenceExtensionsKt.firstBlocking(preferenceManager2.getNotificationDotColor());
- int color = colorOption.getColorPreferenceEntry().getLightColor().invoke(context);
+ ColorOption dotColorOption = PreferenceExtensionsKt.firstBlocking(preferenceManager2.getNotificationDotColor());
+ int dotColor = dotColorOption.getColorPreferenceEntry().getLightColor().invoke(context);
+
+ // Load counter color
+ ColorOption counterColorOption = PreferenceExtensionsKt.firstBlocking(preferenceManager2.getNotificationDotTextColor());
+ int countColor = counterColorOption.getColorPreferenceEntry().getLightColor().invoke(context);
// This is done last, after iconSizePx is calculated above.
Path dotPath = GraphicsUtils.getShapePath(DEFAULT_DOT_SIZE);
- mDotRendererWorkSpace = new DotRenderer(iconSizePx, dotPath, DEFAULT_DOT_SIZE, showNotificationCount, typeface, color);
+ mDotRendererWorkSpace = new DotRenderer(iconSizePx, dotPath, DEFAULT_DOT_SIZE, showNotificationCount, typeface, dotColor, countColor);
mDotRendererAllApps = iconSizePx == allAppsIconSizePx ? mDotRendererWorkSpace :
- new DotRenderer(allAppsIconSizePx, dotPath, DEFAULT_DOT_SIZE, showNotificationCount, typeface, color);
+ new DotRenderer(allAppsIconSizePx, dotPath, DEFAULT_DOT_SIZE, showNotificationCount, typeface, dotColor, countColor);
}
private int getHorizontalMarginPx(InvariantDeviceProfile idp, Resources res) {