From 3d282507caa1559ca367bdd4d656a1ed1f44461d Mon Sep 17 00:00:00 2001 From: rushiiMachine <33725716+rushiiMachine@users.noreply.github.com> Date: Mon, 26 May 2025 22:54:05 -0700 Subject: [PATCH] feat(IconOpts): old discord logo+color option --- .../patcher/steps/patch/PatchIconsStep.kt | 52 ++++++++++++++++--- .../ui/components/CustomDiscordAppIcon.kt | 4 +- .../screens/IconOptionsScreenPreview.kt | 6 +-- .../ui/screens/iconopts/IconOptionsMode.kt | 1 + .../ui/screens/iconopts/IconOptionsModel.kt | 11 ++-- .../ui/screens/iconopts/IconOptionsScreen.kt | 12 ++++- .../ui/screens/patchopts/PatchOptions.kt | 25 +++++++-- app/src/main/res/drawable/ic_discord.xml | 2 +- app/src/main/res/drawable/ic_discord_old.xml | 9 ++++ app/src/main/res/values/strings.xml | 2 + 10 files changed, 100 insertions(+), 24 deletions(-) create mode 100644 app/src/main/res/drawable/ic_discord_old.xml diff --git a/app/src/main/kotlin/com/aliucord/manager/patcher/steps/patch/PatchIconsStep.kt b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/patch/PatchIconsStep.kt index 0b335df..639d11e 100644 --- a/app/src/main/kotlin/com/aliucord/manager/patcher/steps/patch/PatchIconsStep.kt +++ b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/patch/PatchIconsStep.kt @@ -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) diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/components/CustomDiscordAppIcon.kt b/app/src/main/kotlin/com/aliucord/manager/ui/components/CustomDiscordAppIcon.kt index 0dc3391..f3963c2 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/components/CustomDiscordAppIcon.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/components/CustomDiscordAppIcon.kt @@ -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) { diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/IconOptionsScreenPreview.kt b/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/IconOptionsScreenPreview.kt index 37997ec..0ee5bbf 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/IconOptionsScreenPreview.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/IconOptionsScreenPreview.kt @@ -60,12 +60,12 @@ private class IconOptionsParametersProvider : PreviewParameterProvider { + 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"), ) } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/iconopts/IconOptionsScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/iconopts/IconOptionsScreen.kt index 0b16073..bee6a38 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/iconopts/IconOptionsScreen.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screens/iconopts/IconOptionsScreen.kt @@ -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() diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/patchopts/PatchOptions.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/patchopts/PatchOptions.kt index d1d0c67..549a189 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/patchopts/PatchOptions.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screens/patchopts/PatchOptions.kt @@ -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), ) } } diff --git a/app/src/main/res/drawable/ic_discord.xml b/app/src/main/res/drawable/ic_discord.xml index a8b2b45..3e6249b 100644 --- a/app/src/main/res/drawable/ic_discord.xml +++ b/app/src/main/res/drawable/ic_discord.xml @@ -4,6 +4,6 @@ android:viewportWidth="256" android:viewportHeight="256"> diff --git a/app/src/main/res/drawable/ic_discord_old.xml b/app/src/main/res/drawable/ic_discord_old.xml new file mode 100644 index 0000000..9910927 --- /dev/null +++ b/app/src/main/res/drawable/ic_discord_old.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 729a1f8..654aa7e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -171,6 +171,8 @@ Advanced Icon Replacement + Old Discord + The old Discord blurple color and logo from 2021 and prior. Custom Color Custom Image Does not alter the original icon, instead only adding the missing monochrome icon to the APK.