feat: Implement dynamic icon and text colors based on background luminance for improved accessibility
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -94,6 +94,15 @@ data class AvatarColors(val textColor: Color, val backgroundColor: Color)
|
||||
|
||||
private val avatarColorCache = mutableMapOf<String, AvatarColors>()
|
||||
|
||||
/**
|
||||
* Определяет, является ли цвет светлым (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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,15 @@ data class AvatarColors(val textColor: Color, val backgroundColor: Color)
|
||||
|
||||
private val avatarColorCache = mutableMapOf<String, AvatarColors>()
|
||||
|
||||
/**
|
||||
* Определяет, является ли цвет светлым (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),
|
||||
|
||||
Reference in New Issue
Block a user