feat(IconOpts): old discord logo+color option

This commit is contained in:
rushiiMachine 2025-05-26 22:54:05 -07:00
parent 910be40d79
commit 3d282507ca
No known key found for this signature in database
GPG key ID: DCBE5952BB3B6420
10 changed files with 100 additions and 24 deletions

View file

@ -123,11 +123,30 @@ class PatchIconsStep(private val options: PatchOptions) : Step(), KoinComponent
// Add a new color resource to use
if (options.iconReplacement is IconReplacement.CustomColor) {
container.log("Adding icon color resource to arsc")
backgroundIcon = arsc.getPackageChunk()
.addColorResource("icon_background_replacement", options.iconReplacement.color)
}
// Add a new color and axml svg resource
else if (options.iconReplacement is IconReplacement.OldDiscord) {
container.log("Adding icon color resource to arsc")
backgroundIcon = arsc.getPackageChunk()
.addColorResource("icon_background_replacement", IconReplacement.OldBlurpleColor)
container.log("Adding custom icon foreground to arsc")
val iconPathIdx = arsc.getMainArscChunk().stringPool
.addString("res/ic_foreground_replacement.xml")
foregroundIcon = arsc.getPackageChunk().addResource(
typeName = "drawable",
resourceName = "ic_foreground_replacement",
configurations = { println("configuration: $it"); true },
valueType = BinaryResourceValue.Type.STRING,
valueData = iconPathIdx,
)
}
// Add a new mipmap resource and the file to be added later
else if (options.iconReplacement is IconReplacement.CustomImage) {
container.log("Adding custom icon foreground to arsc")
@ -162,14 +181,17 @@ class PatchIconsStep(private val options: PatchOptions) : Step(), KoinComponent
it.writeEntry("res/ic_aliucord_monochrome.xml", context.getResBytes(R.drawable.ic_discord_monochrome))
}
if (options.iconReplacement is IconReplacement.CustomImage) {
if (options.iconReplacement is IconReplacement.OldDiscord) {
container.log("Writing custom icon foreground to apk")
it.writeEntry("res/ic_foreground_replacement.xml", context.getResBytes(R.drawable.ic_discord_old))
} else if (options.iconReplacement is IconReplacement.CustomImage) {
container.log("Writing custom icon foreground to apk")
it.writeEntry("res/ic_foreground_replacement.png", options.iconReplacement.imageBytes)
}
container.log("Writing resources unaligned compressed")
it.deleteEntry("resources.arsc")
// This doesn't need to be aligned and uncompressed here, since it that is done during AlignmentStep
// This doesn't need to be aligned and uncompressed here, since that is done during AlignmentStep
it.writeEntry("resources.arsc", arsc.toByteArray())
}
}
@ -192,13 +214,17 @@ class PatchIconsStep(private val options: PatchOptions) : Step(), KoinComponent
val allIconFiles = squareIconFiles + roundIconFiles
val backgroundColor = when (options.iconReplacement) {
IconReplacement.Original -> IconReplacement.Aliucord.color
IconReplacement.Original -> IconReplacement.AliucordColor
IconReplacement.OldDiscord -> IconReplacement.OldBlurpleColor
is IconReplacement.CustomColor -> options.iconReplacement.color
is IconReplacement.CustomImage -> error("Cannot patch custom images below Android 8 (Adaptive Icons are required)")
}
container.log("Generating static launcher icon")
val icon = createDiscordLauncherIcon(backgroundColor)
val icon = createDiscordLauncherIcon(
backgroundColor = backgroundColor,
oldLogo = options.iconReplacement == IconReplacement.OldDiscord,
)
val iconBytes = ByteArrayOutputStream().use {
icon.compress(Bitmap.CompressFormat.PNG, 0, it)
icon.recycle()
@ -219,14 +245,24 @@ class PatchIconsStep(private val options: PatchOptions) : Step(), KoinComponent
* to create a launcher icon like Discord's. This is used for devices that
* do not support adaptive icons.
*/
private fun createDiscordLauncherIcon(backgroundColor: Color, size: Int = 192): Bitmap {
private fun createDiscordLauncherIcon(
backgroundColor: Color,
oldLogo: Boolean = false,
size: Int = 192,
): Bitmap {
val paint = Paint().apply {
style = Paint.Style.FILL
setColor(backgroundColor.toArgb())
}
val vectorDrawable = ContextCompat.getDrawable(context, R.drawable.ic_discord)!!
val drawable = InsetDrawable(vectorDrawable, (size * .17f).toInt())
val drawableId = when (oldLogo) {
false -> R.drawable.ic_discord
true -> R.drawable.ic_discord_old
}
val vectorDrawable = ContextCompat.getDrawable(context, drawableId)!!
val drawable = InsetDrawable(vectorDrawable, (size * .17f).toInt()).apply {
setTint(Color.White.toArgb())
}
val icon = createBitmap(size, size).applyCanvas {
drawRect(Rect(0, 0, width, height), paint)

View file

@ -34,7 +34,9 @@ fun discordIconDrawable(
InsetDrawable(
/* drawable = */ ContextCompat.getDrawable(context, R.drawable.ic_discord)!!,
/* inset = */ (size * .17f).toInt(),
)
).apply {
setTint(Color.White.toArgb())
}
}
DisposableEffect(size) {

View file

@ -60,12 +60,12 @@ private class IconOptionsParametersProvider : PreviewParameterProvider<IconOptio
override val values = sequenceOf(
IconOptionsParameters(
mode = IconOptionsMode.Original,
selectedColor = IconReplacement.Aliucord.color,
selectedColor = IconReplacement.AliucordColor,
selectedImage = { null },
),
IconOptionsParameters(
mode = IconOptionsMode.Aliucord,
selectedColor = IconReplacement.Aliucord.color,
selectedColor = IconReplacement.AliucordColor,
selectedImage = { null },
),
IconOptionsParameters(
@ -89,7 +89,7 @@ private class IconOptionsParametersProvider : PreviewParameterProvider<IconOptio
)
private fun getMipmapBytes(context: Context, @DrawableRes id: Int): ByteArray {
val drawable = AppCompatResources.getDrawable(context, R.mipmap.ic_launcher)!!
val drawable = AppCompatResources.getDrawable(context, id)!!
val bitmap = drawable.toBitmap()
return ByteArrayOutputStream().use {
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, it)

View file

@ -5,6 +5,7 @@ import androidx.compose.runtime.Immutable
@Immutable
enum class IconOptionsMode {
Original,
OldDiscord,
Aliucord,
CustomColor,
CustomImage,

View file

@ -29,7 +29,7 @@ class IconOptionsModel(
}
// ---------- Replacement color ---------- //
var selectedColor by mutableStateOf(HsvColor(IconReplacement.Aliucord.color))
var selectedColor by mutableStateOf(HsvColor(IconReplacement.AliucordColor))
private set
fun changeSelectedColor(color: HsvColor) {
@ -75,9 +75,9 @@ class IconOptionsModel(
// ---------- Other ---------- //
init {
when (prefilledOptions) {
is IconReplacement.CustomColor if prefilledOptions.color == IconReplacement.Aliucord.color -> {
is IconReplacement.CustomColor if prefilledOptions.color == IconReplacement.AliucordColor -> {
changeMode(IconOptionsMode.Aliucord)
initSelectedColor(IconReplacement.Aliucord.color)
initSelectedColor(IconReplacement.AliucordColor)
}
is IconReplacement.CustomColor -> {
@ -91,14 +91,17 @@ class IconOptionsModel(
}
IconReplacement.Original -> changeMode(IconOptionsMode.Original)
IconReplacement.OldDiscord -> changeMode(IconOptionsMode.OldDiscord)
}
}
fun generateConfig(): IconReplacement = when (mode) {
IconOptionsMode.Original -> IconReplacement.Original
IconOptionsMode.Aliucord -> IconReplacement.Aliucord
IconOptionsMode.OldDiscord -> IconReplacement.OldDiscord
IconOptionsMode.Aliucord -> IconReplacement.CustomColor(IconReplacement.AliucordColor)
IconOptionsMode.CustomColor -> IconReplacement.CustomColor(color = selectedColor.toColor())
IconOptionsMode.CustomImage -> IconReplacement.CustomImage(
// TODO: fix this?
imageBytes = selectedImage ?: throw IllegalStateException("Cannot generate config without a selected image"),
)
}

View file

@ -96,8 +96,9 @@ fun IconOptionsScreenContent(
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.Original -> PatchOptions.IconReplacement.BlurpleColor
IconOptionsMode.OldDiscord -> PatchOptions.IconReplacement.OldBlurpleColor
IconOptionsMode.Aliucord -> PatchOptions.IconReplacement.AliucordColor
IconOptionsMode.CustomColor -> selectedColor.toColor()
IconOptionsMode.CustomImage -> Color.Black // TODO: custom image icon preview
}
@ -130,6 +131,12 @@ fun IconOptionsScreenContent(
selected = mode == IconOptionsMode.Original,
onClick = remember { { setMode(IconOptionsMode.Original) } },
)
RadioSelectorItem(
name = stringResource(R.string.iconopts_variant_title_old_discord),
description = stringResource(R.string.iconopts_variant_desc_old_discord),
selected = mode == IconOptionsMode.OldDiscord,
onClick = remember { { setMode(IconOptionsMode.OldDiscord) } },
)
RadioSelectorItem(
name = stringResource(R.string.aliucord),
description = stringResource(R.string.iconopts_variant_desc_aliucord),
@ -202,6 +209,7 @@ private fun CustomColorOptions(
saturation = color.saturation,
value = color.value,
onColorChange = { hue, saturation ->
// FIXME: lightness slider resets when color wheel is used
color.copy(hue = hue, saturation = saturation).let {
setColor(it)
initialRGBFieldColor = it.toColor()

View file

@ -47,6 +47,16 @@ data class PatchOptions(
@SerialName("original")
data object Original : IconReplacement
/**
* Changes the foreground logo to Discord's old logo and the
* background color to old blurple, like prior to early 2021.
*/
@Immutable
@Parcelize
@Serializable
@SerialName("old_discord")
data object OldDiscord : IconReplacement
/**
* Changes the background of the icon to a specific color without
* altering the foreground or monochrome variants.
@ -77,14 +87,19 @@ data class PatchOptions(
companion object {
/**
* The default icon replacement option.
* The default Aliucord background color.
*/
val Aliucord = CustomColor(Color(0xFF00C853))
val AliucordColor = Color(0xFF00C853)
/**
* The new Discord blurple.
* The new Discord blurple used in icons.
*/
val Blurple = CustomColor(Color(0xFF5865F2))
val BlurpleColor = Color(0xFF5865F2)
/**
* The old Discord icon color (before the accessibility redesign).
*/
val OldBlurpleColor = Color(0xFF7289DA)
}
}
@ -93,7 +108,7 @@ data class PatchOptions(
appName = "Aliucord",
packageName = "com.aliucord",
debuggable = false,
iconReplacement = IconReplacement.Aliucord,
iconReplacement = IconReplacement.CustomColor(IconReplacement.AliucordColor),
)
}
}

View file

@ -4,6 +4,6 @@
android:viewportWidth="256"
android:viewportHeight="256">
<path
android:fillColor="#FFFFFFFF"
android:fillColor="#5865F2"
android:pathData="M216.86,45.1C200.29,37.34 182.57,31.71 164.04,28.5C161.77,32.61 159.11,38.15 157.28,42.55C137.58,39.58 118.07,39.58 98.74,42.55C96.91,38.15 94.19,32.61 91.9,28.5C73.35,31.71 55.61,37.36 39.04,45.14C5.62,95.65 -3.44,144.9 1.09,193.46C23.26,210.01 44.74,220.07 65.86,226.65C71.08,219.47 75.73,211.84 79.74,203.8C72.1,200.9 64.79,197.32 57.89,193.17C59.72,191.81 61.51,190.39 63.24,188.93C105.37,208.63 151.13,208.63 192.75,188.93C194.51,190.39 196.3,191.81 198.11,193.17C191.18,197.34 183.85,200.92 176.22,203.82C180.23,211.84 184.86,219.49 190.1,226.67C211.24,220.09 232.74,210.03 254.91,193.46C260.23,137.17 245.83,88.37 216.86,45.1ZM85.47,163.59C72.83,163.59 62.46,151.79 62.46,137.41C62.46,123.04 72.61,111.21 85.47,111.21C98.34,111.21 108.71,123.02 108.49,137.41C108.51,151.79 98.34,163.59 85.47,163.59ZM170.53,163.59C157.88,163.59 147.51,151.79 147.51,137.41C147.51,123.04 157.66,111.21 170.53,111.21C183.39,111.21 193.76,123.02 193.54,137.41C193.54,151.79 183.39,163.59 170.53,163.59Z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="800"
android:viewportHeight="800">
<path
android:fillColor="#6665D2"
android:pathData="M701.3,185.8c-98,-73.4 -191.9,-71.3 -191.9,-71.3l-9.6,11c116.5,35 170.7,86.4 170.7,86.4c-166.7,-91.8 -379.1,-91.1 -552.5,0c0,0 56.2,-54.2 179.6,-89.1l-6.9,-8.2c0,0 -93.2,-2.1 -191.9,71.3C98.7,185.8 0,363.3 0,582c0,0 57.6,98.7 209.1,103.5c0,0 25.4,-30.2 45.9,-56.2c-87.1,-26 -120,-80.2 -120,-80.2c54.7,34.2 110,55.6 178.9,69.2c112.1,23.1 251.6,-0.7 355.8,-69.2c0,0 -34.3,55.5 -124.1,80.9c20.6,25.4 45.2,54.8 45.2,54.8C742.4,680 800,581.3 800,582C800,363.3 701.3,185.8 701.3,185.8zM272.1,518.3c-38.4,0 -69.9,-33.6 -69.9,-75.4c2.8,-100.1 137.7,-99.8 139.8,0C342.1,484.7 311.2,518.3 272.1,518.3zM522.4,518.3c-38.4,0 -69.9,-33.6 -69.9,-75.4c3.1,-99.9 136.3,-100 139.8,0C592.3,484.7 561.4,518.3 522.4,518.3z" />
</vector>

View file

@ -171,6 +171,8 @@
<string name="patchopts_divider_advanced">Advanced</string>
<string name="iconopts_screen_title">Icon Replacement</string>
<string name="iconopts_variant_title_old_discord">Old Discord</string>
<string name="iconopts_variant_desc_old_discord">The old Discord blurple color and logo from 2021 and prior.</string>
<string name="iconopts_variant_title_color">Custom Color</string>
<string name="iconopts_variant_title_image">Custom Image</string>
<string name="iconopts_variant_desc_discord">Does not alter the original icon, instead only adding the missing monochrome icon to the APK.</string>