diff --git a/app/src/main/java/com/sameerasw/essentials/island/ui/CompactTemplate.kt b/app/src/main/java/com/sameerasw/essentials/island/ui/CompactTemplate.kt index fbb8f08e6..73ff081e8 100644 --- a/app/src/main/java/com/sameerasw/essentials/island/ui/CompactTemplate.kt +++ b/app/src/main/java/com/sameerasw/essentials/island/ui/CompactTemplate.kt @@ -63,7 +63,7 @@ import com.sameerasw.essentials.island.state.IslandUiState @Composable private fun ChipUnit(color: Color, spec: IslandLayoutSpec, startNeighbor: Boolean, endNeighbor: Boolean, content: @Composable () -> Unit) { val container by animateColorAsState(color, label = "islandChip") - val ink = if (container.luminance() > 0.5f) Color.Black else Color.White + val ink = Color.Black val full = spec.cellSize / 2 val startRadius by animateDpAsState(if (startNeighbor) 6.dp else full, IslandMotion.float(), label = "chipStart") val endRadius by animateDpAsState(if (endNeighbor) 6.dp else full, IslandMotion.float(), label = "chipEnd") diff --git a/app/src/main/java/com/sameerasw/essentials/island/ui/components/IslandComponents.kt b/app/src/main/java/com/sameerasw/essentials/island/ui/components/IslandComponents.kt index 21b0bb775..c73ec62c1 100644 --- a/app/src/main/java/com/sameerasw/essentials/island/ui/components/IslandComponents.kt +++ b/app/src/main/java/com/sameerasw/essentials/island/ui/components/IslandComponents.kt @@ -1,5 +1,11 @@ package com.sameerasw.essentials.island.ui.components +import androidx.compose.material3.MaterialTheme +import androidx.compose.ui.graphics.lerp +import androidx.compose.ui.graphics.ColorMatrix +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.ColorFilter +import com.sameerasw.essentials.utils.AppIconUtil import com.sameerasw.essentials.island.ui.squareFit import androidx.compose.foundation.layout.sizeIn import androidx.compose.foundation.layout.aspectRatio @@ -79,20 +85,45 @@ fun IslandBitmap( .clip(shape), ) { bmp -> when { - bmp != null -> Image( - bitmap = remember(bmp) { bmp.asImageBitmap() }, - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = Modifier.fillMaxSize(), - ) + bmp != null -> { + val iconColor = LocalIslandIconColor.current ?: MaterialTheme.colorScheme.primary + val themedMode = remember(bmp) { AppIconUtil.themedMode(bmp) } + Image( + bitmap = remember(bmp) { bmp.asImageBitmap() }, + contentDescription = null, + contentScale = ContentScale.Crop, + colorFilter = when (themedMode) { + AppIconUtil.ThemedMode.MONO -> ColorFilter.tint(iconColor, BlendMode.SrcIn) + AppIconUtil.ThemedMode.DUO -> duotoneFilter(iconColor) + null -> null + }, + modifier = Modifier.fillMaxSize(), + ) + } fallbackRes != null -> Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Icon(painterResource(fallbackRes), null, tint = Color.White, modifier = Modifier.fillMaxSize(0.8f)) + Icon(painterResource(fallbackRes), null, tint = LocalIslandChipInk.current ?: Color.White, modifier = Modifier.fillMaxSize(0.8f)) } else -> Box(Modifier.fillMaxSize()) } } } +private fun duotoneFilter(accent: Color): ColorFilter { + val dark = lerp(accent, Color.Black, 0.55f) + val light = lerp(accent, Color.White, 0.85f) + fun row(low: Float, high: Float) = floatArrayOf((high - low), (high - low), (high - low), 0f, low * 255f) + return ColorFilter.colorMatrix( + ColorMatrix( + floatArrayOf( + *row(dark.red, light.red).also { it[1] = 0f; it[2] = 0f }, + *row(dark.green, light.green).also { it[0] = 0f; it[2] = 0f }, + *row(dark.blue, light.blue).also { it[0] = 0f; it[1] = 0f }, + 0f, 0f, 0f, 1f, 0f, + ), + ), + ) +} + val LocalIslandBitmapFlat = compositionLocalOf { false } val LocalIslandChipInk = compositionLocalOf { null } diff --git a/app/src/main/java/com/sameerasw/essentials/utils/AppIconUtil.kt b/app/src/main/java/com/sameerasw/essentials/utils/AppIconUtil.kt index 820bce568..5f96c9ad5 100644 --- a/app/src/main/java/com/sameerasw/essentials/utils/AppIconUtil.kt +++ b/app/src/main/java/com/sameerasw/essentials/utils/AppIconUtil.kt @@ -57,45 +57,44 @@ object AppIconUtil { return themed(context, icon) ?: icon } + enum class ThemedMode { MONO, DUO } + + private val themedModes = java.util.Collections.synchronizedMap(java.util.WeakHashMap()) + + fun themedMode(bitmap: Bitmap): ThemedMode? = themedModes[bitmap] + private fun themed(context: Context, icon: Drawable): Drawable? { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return null val monochrome = (icon as? AdaptiveIconDrawable)?.monochrome?.mutate() ?: return duotone(context, icon) - monochrome.setTint(context.getColor(android.R.color.system_accent1_200)) + monochrome.setTint(android.graphics.Color.WHITE) val canvasSize = 432 val full = Bitmap.createBitmap(canvasSize, canvasSize, Bitmap.Config.ARGB_8888) monochrome.setBounds(0, 0, canvasSize, canvasSize) monochrome.draw(Canvas(full)) val start = canvasSize * SAFE_ZONE_START / ADAPTIVE_SIZE val side = canvasSize * (SAFE_ZONE_END - SAFE_ZONE_START) / ADAPTIVE_SIZE - return BitmapDrawable(context.resources, Bitmap.createBitmap(full, start, start, side, side)) + val bitmap = Bitmap.createBitmap(full, start, start, side, side) + themedModes[bitmap] = ThemedMode.MONO + return BitmapDrawable(context.resources, bitmap) } private fun duotone(context: Context, icon: Drawable): Drawable { - val dark = context.getColor(android.R.color.system_accent1_700) - val light = context.getColor(android.R.color.system_accent1_100) - fun channel(shift: Int) = (dark shr shift and 0xFF).toFloat() to (light shr shift and 0xFF).toFloat() - val (rd, rl) = channel(16) - val (gd, gl) = channel(8) - val (bd, bl) = channel(0) - fun row(low: Float, high: Float) = - floatArrayOf( - (high - low) / 255f * 0.213f, (high - low) / 255f * 0.715f, (high - low) / 255f * 0.072f, 0f, low, - ) - val matrix = + val gray = ColorMatrix( floatArrayOf( - *row(rd, rl), - *row(gd, gl), - *row(bd, bl), + 0.213f, 0.715f, 0.072f, 0f, 0f, + 0.213f, 0.715f, 0.072f, 0f, 0f, + 0.213f, 0.715f, 0.072f, 0f, 0f, 0f, 0f, 0f, 1f, 0f, ), ) val size = 216 val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) val drawable = icon.constantState?.newDrawable()?.mutate() ?: icon.mutate() - drawable.colorFilter = ColorMatrixColorFilter(matrix) + drawable.colorFilter = ColorMatrixColorFilter(gray) drawable.setBounds(0, 0, size, size) drawable.draw(Canvas(bitmap)) + themedModes[bitmap] = ThemedMode.DUO return BitmapDrawable(context.resources, bitmap) }