feat(IconOpts): colored icon preview

This commit is contained in:
rushiiMachine 2025-05-26 10:01:36 -07:00
parent b89a529afa
commit 910be40d79
No known key found for this signature in database
GPG key ID: DCBE5952BB3B6420
4 changed files with 151 additions and 3 deletions

View file

@ -0,0 +1,82 @@
package com.aliucord.manager.ui.components
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.InsetDrawable
import android.widget.ImageView
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import androidx.core.graphics.applyCanvas
import androidx.core.graphics.createBitmap
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import com.aliucord.manager.R
@Composable
fun discordIconDrawable(
backgroundColor: Color,
size: Dp = 24.dp,
): Drawable {
val density = LocalDensity.current
val context = LocalContext.current
val size = with(density) { size.roundToPx() }
val bitmap = remember(size) { createBitmap(size, size) }
val drawable = remember(context) {
InsetDrawable(
/* drawable = */ ContextCompat.getDrawable(context, R.drawable.ic_discord)!!,
/* inset = */ (size * .17f).toInt(),
)
}
DisposableEffect(size) {
onDispose {
bitmap.recycle()
}
}
DisposableEffect(backgroundColor, bitmap, drawable) {
bitmap.applyCanvas {
val paint = Paint().apply {
style = Paint.Style.FILL
setColor(backgroundColor.toArgb())
}
drawRect(Rect(0, 0, width, height), paint)
drawable.setBounds(0, 0, width, height)
drawable.draw(this)
}
onDispose {}
}
return remember(context, bitmap) {
RoundedBitmapDrawableFactory.create(context.resources, bitmap).apply {
isCircular = true
setAntiAlias(true)
}
}
}
@Composable
fun ColoredDiscordAppIcon(
drawable: Drawable,
modifier: Modifier = Modifier,
) {
AndroidView(
factory = { context ->
ImageView(context).apply {
setImageDrawable(drawable)
}
},
modifier = modifier,
)
}

View file

@ -31,8 +31,10 @@ import coil3.compose.AsyncImage
import com.aliucord.manager.R
import com.aliucord.manager.ui.components.*
import com.aliucord.manager.ui.screens.iconopts.components.*
import com.aliucord.manager.ui.screens.patchopts.PatchOptions
import com.aliucord.manager.ui.screens.patchopts.PatchOptionsScreen
import com.aliucord.manager.ui.util.ColorSaver
import com.aliucord.manager.ui.util.throttledState
import dev.zt64.compose.pipette.CircularColorPicker
import dev.zt64.compose.pipette.HsvColor
import kotlinx.parcelize.IgnoredOnParcel
@ -88,7 +90,37 @@ fun IconOptionsScreenContent(
.padding(paddingValues)
.padding(horizontal = 20.dp, vertical = 20.dp)
) {
// TODO: live icon preview
Row(
horizontalArrangement = Arrangement.spacedBy(24.dp, Alignment.CenterHorizontally),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth(),
) {
val color = when (mode) {
IconOptionsMode.Original -> PatchOptions.IconReplacement.Blurple.color // TODO: old vs new discord color?
IconOptionsMode.Aliucord -> PatchOptions.IconReplacement.Aliucord.color
IconOptionsMode.CustomColor -> selectedColor.toColor()
IconOptionsMode.CustomImage -> Color.Black // TODO: custom image icon preview
}
val throttledColor by throttledState(value = color, throttleMs = 75)
val drawable = discordIconDrawable(
backgroundColor = throttledColor,
size = 72.dp,
)
ColoredDiscordAppIcon(
drawable = drawable,
modifier = Modifier.size(24.dp),
)
ColoredDiscordAppIcon(
drawable = drawable,
modifier = Modifier.size(48.dp),
)
ColoredDiscordAppIcon(
drawable = drawable,
modifier = Modifier.size(72.dp),
)
}
HorizontalDivider(Modifier.padding(vertical = 16.dp))

View file

@ -82,9 +82,9 @@ data class PatchOptions(
val Aliucord = CustomColor(Color(0xFF00C853))
/**
* The Discord icon color.
* The new Discord blurple.
*/
val Discord = CustomColor(Color(0xFF7289DA))
val Blurple = CustomColor(Color(0xFF5865F2))
}
}

View file

@ -0,0 +1,34 @@
package com.aliucord.manager.ui.util
import androidx.compose.runtime.*
import kotlinx.coroutines.*
@Composable
fun <T> throttledState(
value: T,
throttleMs: Long,
): State<T> {
val scope = rememberCoroutineScope()
val state = remember { mutableStateOf(value) }
var lastEmitTime by remember { mutableLongStateOf(0L) }
var trailingJob by remember { mutableStateOf<Job?>(null) }
LaunchedEffect(value) {
val now = System.currentTimeMillis()
val sinceLastEmit = now - lastEmitTime
if (sinceLastEmit >= throttleMs) {
lastEmitTime = now
state.value = value
} else {
trailingJob?.cancel()
trailingJob = scope.launch {
delay(throttleMs - sinceLastEmit)
lastEmitTime = System.currentTimeMillis()
state.value = value
}
}
}
return state
}