feat(IconOptions): custom image preview
This commit is contained in:
parent
961cd93a6a
commit
71bf74c880
3 changed files with 117 additions and 46 deletions
|
|
@ -1,10 +1,10 @@
|
|||
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.graphics.*
|
||||
import android.graphics.drawable.*
|
||||
import android.os.Build
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
|
@ -17,8 +17,9 @@ 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 androidx.core.graphics.drawable.toDrawable
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.screens.patchopts.PatchOptions.IconReplacement
|
||||
|
||||
@Composable
|
||||
fun discordIconDrawable(
|
||||
|
|
@ -30,7 +31,6 @@ fun discordIconDrawable(
|
|||
val context = LocalContext.current
|
||||
val size = with(density) { size.roundToPx() }
|
||||
|
||||
val bitmap = remember(size) { createBitmap(size, size) }
|
||||
val drawable = remember(context, oldLogo) {
|
||||
val drawableId = when (oldLogo) {
|
||||
false -> R.drawable.ic_discord
|
||||
|
|
@ -45,6 +45,7 @@ fun discordIconDrawable(
|
|||
}
|
||||
}
|
||||
|
||||
val bitmap = remember(size) { createBitmap(size, size) }
|
||||
DisposableEffect(size) {
|
||||
onDispose {
|
||||
bitmap.recycle()
|
||||
|
|
@ -57,8 +58,8 @@ fun discordIconDrawable(
|
|||
style = Paint.Style.FILL
|
||||
setColor(backgroundColor.toArgb())
|
||||
}
|
||||
|
||||
drawRect(Rect(0, 0, width, height), paint)
|
||||
|
||||
drawable.setBounds(0, 0, width, height)
|
||||
drawable.draw(this)
|
||||
}
|
||||
|
|
@ -67,15 +68,30 @@ fun discordIconDrawable(
|
|||
}
|
||||
|
||||
return remember(context, bitmap) {
|
||||
RoundedBitmapDrawableFactory.create(context.resources, bitmap).apply {
|
||||
isCircular = true
|
||||
setAntiAlias(true)
|
||||
}
|
||||
bitmap.toDrawable(context.resources)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@Composable
|
||||
fun customIconDrawable(
|
||||
foregroundIcon: ByteArray,
|
||||
backgroundColor: Color = IconReplacement.BlurpleColor,
|
||||
): Drawable {
|
||||
val context = LocalContext.current
|
||||
|
||||
return remember(context, foregroundIcon) {
|
||||
val foregroundIconBitmap = BitmapFactory.decodeByteArray(foregroundIcon, 0, foregroundIcon.size)
|
||||
|
||||
AdaptiveIconDrawable(
|
||||
/* backgroundDrawable = */ backgroundColor.toArgb().toDrawable(),
|
||||
/* foregroundDrawable = */ foregroundIconBitmap.toDrawable(context.resources),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ColoredDiscordAppIcon(
|
||||
fun Drawable(
|
||||
drawable: Drawable,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -85,6 +101,9 @@ fun ColoredDiscordAppIcon(
|
|||
setImageDrawable(drawable)
|
||||
}
|
||||
},
|
||||
update = { imageView ->
|
||||
imageView.setImageDrawable(drawable)
|
||||
},
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import androidx.compose.animation.*
|
|||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
|
|
@ -90,39 +92,11 @@ fun IconOptionsScreenContent(
|
|||
.padding(paddingValues)
|
||||
.padding(horizontal = 20.dp, vertical = 20.dp)
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(24.dp, Alignment.CenterHorizontally),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
val color = when (mode) {
|
||||
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
|
||||
}
|
||||
val throttledColor by throttledState(value = color, throttleMs = 75)
|
||||
|
||||
val drawable = discordIconDrawable(
|
||||
backgroundColor = throttledColor,
|
||||
oldLogo = mode == IconOptionsMode.OldDiscord,
|
||||
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),
|
||||
)
|
||||
}
|
||||
IconPreview(
|
||||
mode = mode,
|
||||
selectedColor = selectedColor,
|
||||
selectedImage = selectedImage,
|
||||
)
|
||||
|
||||
HorizontalDivider(Modifier.padding(vertical = 16.dp))
|
||||
|
||||
|
|
@ -186,6 +160,82 @@ fun IconOptionsScreenContent(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun IconPreview(
|
||||
mode: IconOptionsMode,
|
||||
selectedColor: HsvColor,
|
||||
selectedImage: () -> ByteArray?,
|
||||
) {
|
||||
Label(
|
||||
name = stringResource(R.string.iconopts_icon_preview_title),
|
||||
description = stringResource(R.string.iconopts_icon_preview_desc),
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(24.dp, Alignment.CenterHorizontally),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 14.dp),
|
||||
) {
|
||||
val drawable = if (mode == IconOptionsMode.CustomImage) {
|
||||
val image = selectedImage()
|
||||
|
||||
if (image != null && Build.VERSION.SDK_INT >= 26) {
|
||||
customIconDrawable(
|
||||
foregroundIcon = image,
|
||||
)
|
||||
} else {
|
||||
discordIconDrawable(
|
||||
backgroundColor = Color.Black,
|
||||
oldLogo = false,
|
||||
size = 72.dp,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val color = when (mode) {
|
||||
IconOptionsMode.Original -> PatchOptions.IconReplacement.BlurpleColor
|
||||
IconOptionsMode.OldDiscord -> PatchOptions.IconReplacement.OldBlurpleColor
|
||||
IconOptionsMode.Aliucord -> PatchOptions.IconReplacement.AliucordColor
|
||||
IconOptionsMode.CustomColor -> selectedColor.toColor()
|
||||
IconOptionsMode.CustomImage -> error("unreachable")
|
||||
}
|
||||
val throttledColor by throttledState(value = color, throttleMs = 75)
|
||||
|
||||
discordIconDrawable(
|
||||
backgroundColor = throttledColor,
|
||||
oldLogo = mode == IconOptionsMode.OldDiscord,
|
||||
size = 72.dp,
|
||||
)
|
||||
}
|
||||
|
||||
Drawable(
|
||||
drawable = drawable,
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.clip(CircleShape),
|
||||
)
|
||||
Drawable(
|
||||
drawable = drawable,
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(CircleShape),
|
||||
)
|
||||
Drawable(
|
||||
drawable = drawable,
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(RoundedCornerShape(4.dp)),
|
||||
)
|
||||
Drawable(
|
||||
drawable = drawable,
|
||||
modifier = Modifier
|
||||
.size(72.dp)
|
||||
.clip(CircleShape),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CustomColorOptions(
|
||||
color: HsvColor,
|
||||
|
|
|
|||
|
|
@ -189,8 +189,10 @@
|
|||
<string name="iconopts_failed_image_too_big">Image size exceeds maximum of 256KiB</string>
|
||||
<string name="iconopts_btn_open_selection">Select Image</string>
|
||||
<string name="iconopts_btn_open_example_image">Example image</string>
|
||||
<string name="iconopts_icon_preview_title">Icon Preview</string>
|
||||
<string name="iconopts_icon_preview_desc">A preview of the selected options, cropped and scaled to show how the icon will appear on your device</string>
|
||||
<string name="iconopts_image_preview_title">Preview</string>
|
||||
<string name="iconopts_image_preview_desc">Uncropped preview of the selected image</string>
|
||||
<string name="iconopts_image_preview_desc">A full, uncropped preview of the selected image.</string>
|
||||
<string name="iconopts_image_preview_none">No image selected</string>
|
||||
|
||||
<string name="log_title">Log</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue