diff --git a/android-component/src/main/java/io/lemon/core/component/intent/Intent.kt b/android-component/src/main/java/io/lemon/core/android/component/intent/Intent.kt similarity index 100% rename from android-component/src/main/java/io/lemon/core/component/intent/Intent.kt rename to android-component/src/main/java/io/lemon/core/android/component/intent/Intent.kt diff --git a/android-component/src/main/java/io/lemon/core/component/intent/IntentBuilder.kt b/android-component/src/main/java/io/lemon/core/android/component/intent/IntentBuilder.kt similarity index 100% rename from android-component/src/main/java/io/lemon/core/component/intent/IntentBuilder.kt rename to android-component/src/main/java/io/lemon/core/android/component/intent/IntentBuilder.kt diff --git a/android-component/src/main/java/io/lemon/core/component/intent/PendingIntent.kt b/android-component/src/main/java/io/lemon/core/android/component/intent/PendingIntent.kt similarity index 100% rename from android-component/src/main/java/io/lemon/core/component/intent/PendingIntent.kt rename to android-component/src/main/java/io/lemon/core/android/component/intent/PendingIntent.kt diff --git a/android-component/src/main/java/io/lemon/core/component/launcher/ActivityLauncher.kt b/android-component/src/main/java/io/lemon/core/android/component/launcher/ActivityLauncher.kt similarity index 100% rename from android-component/src/main/java/io/lemon/core/component/launcher/ActivityLauncher.kt rename to android-component/src/main/java/io/lemon/core/android/component/launcher/ActivityLauncher.kt diff --git a/android-component/src/main/java/io/lemon/core/component/launcher/ReceiverLauncher.kt b/android-component/src/main/java/io/lemon/core/android/component/launcher/ReceiverLauncher.kt similarity index 100% rename from android-component/src/main/java/io/lemon/core/component/launcher/ReceiverLauncher.kt rename to android-component/src/main/java/io/lemon/core/android/component/launcher/ReceiverLauncher.kt diff --git a/android-component/src/main/java/io/lemon/core/component/launcher/ServiceLauncher.kt b/android-component/src/main/java/io/lemon/core/android/component/launcher/ServiceLauncher.kt similarity index 100% rename from android-component/src/main/java/io/lemon/core/component/launcher/ServiceLauncher.kt rename to android-component/src/main/java/io/lemon/core/android/component/launcher/ServiceLauncher.kt diff --git a/compose-util/src/main/java/io/lemon/core/compose/util/Modifier.kt b/compose-util/src/main/java/io/lemon/core/compose/util/modifier/Clickable.kt similarity index 98% rename from compose-util/src/main/java/io/lemon/core/compose/util/Modifier.kt rename to compose-util/src/main/java/io/lemon/core/compose/util/modifier/Clickable.kt index fb82c78..a6b09d0 100644 --- a/compose-util/src/main/java/io/lemon/core/compose/util/Modifier.kt +++ b/compose-util/src/main/java/io/lemon/core/compose/util/modifier/Clickable.kt @@ -1,4 +1,4 @@ -package io.lemon.core.compose.util +package io.lemon.core.compose.util.modifier import androidx.compose.foundation.Indication import androidx.compose.foundation.clickable diff --git a/compose-util/src/main/java/io/lemon/core/compose/util/modifier/Shadow.kt b/compose-util/src/main/java/io/lemon/core/compose/util/modifier/Shadow.kt new file mode 100644 index 0000000..1b8ed42 --- /dev/null +++ b/compose-util/src/main/java/io/lemon/core/compose/util/modifier/Shadow.kt @@ -0,0 +1,104 @@ +package io.lemon.core.compose.util.modifier + +import android.graphics.BlurMaskFilter +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Paint +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.drawOutline +import androidx.compose.ui.graphics.drawscope.drawIntoCanvas +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.unit.dp + +/** + * @param shape 그림자에 적용할 모양 + * @param color 그림자 색상 + * @param offset 그림자의 위치 + * @param blur 그림자 흐림 효과 (번짐의 정도) + * @param spread 그림자의 확산효과이며, 그림자의 크기; + * 음수로 할 경우 그림자의 크기가 기존 도형보다 줄어듭니다. + */ +fun Modifier.dropShadow( + shape: Shape, + color: Color, + offset: DpOffset, + blur: Dp = 1.dp, + spread: Dp = 0.dp +) = drawBehind { + + drawIntoCanvas { canvas -> + canvas.save() + canvas.translate(dx = offset.x.toPx(), dy = offset.y.toPx()) // 캔버스 위치 이동 + canvas.drawOutline( + outline = shape.createOutline( + size = Size( + width = size.width + spread.toPx(), + height = size.height + spread.toPx() + ), + layoutDirection = layoutDirection, + density = this + ), // 그림자 사이즈 구성 + paint = Paint().apply { + this.color = color + blur.toPx().takeIf { it > 0f }?.let { radius -> + asFrameworkPaint().apply { + maskFilter = BlurMaskFilter( + /* radius = */ radius, + /* style = */ BlurMaskFilter.Blur.NORMAL + ) + } + } + } // 그림자 스타일 구성 + ) // 그림자 생성 + canvas.restore() + } +} + +/** + * @param shape 그림자에 적용할 모양 + * @param color 그림자 색상 + * @param yOffset 그림자의 y 오프셋 + * @param xOffset 그림자의 x 오프셋 + * @param blur 그림자 흐림 효과 (번짐의 정도) + * @param spread 그림자의 확산효과이며, 그림자의 크기; + * 음수로 할 경우 그림자의 크기가 기존 도형보다 줄어듭니다. + */ +fun Modifier.dropShadow( + shape: Shape, + color: Color, + yOffset: Dp = 0.dp, + xOffset: Dp = 0.dp, + blur: Dp = 1.dp, + spread: Dp = 0.dp +) = drawBehind { + + drawIntoCanvas { canvas -> + canvas.save() + canvas.translate(dx = xOffset.toPx(), dy = yOffset.toPx()) // 캔버스 위치 이동 + canvas.drawOutline( + outline = shape.createOutline( + size = Size( + width = size.width + spread.toPx(), + height = size.height + spread.toPx() + ), + layoutDirection = layoutDirection, + density = this + ), // 그림자 사이즈 구성 + paint = Paint().apply { + this.color = color + blur.toPx().takeIf { it > 0f }?.let { radius -> + asFrameworkPaint().apply { + maskFilter = BlurMaskFilter( + /* radius = */ radius, + /* style = */ BlurMaskFilter.Blur.NORMAL + ) + } + } + } // 그림자 스타일 구성 + ) // 그림자 생성 + canvas.restore() + } +} diff --git a/compose-util/src/main/java/io/lemon/core/compose/util/modifier/Toggleable.kt b/compose-util/src/main/java/io/lemon/core/compose/util/modifier/Toggleable.kt new file mode 100644 index 0000000..5e70c73 --- /dev/null +++ b/compose-util/src/main/java/io/lemon/core/compose/util/modifier/Toggleable.kt @@ -0,0 +1,103 @@ +package io.lemon.core.compose.util.modifier + +import androidx.compose.foundation.Indication +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.selection.toggleable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableLongStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.semantics.Role +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +/** + * Toggle interaction 이 연속적으로 발생하는 것을 방지하는 확장자 + */ +fun Modifier.throttleToggleable( + value: Boolean, + onValueChange: (Boolean) -> Unit, + enabled: Boolean = true, + role: Role? = null, + coroutineDispatcher: CoroutineDispatcher = Dispatchers.Main, + throttleTime: Long = 250L, +) = composed { + val coroutineScope = rememberCoroutineScope { coroutineDispatcher } + var lastEmissionTime: Long by remember { mutableLongStateOf(0L) } + + toggleable( + value = value, + onValueChange = { + val currentTime = System.currentTimeMillis() + if (currentTime - lastEmissionTime >= throttleTime) { + coroutineScope.launch { + lastEmissionTime = currentTime + onValueChange(it) + } + } + lastEmissionTime = currentTime + }, + enabled = enabled, + role = role, + ) +} + +/** + * Toggle interaction 이 연속적으로 발생하는 것을 방지하는 확장자 + */ +fun Modifier.throttleToggleable( + value: Boolean, + onValueChange: (Boolean) -> Unit, + interactionSource: MutableInteractionSource = MutableInteractionSource(), + indication: Indication? = null, + enabled: Boolean = true, + role: Role? = null, + coroutineDispatcher: CoroutineDispatcher = Dispatchers.Main, + throttleTime: Long = 250L, +) = composed { + val coroutineScope = rememberCoroutineScope { coroutineDispatcher } + var lastEmissionTime: Long by remember { mutableLongStateOf(0L) } + + noRippleToggleable( + value = value, + onValueChange = { + val currentTime = System.currentTimeMillis() + if (currentTime - lastEmissionTime >= throttleTime) { + coroutineScope.launch { + lastEmissionTime = currentTime + onValueChange(it) + } + } + lastEmissionTime = currentTime + }, + interactionSource = remember { interactionSource }, + indication = indication, + enabled = enabled, + role = role + ) +} + +/** + * Toggle interaction 중 발생하는 시각 이펙트를 제거하는 확장자 + */ +fun Modifier.noRippleToggleable( + value: Boolean, + onValueChange: (Boolean) -> Unit, + interactionSource: MutableInteractionSource = MutableInteractionSource(), + indication: Indication? = null, + enabled: Boolean = true, + role: Role? = null, +) = composed { + toggleable( + value = value, + interactionSource = remember { interactionSource }, + indication = indication, + enabled = enabled, + onValueChange = onValueChange, + role = role + ) +}