diff --git a/bezier/src/main/java/io/channel/bezier/compose/component/avatar/BezierAvatar.kt b/bezier/src/main/java/io/channel/bezier/compose/component/avatar/BezierAvatar.kt new file mode 100644 index 00000000..f301192a --- /dev/null +++ b/bezier/src/main/java/io/channel/bezier/compose/component/avatar/BezierAvatar.kt @@ -0,0 +1,217 @@ +package io.channel.bezier.compose.component.avatar + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredSize +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil.compose.rememberAsyncImagePainter +import io.channel.bezier.BezierTheme +import io.channel.bezier.compose.R +import io.channel.bezier.compose.component.avatar.properties.BezierAvatarBadge +import io.channel.bezier.compose.component.avatar.properties.BezierAvatarSize +import io.channel.bezier.compose.component.badge.chat.BezierChatBadge +import io.channel.bezier.compose.component.badge.status.BezierStatusBadge +import io.channel.bezier.extension.outlineBorder +import io.channel.bezier.extension.thenIf +import io.channel.bezier.shape.SmoothRoundedCornerShape + +internal const val AvatarRadiusFraction = 42 + +@Composable +fun BezierAvatar( + url: String, + size: BezierAvatarSize, + isOnline: Boolean, + doNotDisturb: Boolean, + modifier: Modifier = Modifier, + showBorder: Boolean = false, + errorPainter: Painter = painterResource(id = R.drawable.unknown), +) { + BezierAvatar( + url = url, + size = size, + modifier = modifier, + showBorder = showBorder, + badge = BezierAvatarBadge.Status(isOnline = isOnline, doNotDisturb = doNotDisturb), + errorPainter = errorPainter, + ) +} + +@Composable +fun BezierAvatar( + url: String, + size: BezierAvatarSize, + modifier: Modifier = Modifier, + showBorder: Boolean = false, + badge: BezierAvatarBadge = BezierAvatarBadge.None, + errorPainter: Painter = painterResource(id = R.drawable.unknown), +) { + val painter = rememberAsyncImagePainter( + model = url, + error = errorPainter, + ) + + BezierAvatar( + painter = painter, + size = size, + modifier = modifier, + showBorder = showBorder, + badge = badge, + ) +} + +@Composable +fun BezierAvatar( + painter: Painter, + size: BezierAvatarSize, + isOnline: Boolean, + doNotDisturb: Boolean, + modifier: Modifier = Modifier, + showBorder: Boolean = false, +) { + BezierAvatar( + painter = painter, + size = size, + modifier = modifier, + showBorder = showBorder, + badge = BezierAvatarBadge.Status(isOnline = isOnline, doNotDisturb = doNotDisturb), + ) +} + +@Composable +fun BezierAvatar( + painter: Painter, + size: BezierAvatarSize, + modifier: Modifier = Modifier, + showBorder: Boolean = false, + badge: BezierAvatarBadge = BezierAvatarBadge.None, +) { + val avatarShape = SmoothRoundedCornerShape(AvatarRadiusFraction) + + Box( + modifier = modifier.requiredSize(size.size), + ) { + Image( + modifier = Modifier + .fillMaxSize() + .thenIf(showBorder) { + outlineBorder( + width = size.outlineBorderWidth, + color = BezierTheme.colorSchemes.surfaceNormal.color, + shape = avatarShape, + ) + } + .clip(avatarShape), + painter = painter, + contentDescription = null, + ) + + Box( + modifier = Modifier + .align(Alignment.BottomEnd) + .offset(x = size.badgeOffset, y = size.badgeOffset), + ) { + when (badge) { + BezierAvatarBadge.None -> Unit + BezierAvatarBadge.Chat -> BezierChatBadge(size = size.chatBadgeSize) + is BezierAvatarBadge.Status -> BezierStatusBadge( + isOnline = badge.isOnline, + doNotDisturb = badge.doNotDisturb, + size = size.statusBadgeSize, + ) + + is BezierAvatarBadge.Integration -> IntegrationBadge( + integration = badge, + size = size, + ) + } + } + } +} + +@Composable +private fun IntegrationBadge( + integration: BezierAvatarBadge.Integration, + size: BezierAvatarSize, +) { + val shape = integration.createShape(size) + + Image( + modifier = Modifier + .size(size.size) + .outlineBorder( + width = 2.dp, + color = BezierTheme.colorSchemes.surfaceNormal.color, + shape = shape, + ) + .clip(shape), + painter = integration.painter(), + contentDescription = integration.name, + ) +} + +@Composable +@Preview(widthDp = 700, heightDp = 1100) +private fun BezierAvatarPreview() { + val badges = listOf( + BezierAvatarBadge.None, + BezierAvatarBadge.Status(isOnline = true, doNotDisturb = false), + BezierAvatarBadge.Status(isOnline = true, doNotDisturb = true), + BezierAvatarBadge.Status(isOnline = false, doNotDisturb = true), + BezierAvatarBadge.Status(isOnline = false, doNotDisturb = false), + BezierAvatarBadge.Chat, + BezierAvatarBadge.Integration.Kakao, + ) + + BezierTheme { + Column( + modifier = Modifier + .fillMaxSize() + .padding(start = 20.dp, top = 20.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Row( + verticalAlignment = Alignment.Bottom, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + BezierAvatarSize.entries.forEach { size -> + BezierAvatar( + painter = painterResource(id = R.drawable.unknown), + size = size, + showBorder = false, + ) + } + } + + badges.forEach { badge -> + Row( + verticalAlignment = Alignment.Bottom, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + BezierAvatarSize.entries.forEach { size -> + BezierAvatar( + painter = painterResource(id = R.drawable.unknown), + size = size, + showBorder = true, + badge = badge, + ) + } + } + } + } + } +} diff --git a/bezier/src/main/java/io/channel/bezier/compose/component/avatar/properties/BezierAvatarBadge.kt b/bezier/src/main/java/io/channel/bezier/compose/component/avatar/properties/BezierAvatarBadge.kt new file mode 100644 index 00000000..23e044a5 --- /dev/null +++ b/bezier/src/main/java/io/channel/bezier/compose/component/avatar/properties/BezierAvatarBadge.kt @@ -0,0 +1,46 @@ +package io.channel.bezier.compose.component.avatar.properties + +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.res.painterResource +import io.channel.bezier.compose.R +import io.channel.bezier.compose.component.avatar.AvatarRadiusFraction +import io.channel.bezier.shape.SmoothRoundedCornerShape + +@Stable +sealed interface BezierAvatarBadge { + data object None : BezierAvatarBadge + + data class Status(val isOnline: Boolean, val doNotDisturb: Boolean) : BezierAvatarBadge + + data object Chat : BezierAvatarBadge + + enum class Integration( + internal val painter: @Composable () -> Painter + ) : BezierAvatarBadge { + Kakao(painter = { painterResource(id = R.drawable.image_kakao) }), + Line(painter = { painterResource(id = R.drawable.image_line) }), + NaverTalk(painter = { painterResource(id = R.drawable.image_talktalk) }), + Instagram(painter = { painterResource(id = R.drawable.image_instagram) }), + PhoneNumber(painter = { painterResource(id = R.drawable.image_avatar_call) }), + Email(painter = { painterResource(id = R.drawable.image_email) }); + + fun createShape(size: BezierAvatarSize): Shape { + return when (size) { + BezierAvatarSize.Size16, + BezierAvatarSize.Size20, + BezierAvatarSize.Size24, + BezierAvatarSize.Size30, + BezierAvatarSize.Size36 -> CircleShape + BezierAvatarSize.Size42, + BezierAvatarSize.Size48, + BezierAvatarSize.Size72, + BezierAvatarSize.Size90, + BezierAvatarSize.Size120 -> SmoothRoundedCornerShape(AvatarRadiusFraction) + } + } + } +} diff --git a/bezier/src/main/java/io/channel/bezier/compose/component/avatar/properties/BezierAvatarSize.kt b/bezier/src/main/java/io/channel/bezier/compose/component/avatar/properties/BezierAvatarSize.kt new file mode 100644 index 00000000..3ec8835f --- /dev/null +++ b/bezier/src/main/java/io/channel/bezier/compose/component/avatar/properties/BezierAvatarSize.kt @@ -0,0 +1,101 @@ +package io.channel.bezier.compose.component.avatar.properties + +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import io.channel.bezier.compose.component.badge.chat.properties.BezierChatBadgeSize +import io.channel.bezier.compose.component.badge.status.properties.BezierStatusBadgeSize + +/** + * 정의되지 않은 값에 대한 fallback 입니다. + */ +private val DefaultIntegrationBadgeSize = 16.dp + +enum class BezierAvatarSize( + internal val size: Dp, + internal val outlineBorderWidth: Dp, + internal val statusBadgeSize: BezierStatusBadgeSize, + internal val chatBadgeSize: BezierChatBadgeSize, + internal val integrationBadgeSize: Dp, + internal val badgeOffset: Dp, +) { + Size16( + size = 16.dp, + outlineBorderWidth = 2.dp, + statusBadgeSize = BezierStatusBadgeSize.Medium, + chatBadgeSize = BezierChatBadgeSize.Medium, + integrationBadgeSize = DefaultIntegrationBadgeSize, + badgeOffset = 4.dp, + ), + Size20( + size = 20.dp, + outlineBorderWidth = 2.dp, + statusBadgeSize = BezierStatusBadgeSize.Medium, + chatBadgeSize = BezierChatBadgeSize.Medium, + integrationBadgeSize = DefaultIntegrationBadgeSize, + badgeOffset = 4.dp, + ), + Size24( + size = 24.dp, + outlineBorderWidth = 2.dp, + statusBadgeSize = BezierStatusBadgeSize.Medium, + chatBadgeSize = BezierChatBadgeSize.Medium, + integrationBadgeSize = 16.dp, + badgeOffset = 4.dp, + ), + Size30( + size = 30.dp, + outlineBorderWidth = 2.dp, + statusBadgeSize = BezierStatusBadgeSize.Medium, + chatBadgeSize = BezierChatBadgeSize.Medium, + integrationBadgeSize = DefaultIntegrationBadgeSize, + badgeOffset = 4.dp, + ), + Size36( + size = 36.dp, + outlineBorderWidth = 2.dp, + statusBadgeSize = BezierStatusBadgeSize.Medium, + chatBadgeSize = BezierChatBadgeSize.Medium, + integrationBadgeSize = DefaultIntegrationBadgeSize, + badgeOffset = 3.dp, + ), + Size42( + size = 42.dp, + outlineBorderWidth = 2.dp, + statusBadgeSize = BezierStatusBadgeSize.Medium, + chatBadgeSize = BezierChatBadgeSize.Medium, + integrationBadgeSize = 20.dp, + badgeOffset = 2.dp, + ), + Size48( + size = 48.dp, + outlineBorderWidth = 2.dp, + statusBadgeSize = BezierStatusBadgeSize.Medium, + chatBadgeSize = BezierChatBadgeSize.Medium, + integrationBadgeSize = 20.dp, + badgeOffset = 2.dp, + ), + Size72( + size = 72.dp, + outlineBorderWidth = 2.dp, + statusBadgeSize = BezierStatusBadgeSize.Large, + chatBadgeSize = BezierChatBadgeSize.Large, + integrationBadgeSize = DefaultIntegrationBadgeSize, + badgeOffset = (-2).dp, + ), + Size90( + size = 90.dp, + outlineBorderWidth = 2.dp, + statusBadgeSize = BezierStatusBadgeSize.Large, + chatBadgeSize = BezierChatBadgeSize.Large, + integrationBadgeSize = DefaultIntegrationBadgeSize, + badgeOffset = (-2).dp, + ), + Size120( + size = 120.dp, + outlineBorderWidth = 4.dp, + statusBadgeSize = BezierStatusBadgeSize.Large, + chatBadgeSize = BezierChatBadgeSize.Large, + integrationBadgeSize = DefaultIntegrationBadgeSize, + badgeOffset = (-2).dp, + ), +} diff --git a/bezier/src/main/res/drawable-hdpi/image_avatar_call.png b/bezier/src/main/res/drawable-hdpi/image_avatar_call.png new file mode 100644 index 00000000..9d62fe92 Binary files /dev/null and b/bezier/src/main/res/drawable-hdpi/image_avatar_call.png differ diff --git a/bezier/src/main/res/drawable-hdpi/image_email.png b/bezier/src/main/res/drawable-hdpi/image_email.png new file mode 100644 index 00000000..bfffc3d0 Binary files /dev/null and b/bezier/src/main/res/drawable-hdpi/image_email.png differ diff --git a/bezier/src/main/res/drawable-hdpi/image_instagram.png b/bezier/src/main/res/drawable-hdpi/image_instagram.png new file mode 100644 index 00000000..03e0741d Binary files /dev/null and b/bezier/src/main/res/drawable-hdpi/image_instagram.png differ diff --git a/bezier/src/main/res/drawable-hdpi/image_kakao.png b/bezier/src/main/res/drawable-hdpi/image_kakao.png new file mode 100644 index 00000000..23915dfb Binary files /dev/null and b/bezier/src/main/res/drawable-hdpi/image_kakao.png differ diff --git a/bezier/src/main/res/drawable-hdpi/image_line.png b/bezier/src/main/res/drawable-hdpi/image_line.png new file mode 100644 index 00000000..c2fa5c6a Binary files /dev/null and b/bezier/src/main/res/drawable-hdpi/image_line.png differ diff --git a/bezier/src/main/res/drawable-hdpi/image_talktalk.png b/bezier/src/main/res/drawable-hdpi/image_talktalk.png new file mode 100644 index 00000000..4e7d6d41 Binary files /dev/null and b/bezier/src/main/res/drawable-hdpi/image_talktalk.png differ diff --git a/bezier/src/main/res/drawable-hdpi/image_text.png b/bezier/src/main/res/drawable-hdpi/image_text.png new file mode 100644 index 00000000..ff2c03a8 Binary files /dev/null and b/bezier/src/main/res/drawable-hdpi/image_text.png differ diff --git a/bezier/src/main/res/drawable-mdpi/image_avatar_call.png b/bezier/src/main/res/drawable-mdpi/image_avatar_call.png new file mode 100644 index 00000000..3d60f698 Binary files /dev/null and b/bezier/src/main/res/drawable-mdpi/image_avatar_call.png differ diff --git a/bezier/src/main/res/drawable-mdpi/image_email.png b/bezier/src/main/res/drawable-mdpi/image_email.png new file mode 100644 index 00000000..51e0f85c Binary files /dev/null and b/bezier/src/main/res/drawable-mdpi/image_email.png differ diff --git a/bezier/src/main/res/drawable-mdpi/image_instagram.png b/bezier/src/main/res/drawable-mdpi/image_instagram.png new file mode 100644 index 00000000..db398303 Binary files /dev/null and b/bezier/src/main/res/drawable-mdpi/image_instagram.png differ diff --git a/bezier/src/main/res/drawable-mdpi/image_kakao.png b/bezier/src/main/res/drawable-mdpi/image_kakao.png new file mode 100644 index 00000000..1ced82cb Binary files /dev/null and b/bezier/src/main/res/drawable-mdpi/image_kakao.png differ diff --git a/bezier/src/main/res/drawable-mdpi/image_line.png b/bezier/src/main/res/drawable-mdpi/image_line.png new file mode 100644 index 00000000..d377b784 Binary files /dev/null and b/bezier/src/main/res/drawable-mdpi/image_line.png differ diff --git a/bezier/src/main/res/drawable-mdpi/image_talktalk.png b/bezier/src/main/res/drawable-mdpi/image_talktalk.png new file mode 100644 index 00000000..3c95ba63 Binary files /dev/null and b/bezier/src/main/res/drawable-mdpi/image_talktalk.png differ diff --git a/bezier/src/main/res/drawable-mdpi/image_text.png b/bezier/src/main/res/drawable-mdpi/image_text.png new file mode 100644 index 00000000..65830790 Binary files /dev/null and b/bezier/src/main/res/drawable-mdpi/image_text.png differ diff --git a/bezier/src/main/res/drawable-xhdpi/image_avatar_call.png b/bezier/src/main/res/drawable-xhdpi/image_avatar_call.png new file mode 100644 index 00000000..7efa7dff Binary files /dev/null and b/bezier/src/main/res/drawable-xhdpi/image_avatar_call.png differ diff --git a/bezier/src/main/res/drawable-xhdpi/image_email.png b/bezier/src/main/res/drawable-xhdpi/image_email.png new file mode 100644 index 00000000..68846b84 Binary files /dev/null and b/bezier/src/main/res/drawable-xhdpi/image_email.png differ diff --git a/bezier/src/main/res/drawable-xhdpi/image_instagram.png b/bezier/src/main/res/drawable-xhdpi/image_instagram.png new file mode 100644 index 00000000..14d891ea Binary files /dev/null and b/bezier/src/main/res/drawable-xhdpi/image_instagram.png differ diff --git a/bezier/src/main/res/drawable-xhdpi/image_kakao.png b/bezier/src/main/res/drawable-xhdpi/image_kakao.png new file mode 100644 index 00000000..47dc667d Binary files /dev/null and b/bezier/src/main/res/drawable-xhdpi/image_kakao.png differ diff --git a/bezier/src/main/res/drawable-xhdpi/image_line.png b/bezier/src/main/res/drawable-xhdpi/image_line.png new file mode 100644 index 00000000..92938363 Binary files /dev/null and b/bezier/src/main/res/drawable-xhdpi/image_line.png differ diff --git a/bezier/src/main/res/drawable-xhdpi/image_talktalk.png b/bezier/src/main/res/drawable-xhdpi/image_talktalk.png new file mode 100644 index 00000000..b04e6ddd Binary files /dev/null and b/bezier/src/main/res/drawable-xhdpi/image_talktalk.png differ diff --git a/bezier/src/main/res/drawable-xhdpi/image_text.png b/bezier/src/main/res/drawable-xhdpi/image_text.png new file mode 100644 index 00000000..72686685 Binary files /dev/null and b/bezier/src/main/res/drawable-xhdpi/image_text.png differ diff --git a/bezier/src/main/res/drawable-xxhdpi/image_avatar_call.png b/bezier/src/main/res/drawable-xxhdpi/image_avatar_call.png new file mode 100644 index 00000000..7bbe50ab Binary files /dev/null and b/bezier/src/main/res/drawable-xxhdpi/image_avatar_call.png differ diff --git a/bezier/src/main/res/drawable-xxhdpi/image_email.png b/bezier/src/main/res/drawable-xxhdpi/image_email.png new file mode 100644 index 00000000..336e7f2c Binary files /dev/null and b/bezier/src/main/res/drawable-xxhdpi/image_email.png differ diff --git a/bezier/src/main/res/drawable-xxhdpi/image_instagram.png b/bezier/src/main/res/drawable-xxhdpi/image_instagram.png new file mode 100644 index 00000000..31697aad Binary files /dev/null and b/bezier/src/main/res/drawable-xxhdpi/image_instagram.png differ diff --git a/bezier/src/main/res/drawable-xxhdpi/image_kakao.png b/bezier/src/main/res/drawable-xxhdpi/image_kakao.png new file mode 100644 index 00000000..09207638 Binary files /dev/null and b/bezier/src/main/res/drawable-xxhdpi/image_kakao.png differ diff --git a/bezier/src/main/res/drawable-xxhdpi/image_line.png b/bezier/src/main/res/drawable-xxhdpi/image_line.png new file mode 100644 index 00000000..9a454e04 Binary files /dev/null and b/bezier/src/main/res/drawable-xxhdpi/image_line.png differ diff --git a/bezier/src/main/res/drawable-xxhdpi/image_talktalk.png b/bezier/src/main/res/drawable-xxhdpi/image_talktalk.png new file mode 100644 index 00000000..1b7252a5 Binary files /dev/null and b/bezier/src/main/res/drawable-xxhdpi/image_talktalk.png differ diff --git a/bezier/src/main/res/drawable-xxhdpi/image_text.png b/bezier/src/main/res/drawable-xxhdpi/image_text.png new file mode 100644 index 00000000..d33da0e4 Binary files /dev/null and b/bezier/src/main/res/drawable-xxhdpi/image_text.png differ diff --git a/bezier/src/main/res/drawable-xxxhdpi/image_avatar_call.png b/bezier/src/main/res/drawable-xxxhdpi/image_avatar_call.png new file mode 100644 index 00000000..3b32a085 Binary files /dev/null and b/bezier/src/main/res/drawable-xxxhdpi/image_avatar_call.png differ diff --git a/bezier/src/main/res/drawable-xxxhdpi/image_email.png b/bezier/src/main/res/drawable-xxxhdpi/image_email.png new file mode 100644 index 00000000..a0fcf334 Binary files /dev/null and b/bezier/src/main/res/drawable-xxxhdpi/image_email.png differ diff --git a/bezier/src/main/res/drawable-xxxhdpi/image_instagram.png b/bezier/src/main/res/drawable-xxxhdpi/image_instagram.png new file mode 100644 index 00000000..bc5da470 Binary files /dev/null and b/bezier/src/main/res/drawable-xxxhdpi/image_instagram.png differ diff --git a/bezier/src/main/res/drawable-xxxhdpi/image_kakao.png b/bezier/src/main/res/drawable-xxxhdpi/image_kakao.png new file mode 100644 index 00000000..3172fec4 Binary files /dev/null and b/bezier/src/main/res/drawable-xxxhdpi/image_kakao.png differ diff --git a/bezier/src/main/res/drawable-xxxhdpi/image_line.png b/bezier/src/main/res/drawable-xxxhdpi/image_line.png new file mode 100644 index 00000000..755f4e89 Binary files /dev/null and b/bezier/src/main/res/drawable-xxxhdpi/image_line.png differ diff --git a/bezier/src/main/res/drawable-xxxhdpi/image_talktalk.png b/bezier/src/main/res/drawable-xxxhdpi/image_talktalk.png new file mode 100644 index 00000000..09cd93a3 Binary files /dev/null and b/bezier/src/main/res/drawable-xxxhdpi/image_talktalk.png differ diff --git a/bezier/src/main/res/drawable-xxxhdpi/image_text.png b/bezier/src/main/res/drawable-xxxhdpi/image_text.png new file mode 100644 index 00000000..441107f6 Binary files /dev/null and b/bezier/src/main/res/drawable-xxxhdpi/image_text.png differ