Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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<Color?> { null }
Expand Down
33 changes: 16 additions & 17 deletions app/src/main/java/com/sameerasw/essentials/utils/AppIconUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<Bitmap, ThemedMode>())

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)
}

Expand Down
Loading