diff --git a/app/src/main/java/com/ggaebiz/ggaebiz/di/AppModule.kt b/app/src/main/java/com/ggaebiz/ggaebiz/di/AppModule.kt index 4100827..999891d 100644 --- a/app/src/main/java/com/ggaebiz/ggaebiz/di/AppModule.kt +++ b/app/src/main/java/com/ggaebiz/ggaebiz/di/AppModule.kt @@ -89,7 +89,7 @@ val appModule = module { viewModel { TimerViewModel(get(), get(), get(), get(), get(), get(), get(), get()) } viewModel { AlarmViewModel(get(), get(), get(), get(), get(), get(), get(), get()) } viewModel { ConfigViewModel(get()) } - viewModel { EditorViewModel(get(), get()) } + viewModel { EditorViewModel(get(), get(), get()) } viewModel { EditorResultViewModel(get(), get()) } viewModel { ProofCardViewModel(get(), get(), get(), get(), get()) } diff --git a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/designsystem/theme/Type.kt b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/designsystem/theme/Type.kt index 416e619..32a82d1 100644 --- a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/designsystem/theme/Type.kt +++ b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/designsystem/theme/Type.kt @@ -64,6 +64,22 @@ val ZuumeFont = FontFamily( Font(R.font.zuume_bold, FontWeight.Bold) ) +val PoppinsFont = FontFamily( + Font(R.font.poppins_medium, FontWeight.Medium), + Font(R.font.poppins_semibold, FontWeight.SemiBold), + Font(R.font.poppins_bold, FontWeight.Bold)) + +val DigitalNumbersFont = FontFamily( + Font(R.font.digital_numbers_regular, FontWeight.SemiBold)) + +val TimeLineFont = FontFamily( + Font(R.font.timeline_bold, FontWeight.Bold) +) + +val AgbalumoFont = FontFamily( + Font(R.font.agbalumo_regular, FontWeight.Medium) +) + val Typography = GaeBizTypography( titleBold = TextStyle( fontFamily = PretendardFont, diff --git a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/model/Sticker.kt b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/model/Sticker.kt index 935d4cd..525e100 100644 --- a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/model/Sticker.kt +++ b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/model/Sticker.kt @@ -13,6 +13,27 @@ data class Sticker( val source: StickerSource ) +enum class TimeStampStyleType { + ORANGE_ZUUME, + GRAY_ZUUME, + ORANGE_POPPINS, + GRAY_POPPINS, + DARK_ZUUME, + DARK_DIGITAL, + WIHTE_TIMELINE, + GRAY_TIMELINE, + WHITE_AGBALUMO, + GRAY_AGBALUMO, + ORANGE_POPPINS_TIME_FORMAT, + GRAY_POPPINS_TIME_FORMAT, + WHITE_POPPINS_TIME_CLOCK_FORMAT, + GRAY_POPPINS_TIME_CLOCK_FORMAT, +} + sealed interface StickerSource { data class Png(@DrawableRes val resId: Int) : StickerSource + data class TimeStamp( + val text: String, + val styleType: TimeStampStyleType, + ) : StickerSource } diff --git a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/Component.kt b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/Component.kt index fc30857..138f61d 100644 --- a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/Component.kt +++ b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/Component.kt @@ -8,14 +8,20 @@ import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn 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.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.shape.CircleShape @@ -26,8 +32,14 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.key +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.tooling.preview.Preview +import com.ggaebiz.ggaebiz.presentation.model.TimeStampStyleType +import com.ggaebiz.ggaebiz.presentation.ui.proof.editor.generateTimeStampSources +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -36,16 +48,31 @@ import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.ggaebiz.ggaebiz.R +import com.ggaebiz.ggaebiz.presentation.designsystem.theme.DigitalNumbersFont import com.ggaebiz.ggaebiz.presentation.designsystem.theme.GaeBizTheme +import com.ggaebiz.ggaebiz.presentation.designsystem.theme.PoppinsFont +import com.ggaebiz.ggaebiz.presentation.designsystem.theme.AgbalumoFont +import com.ggaebiz.ggaebiz.presentation.designsystem.theme.TimeLineFont +import com.ggaebiz.ggaebiz.presentation.designsystem.theme.ZuumeFont import com.ggaebiz.ggaebiz.presentation.model.Sticker import com.ggaebiz.ggaebiz.presentation.model.StickerSource +import com.ggaebiz.ggaebiz.presentation.ui.proof.editor.TimeStampColor +import com.ggaebiz.ggaebiz.presentation.ui.proof.editor.TimeStampStyle +import com.ggaebiz.ggaebiz.presentation.ui.proof.editor.toStyle import kotlin.math.PI import kotlin.math.cos import kotlin.math.sin @@ -139,16 +166,31 @@ fun StickersCanvas( val currentOffset by rememberUpdatedState(s.offset) val currentScale by rememberUpdatedState(s.scale) val currentRotation by rememberUpdatedState(s.rotation) + val isTimeStamp = s.source is StickerSource.TimeStamp + var measuredSize by remember(s.id) { mutableStateOf(IntSize.Zero) } Box( modifier = Modifier .offset { - IntOffset( - (s.offset.x - sizePx / 2f).toInt(), - (s.offset.y - sizePx / 2f).toInt() - ) + if (isTimeStamp) { + IntOffset( + (s.offset.x - measuredSize.width * s.scale / 2f).toInt(), + (s.offset.y - measuredSize.height * s.scale / 2f).toInt() + ) + } else { + IntOffset( + (s.offset.x - sizePx / 2f).toInt(), + (s.offset.y - sizePx / 2f).toInt() + ) + } + } + .then(if (isTimeStamp) Modifier.wrapContentSize() else Modifier.size(sizeDp)) + .onSizeChanged { if (isTimeStamp) measuredSize = it } + .graphicsLayer { + if (isTimeStamp) { + scaleX = s.scale; scaleY = s.scale + } + rotationZ = s.rotation } - .size(sizeDp) - .graphicsLayer { rotationZ = s.rotation } .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null @@ -181,13 +223,15 @@ fun StickersCanvas( ) } ) { - when (s.source) { + when (val src = s.source) { is StickerSource.Png -> Image( - painter = painterResource(id = s.source.resId), + painter = painterResource(id = src.resId), contentDescription = null, modifier = Modifier.fillMaxSize(), contentScale = ContentScale.Fit ) + + is StickerSource.TimeStamp -> TimeStampSticker(source = src) } if (isSelected) { @@ -278,22 +322,26 @@ fun StickersCanvas( fun StickerPickerGrid( sources: List, onPick: (StickerSource) -> Unit, + columns: Int = 3, + horizontalPadding: Dp = 16.dp, + horizontalSpacing: Dp = 8.dp, ) { LazyVerticalGrid( - columns = GridCells.Fixed(3), + columns = GridCells.Fixed(columns), modifier = Modifier .fillMaxWidth() .heightIn(min = 120.dp, max = 300.dp) .nestedScroll(rememberNestedScrollInteropConnection()) - .padding(horizontal = 16.dp, vertical = 8.dp), - verticalArrangement = Arrangement.spacedBy(12.dp), - horizontalArrangement = Arrangement.spacedBy(12.dp) + .padding(horizontal = horizontalPadding, vertical = 8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(horizontalSpacing) ) { items(sources.size) { idx -> val src = sources[idx] Box( modifier = Modifier - .aspectRatio(1f) + .fillMaxWidth() + .then(if (src is StickerSource.Png) Modifier.aspectRatio(1f) else Modifier) .clickable { onPick(src) }, contentAlignment = Alignment.Center ) { @@ -304,8 +352,362 @@ fun StickerPickerGrid( modifier = Modifier.fillMaxSize(), contentScale = ContentScale.Fit ) + + is StickerSource.TimeStamp -> TimeStampStickerPreview(source = src) } } } } } + +@Composable +fun TimeStampSticker(source: StickerSource.TimeStamp) { + when (val style = source.styleType.toStyle()) { + is TimeStampStyle.Zuume -> TimestampZuume(source.text, style.color) + is TimeStampStyle.Poppins -> TimestampPoppins(source.text, style.color) + is TimeStampStyle.Digital -> TimestampDigital(source.text, style.color) + is TimeStampStyle.Timeline -> TimestampTimeline(source.text, style.color) + is TimeStampStyle.Agbalumo -> TimestampAgbalumo(source.text, style.color) + is TimeStampStyle.PoppinsTimeFormat -> TimestampPoppinsTimeFormat(source.text, style.color) + is TimeStampStyle.PoppinsClockFormat -> TimestampPoppinsClockFormat( + source.text, + style.color + ) + } +} + +@Composable +private fun TimeStampStickerPreview(source: StickerSource.TimeStamp) { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + contentAlignment = Alignment.Center, + ) { + val stickerSize = source.styleType.previewSize + val scale = minOf(1f, maxWidth / stickerSize.width) + + Box( + modifier = Modifier + .fillMaxWidth() + .height(stickerSize.height * scale), + contentAlignment = Alignment.Center, + ) { + Box( + modifier = Modifier + .requiredSize(stickerSize.width, stickerSize.height) + .graphicsLayer { + scaleX = scale + scaleY = scale + }, + contentAlignment = Alignment.Center, + ) { + TimeStampSticker(source = source) + } + } + } +} + +private val TimeStampStyleType.previewSize + get() = when (this) { + TimeStampStyleType.ORANGE_ZUUME, + TimeStampStyleType.GRAY_ZUUME, + TimeStampStyleType.DARK_ZUUME, + -> DpSize(width = 140.dp, height = 80.dp) + + TimeStampStyleType.DARK_DIGITAL -> DpSize(width = 140.dp, height = 64.dp) + + TimeStampStyleType.ORANGE_POPPINS, + TimeStampStyleType.GRAY_POPPINS, + -> DpSize(width = 128.dp, height = 64.dp) + + TimeStampStyleType.WIHTE_TIMELINE, + TimeStampStyleType.GRAY_TIMELINE, + -> DpSize(width = 137.dp, height = 80.dp) + + TimeStampStyleType.WHITE_AGBALUMO, + TimeStampStyleType.GRAY_AGBALUMO, + -> DpSize(width = 124.dp, height = 80.dp) + + TimeStampStyleType.ORANGE_POPPINS_TIME_FORMAT, + TimeStampStyleType.GRAY_POPPINS_TIME_FORMAT, + -> DpSize(width = 150.dp, height = 56.dp) + + TimeStampStyleType.WHITE_POPPINS_TIME_CLOCK_FORMAT, + TimeStampStyleType.GRAY_POPPINS_TIME_CLOCK_FORMAT, + -> DpSize(width = 80.dp, height = 140.dp) + } + +@Composable +private fun TimestampZuume(text: String, color: TimeStampColor) { + val hasBackground = color.backgroundColor != null + Box( + modifier = Modifier + .background(color.backgroundColor ?: Color.Transparent, RoundedCornerShape(40)) + .size(width = 140.dp, height = 80.dp), + contentAlignment = Alignment.Center, + ) { + Text( + fontFamily = ZuumeFont, + text = text, + color = color.textColor, + fontWeight = FontWeight.SemiBold, + fontSize = 40.sp, + lineHeight = 80.sp, + letterSpacing = 1.sp, + maxLines = 1, + ) + } +} + +@Composable +private fun TimestampPoppins(text: String, color: TimeStampColor) { + Box( + modifier = Modifier + .background(color.backgroundColor ?: Color.Transparent, RoundedCornerShape(40)) + .size(width = 128.dp, height = 64.dp), + contentAlignment = Alignment.Center, + ) { + Text( + fontFamily = PoppinsFont, + text = text, + color = color.textColor, + fontWeight = FontWeight.SemiBold, + fontSize = 32.sp, + lineHeight = 64.sp, + maxLines = 1, + ) + } +} + + +@Composable +private fun TimestampDigital(text: String, color: TimeStampColor) { + val parts = text.split(":", limit = 2) + val hour = parts.getOrElse(0) { text }.trim() + val minute = parts.getOrElse(1) { "" }.trim() + + Box( + modifier = Modifier + .background(color.backgroundColor ?: Color.Transparent, RoundedCornerShape(12)) + .size(width = 140.dp, height = 64.dp), + contentAlignment = Alignment.Center, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + ) { + Text( + fontFamily = DigitalNumbersFont, + text = hour, + color = color.textColor, + fontWeight = FontWeight.Normal, + fontSize = 32.sp, + lineHeight = 64.sp, + maxLines = 1, + ) + Text( + text = ":", + color = color.textColor, + fontWeight = FontWeight.Normal, + fontFamily = DigitalNumbersFont, + fontSize = 32.sp, + lineHeight = 64.sp, + maxLines = 1, + modifier = Modifier.width(10.dp), + textAlign = TextAlign.Center, + ) + Text( + fontFamily = DigitalNumbersFont, + text = minute, + color = color.textColor, + fontWeight = FontWeight.Normal, + fontSize = 32.sp, + lineHeight = 64.sp, + maxLines = 1, + ) + } + } +} + +@Composable +private fun TimestampTimeline(text: String, color: TimeStampColor) { + Box( + modifier = Modifier.size(width = 137.dp, height = 80.dp), + contentAlignment = Alignment.Center, + ) { + Text( + fontFamily = TimeLineFont, + text = text, + color = color.textColor, + fontWeight = FontWeight.Normal, + fontSize = 40.sp, + lineHeight = 80.sp, + maxLines = 1, + letterSpacing = 1.sp + ) + } +} + +@Composable +private fun TimestampAgbalumo(text: String, color: TimeStampColor) { + Box( + modifier = Modifier + .size(width = 124.dp, height = 80.dp), + contentAlignment = Alignment.BottomCenter, + ) { + Text( + fontFamily = AgbalumoFont, + text = text, + color = color.textColor, + fontWeight = FontWeight.Normal, + fontSize = 40.sp, + lineHeight = 80.sp, + maxLines = 1, + letterSpacing = 1.sp + ) + } +} + +@Composable +private fun TimestampPoppinsTimeFormat(text: String, color: TimeStampColor) { + Box( + modifier = Modifier + .background(color.backgroundColor ?: Color.Transparent, RoundedCornerShape(50)) + .size(width = 150.dp, height = 56.dp), + contentAlignment = Alignment.Center, + ) { + Text( + fontFamily = PoppinsFont, + text = text, + color = color.textColor, + fontWeight = FontWeight.SemiBold, + fontSize = 24.sp, + lineHeight = 48.sp, + maxLines = 1, + letterSpacing = 1.sp + ) + } +} + +@Composable +private fun TimestampPoppinsClockFormat(text: String, color: TimeStampColor) { + val parts = text.split(":") + val top = parts.getOrElse(0) { text }.trim() + val bottom = parts.getOrElse(1) { "" }.trim() + Box( + modifier = Modifier.size(width = 80.dp, height = 140.dp), + contentAlignment = Alignment.Center, + ) { + Text( + fontFamily = PoppinsFont, + text = "$top\n$bottom", + color = color.textColor, + fontWeight = FontWeight.Medium, + fontSize = 40.sp, + lineHeight = 40.sp, + maxLines = 2, + letterSpacing = 1.sp + ) + } +} + + +// ─── Previews ──────────────────────────────────────────────────────────────── + +@Preview(name = "Tabs - 타임스탬프 선택", showBackground = true, apiLevel = 34) +@Composable +private fun Segmented2TabsLeftPreview() { + GaeBizTheme { + Segmented2Tabs( + left = "타임 스탬프", + right = "스티커", + selectedRight = false, + onSelectLeft = {}, + onSelectRight = {}, + ) + } +} + +@Preview(name = "Tabs - 스티커 선택", showBackground = true, apiLevel = 34) +@Composable +private fun Segmented2TabsRightPreview() { + GaeBizTheme { + Segmented2Tabs( + left = "타임 스탬프", + right = "스티커", + selectedRight = true, + onSelectLeft = {}, + onSelectRight = {}, + ) + } +} + +@Preview( + name = "StickerPickerGrid - 타임스탬프 (2열, 전체)", + showBackground = true, + backgroundColor = 0xFF1C1C1C, + apiLevel = 34 +) +@Composable +private fun StickerPickerGridTimestampPreview() { + GaeBizTheme { + val sources = generateTimeStampSources(hour = 0, minute = 30) + StickerPickerGrid( + sources = sources, + columns = 2, + onPick = {}, + ) + } +} + +@Preview( + name = "StickerPickerGrid - 스티커 (3열)", + showBackground = true, + backgroundColor = 0xFF1C1C1C, + apiLevel = 34 +) +@Composable +private fun StickerPickerGridStickerPreview() { + GaeBizTheme { + StickerPickerGrid( + sources = listOf( + StickerSource.Png(R.drawable.sticker_1), + StickerSource.Png(R.drawable.sticker_2), + StickerSource.Png(R.drawable.sticker_3), + StickerSource.Png(R.drawable.sticker_4), + StickerSource.Png(R.drawable.sticker_5), + StickerSource.Png(R.drawable.sticker_6), + ), + columns = 3, + onPick = {}, + ) + } +} + +@Preview( + name = "TimeStampSticker - 전체 스타일", + showBackground = true, + backgroundColor = 0xFF333333, + apiLevel = 34 +) +@Composable +private fun TimeStampStickerAllStylesPreview() { + GaeBizTheme { + Column( + modifier = Modifier.padding(20.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + TimeStampStyleType.entries.forEach { styleType -> + val text = if (styleType == TimeStampStyleType.ORANGE_ZUUME) "0h 30min" else "00:30" + TimeStampSticker( + source = StickerSource.TimeStamp( + text = text, + styleType = styleType + ) + ) + } + } + } +} diff --git a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/EditorContract.kt b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/EditorContract.kt index 7a4d8e4..e6295bc 100644 --- a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/EditorContract.kt +++ b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/EditorContract.kt @@ -9,7 +9,7 @@ import com.ggaebiz.ggaebiz.presentation.model.Sticker import com.ggaebiz.ggaebiz.presentation.model.StickerSource data class EditorState( - val selectTab : SelectTab = SelectTab.STICKER, + val selectTab : SelectTab = SelectTab.TIME_STAMP, val imageUri : Uri?, val previewBitmap : ImageBitmap? = null, val isImageLoadError : Boolean = false, diff --git a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/EditorScreen.kt b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/EditorScreen.kt index 3262c40..c261c05 100644 --- a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/EditorScreen.kt +++ b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/EditorScreen.kt @@ -137,17 +137,19 @@ fun PhotoEditorScreen( ) }, sheetContent = { - // TODO: 타임스탬프 기능 추가 시 주석 해제 - // Segmented2Tabs( - // left = "타임 스탬프", - // right = "스티커", - // selectedRight = uiState.selectTab == SelectTab.STICKER, - // onSelectLeft = { processIntent(EditorIntent.ChangeTab(SelectTab.TIME_STAMP)) }, - // onSelectRight = { processIntent(EditorIntent.ChangeTab(SelectTab.STICKER)) }, - // modifier = Modifier.padding(top = 8.dp) - // ) + Segmented2Tabs( + left = "타임 스탬프", + right = "스티커", + selectedRight = uiState.selectTab == SelectTab.STICKER, + onSelectLeft = { processIntent(EditorIntent.ChangeTab(SelectTab.TIME_STAMP)) }, + onSelectRight = { processIntent(EditorIntent.ChangeTab(SelectTab.STICKER)) }, + modifier = Modifier.padding(top = 8.dp) + ) StickerPickerGrid( sources = uiState.nowSource, + columns = if (uiState.selectTab == SelectTab.TIME_STAMP) 2 else 3, + horizontalPadding = if (uiState.selectTab == SelectTab.TIME_STAMP) 0.dp else 16.dp, + horizontalSpacing = if (uiState.selectTab == SelectTab.TIME_STAMP) 0.dp else 12.dp, onPick = { src -> processIntent( EditorIntent.OnPick( diff --git a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/EditorViewModel.kt b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/EditorViewModel.kt index 177215b..850d1b9 100644 --- a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/EditorViewModel.kt +++ b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/EditorViewModel.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.asImageBitmap import androidx.lifecycle.SavedStateHandle import com.ggaebiz.ggaebiz.domain.usecase.CreateCachedImageUseCase +import com.ggaebiz.ggaebiz.domain.usecase.GetCurrentTimerUseCase import com.ggaebiz.ggaebiz.presentation.common.base.BaseViewModel import com.ggaebiz.ggaebiz.presentation.model.Sticker import com.ggaebiz.ggaebiz.presentation.ui.proof.loadBitmapFromUri @@ -13,12 +14,20 @@ import kotlinx.coroutines.withContext class EditorViewModel( savedStateHandle: SavedStateHandle, - private val createCachedImageUseCase: CreateCachedImageUseCase + private val createCachedImageUseCase: CreateCachedImageUseCase, + private val getCurrentTimerUseCase: GetCurrentTimerUseCase, ) : BaseViewModel( EditorState( - imageUri = savedStateHandle.get("uri")?.let(Uri::parse) + imageUri = savedStateHandle.get("uri")?.let(Uri::parse), ) ) { + init { + launch { + val (_, hour, minute, _) = getCurrentTimerUseCase() + updateState { it.copy(timeStampResource = generateTimeStampSources(hour, minute)) } + } + } + fun processIntent(intent: EditorIntent) { when (intent) { is EditorIntent.OnLoadImageData -> loadImageData(intent) diff --git a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/TimeStampGenerator.kt b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/TimeStampGenerator.kt new file mode 100644 index 0000000..50e64f9 --- /dev/null +++ b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/TimeStampGenerator.kt @@ -0,0 +1,25 @@ +package com.ggaebiz.ggaebiz.presentation.ui.proof.editor + +import com.ggaebiz.ggaebiz.presentation.model.StickerSource +import com.ggaebiz.ggaebiz.presentation.model.TimeStampStyleType + +fun generateTimeStampSources(hour: Int, minute: Int): List { + val hmText = "%02d : %02d".format(hour, minute) + val minuteText = "${hour}h ${minute}min" + return listOf( + StickerSource.TimeStamp(text = hmText, styleType = TimeStampStyleType.ORANGE_ZUUME), + StickerSource.TimeStamp(text = hmText, styleType = TimeStampStyleType.GRAY_ZUUME), + StickerSource.TimeStamp(text = hmText, styleType = TimeStampStyleType.ORANGE_POPPINS), + StickerSource.TimeStamp(text = hmText, styleType = TimeStampStyleType.GRAY_POPPINS), + StickerSource.TimeStamp(text = hmText, styleType = TimeStampStyleType.DARK_ZUUME), + StickerSource.TimeStamp(text = hmText, styleType = TimeStampStyleType.DARK_DIGITAL), + StickerSource.TimeStamp(text = hmText, styleType = TimeStampStyleType.WIHTE_TIMELINE), + StickerSource.TimeStamp(text = hmText, styleType = TimeStampStyleType.GRAY_TIMELINE), + StickerSource.TimeStamp(text = hmText, styleType = TimeStampStyleType.WHITE_AGBALUMO), + StickerSource.TimeStamp(text = hmText, styleType = TimeStampStyleType.GRAY_AGBALUMO), + StickerSource.TimeStamp(text = minuteText, styleType = TimeStampStyleType.ORANGE_POPPINS_TIME_FORMAT), + StickerSource.TimeStamp(text = minuteText, styleType = TimeStampStyleType.GRAY_POPPINS_TIME_FORMAT), + StickerSource.TimeStamp(text = hmText, styleType = TimeStampStyleType.WHITE_POPPINS_TIME_CLOCK_FORMAT), + StickerSource.TimeStamp(text = hmText, styleType = TimeStampStyleType.GRAY_POPPINS_TIME_CLOCK_FORMAT), + ) +} diff --git a/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/TimeStampStyle.kt b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/TimeStampStyle.kt new file mode 100644 index 0000000..cd419cd --- /dev/null +++ b/app/src/main/java/com/ggaebiz/ggaebiz/presentation/ui/proof/editor/TimeStampStyle.kt @@ -0,0 +1,54 @@ +package com.ggaebiz.ggaebiz.presentation.ui.proof.editor + +import androidx.compose.ui.graphics.Color +import com.ggaebiz.ggaebiz.presentation.designsystem.theme.GaeBizColorScheme +import com.ggaebiz.ggaebiz.presentation.model.TimeStampStyleType + +data class TimeStampColor( + val textColor: Color, + val backgroundColor: Color? = null, +) { + companion object { + val ORANGE_PILL = TimeStampColor(textColor = GaeBizColorScheme.white, backgroundColor = GaeBizColorScheme.primaryOrange) + val WHITE_PILL = TimeStampColor(textColor = GaeBizColorScheme.white, backgroundColor = GaeBizColorScheme.white16) + + val GRAY_OPACITY_PILL = TimeStampColor(textColor = GaeBizColorScheme.gray900, backgroundColor = GaeBizColorScheme.black20) + val WHITE_OPACITY_PILL = TimeStampColor(textColor = GaeBizColorScheme.white, backgroundColor = GaeBizColorScheme.black40) + + val WHITE_TEXT = TimeStampColor(textColor = GaeBizColorScheme.white) + val GRAY_TEXT = TimeStampColor(textColor = GaeBizColorScheme.gray900) } +} + +sealed class TimeStampStyle { + data class Zuume(val color: TimeStampColor) : TimeStampStyle() + data class Poppins(val color: TimeStampColor) : TimeStampStyle() + data class Digital(val color: TimeStampColor) : TimeStampStyle() + data class Timeline(val color: TimeStampColor) : TimeStampStyle() + data class Agbalumo(val color: TimeStampColor) : TimeStampStyle() + data class PoppinsTimeFormat(val color: TimeStampColor) : TimeStampStyle() + data class PoppinsClockFormat(val color: TimeStampColor) : TimeStampStyle() +} + +fun TimeStampStyleType.toStyle(): TimeStampStyle = when (this) { + TimeStampStyleType.ORANGE_ZUUME -> TimeStampStyle.Zuume(TimeStampColor.ORANGE_PILL) + TimeStampStyleType.GRAY_ZUUME -> TimeStampStyle.Zuume(TimeStampColor.WHITE_PILL) + + TimeStampStyleType.ORANGE_POPPINS -> TimeStampStyle.Poppins(TimeStampColor.ORANGE_PILL) + TimeStampStyleType.GRAY_POPPINS -> TimeStampStyle.Poppins(TimeStampColor.WHITE_PILL) + + TimeStampStyleType.DARK_ZUUME -> TimeStampStyle.Zuume(TimeStampColor.GRAY_OPACITY_PILL) + TimeStampStyleType.DARK_DIGITAL -> TimeStampStyle.Digital(TimeStampColor.WHITE_OPACITY_PILL) + + TimeStampStyleType.WIHTE_TIMELINE -> TimeStampStyle.Timeline(TimeStampColor.WHITE_TEXT) + TimeStampStyleType.GRAY_TIMELINE -> TimeStampStyle.Timeline(TimeStampColor.GRAY_TEXT) + + + TimeStampStyleType.WHITE_AGBALUMO -> TimeStampStyle.Agbalumo(TimeStampColor.WHITE_TEXT) + TimeStampStyleType.GRAY_AGBALUMO -> TimeStampStyle.Agbalumo(TimeStampColor.GRAY_TEXT) + + TimeStampStyleType.ORANGE_POPPINS_TIME_FORMAT -> TimeStampStyle.PoppinsTimeFormat(TimeStampColor.ORANGE_PILL) + TimeStampStyleType.GRAY_POPPINS_TIME_FORMAT -> TimeStampStyle.PoppinsTimeFormat(TimeStampColor.WHITE_PILL) + + TimeStampStyleType.WHITE_POPPINS_TIME_CLOCK_FORMAT -> TimeStampStyle.PoppinsClockFormat(TimeStampColor.WHITE_TEXT) + TimeStampStyleType.GRAY_POPPINS_TIME_CLOCK_FORMAT -> TimeStampStyle.PoppinsClockFormat(TimeStampColor.GRAY_TEXT) +} diff --git a/app/src/main/res/font/agbalumo_regular.ttf b/app/src/main/res/font/agbalumo_regular.ttf new file mode 100644 index 0000000..e5ccdca Binary files /dev/null and b/app/src/main/res/font/agbalumo_regular.ttf differ diff --git a/app/src/main/res/font/digital_numbers_regular.ttf b/app/src/main/res/font/digital_numbers_regular.ttf new file mode 100644 index 0000000..d6017ca Binary files /dev/null and b/app/src/main/res/font/digital_numbers_regular.ttf differ diff --git a/app/src/main/res/font/poppins_bold.ttf b/app/src/main/res/font/poppins_bold.ttf new file mode 100644 index 0000000..00559ee Binary files /dev/null and b/app/src/main/res/font/poppins_bold.ttf differ diff --git a/app/src/main/res/font/poppins_medium.ttf b/app/src/main/res/font/poppins_medium.ttf new file mode 100644 index 0000000..6bcdcc2 Binary files /dev/null and b/app/src/main/res/font/poppins_medium.ttf differ diff --git a/app/src/main/res/font/poppins_semibold.ttf b/app/src/main/res/font/poppins_semibold.ttf new file mode 100644 index 0000000..74c726e Binary files /dev/null and b/app/src/main/res/font/poppins_semibold.ttf differ diff --git a/app/src/main/res/font/timeline_bold.otf b/app/src/main/res/font/timeline_bold.otf new file mode 100644 index 0000000..1df493e Binary files /dev/null and b/app/src/main/res/font/timeline_bold.otf differ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 74409e5..67ea30d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ junitVersion = "1.2.1" espressoCore = "3.6.1" lifecycleRuntimeKtx = "2.8.7" activityCompose = "1.10.0" -composeBom = "2025.01.01" +composeBom = "2025.03.00" media3Exoplayer = "1.5.1" navigationCompose = "2.8.6" kotlinxSerializationJson = "1.8.0"