From 89746c5bbd9f886177d4336f39d246f1018797bd Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Sun, 25 Jan 2026 19:00:35 +0500 Subject: [PATCH] feat: Implement dynamic icon and text colors based on background luminance for improved accessibility --- .../messenger/ui/chats/ChatDetailScreen.kt | 4 ++-- .../messenger/ui/chats/ChatsListScreen.kt | 17 +++++++++++------ .../messenger/ui/settings/OtherProfileScreen.kt | 12 ++++++++---- .../messenger/ui/settings/ProfileScreen.kt | 15 ++++++++++++--- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt index 2711d8a..3455fa5 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt @@ -898,7 +898,7 @@ fun ChatDetailScreen( contentDescription = "Call", tint = - Color.White + if (isDarkTheme) Color.White else Color(0xFF007AFF) ) } } @@ -931,7 +931,7 @@ fun ChatDetailScreen( contentDescription = "More", tint = - Color.White, + if (isDarkTheme) Color.White else Color(0xFF007AFF), modifier = Modifier.size( 26.dp diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt index 8bbb3c3..881f390 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt @@ -94,6 +94,15 @@ data class AvatarColors(val textColor: Color, val backgroundColor: Color) private val avatarColorCache = mutableMapOf() +/** + * Определяет, является ли цвет светлым (true) или темным (false) + * Использует формулу relative luminance из WCAG + */ +fun isColorLight(color: Color): Boolean { + val luminance = 0.299f * color.red + 0.587f * color.green + 0.114f * color.blue + return luminance > 0.5f +} + fun getAvatarColor(name: String, isDarkTheme: Boolean): AvatarColors { val cacheKey = "${name}_${if (isDarkTheme) "dark" else "light"}" return avatarColorCache.getOrPut(cacheKey) { @@ -453,7 +462,7 @@ fun ChatsListScreen( text = accountName, fontSize = 16.sp, fontWeight = FontWeight.SemiBold, - color = Color.White + color = if (isColorLight(headerColor)) Color.Black else Color.White ) } @@ -465,11 +474,7 @@ fun ChatsListScreen( "@$accountUsername", fontSize = 13.sp, color = - Color.White - .copy( - alpha = - 0.7f - ) + if (isColorLight(headerColor)) Color.Black.copy(alpha = 0.7f) else Color.White.copy(alpha = 0.7f) ) } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt index 7113670..1de7296 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt @@ -248,7 +248,7 @@ private fun CollapsingOtherProfileHeader( Icon( imageVector = Icons.Filled.ArrowBack, contentDescription = "Back", - tint = Color.White, + tint = if (isColorLight(avatarColors.backgroundColor)) Color.Black else Color.White, modifier = Modifier.size(24.dp) ) } @@ -272,7 +272,7 @@ private fun CollapsingOtherProfileHeader( Icon( imageVector = Icons.Default.MoreVert, contentDescription = "Profile menu", - tint = Color.White, + tint = if (isColorLight(avatarColors.backgroundColor)) Color.Black else Color.White, modifier = Modifier.size(24.dp) ) } @@ -347,7 +347,7 @@ private fun CollapsingOtherProfileHeader( text = name, fontSize = nameFontSize, fontWeight = FontWeight.SemiBold, - color = Color.White, + color = if (isColorLight(avatarColors.backgroundColor)) Color.Black else Color.White, maxLines = 1, overflow = TextOverflow.Ellipsis, textAlign = TextAlign.Center @@ -368,7 +368,11 @@ private fun CollapsingOtherProfileHeader( Text( text = if (isOnline) "online" else "offline", fontSize = onlineFontSize, - color = if (isOnline) Color(0xFF4CAF50) else Color.White.copy(alpha = 0.6f) + color = if (isOnline) { + Color(0xFF4CAF50) + } else { + if (isColorLight(avatarColors.backgroundColor)) Color.Black.copy(alpha = 0.6f) else Color.White.copy(alpha = 0.6f) + } ) } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt index a16b76d..1a86d34 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt @@ -102,6 +102,15 @@ data class AvatarColors(val textColor: Color, val backgroundColor: Color) private val avatarColorCache = mutableMapOf() +/** + * Определяет, является ли цвет светлым (true) или темным (false) + * Использует формулу relative luminance из WCAG + */ +fun isColorLight(color: Color): Boolean { + val luminance = 0.299f * color.red + 0.587f * color.green + 0.114f * color.blue + return luminance > 0.5f +} + fun getAvatarColor(name: String, isDarkTheme: Boolean): AvatarColors { val cacheKey = "${name}_${if (isDarkTheme) "dark" else "light"}" return avatarColorCache.getOrPut(cacheKey) { @@ -697,7 +706,7 @@ private fun CollapsingProfileHeader( Icon( imageVector = TablerIcons.ArrowLeft, contentDescription = "Back", - tint = Color.White, + tint = if (isColorLight(avatarColors.backgroundColor)) Color.Black else Color.White, modifier = Modifier.size(24.dp) ) } @@ -721,7 +730,7 @@ private fun CollapsingProfileHeader( Icon( imageVector = TablerIcons.DotsVertical, contentDescription = "Profile menu", - tint = Color.White, + tint = if (isColorLight(avatarColors.backgroundColor)) Color.Black else Color.White, modifier = Modifier.size(24.dp) ) } @@ -805,7 +814,7 @@ private fun CollapsingProfileHeader( text = name, fontSize = nameFontSize, fontWeight = FontWeight.SemiBold, - color = Color.White, + color = if (isColorLight(avatarColors.backgroundColor)) Color.Black else Color.White, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.widthIn(max = 220.dp),