Skip to content

Commit 1f70ce3

Browse files
authored
Merge pull request #84 from YAPP-Github/fix/#78-archive-upload-qa
[fix] #78 아카이브 업로드 QA 수정
2 parents 03dee6f + f9f49af commit 1f70ce3

32 files changed

Lines changed: 933 additions & 355 deletions

File tree

core/common/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ dependencies {
1313
api(libs.kakao.user)
1414
implementation(libs.androidx.security.crypto)
1515
implementation(libs.androidx.core.ktx)
16+
17+
implementation(libs.androidx.exifinterface)
1618
}

core/common/src/main/java/com/neki/android/core/common/util/ByteArray.kt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package com.neki.android.core.common.util
33
import android.content.Context
44
import android.graphics.Bitmap
55
import android.graphics.BitmapFactory
6+
import android.graphics.Matrix
67
import android.net.Uri
8+
import androidx.exifinterface.media.ExifInterface
79
import kotlinx.coroutines.Dispatchers
810
import kotlinx.coroutines.withContext
911
import java.io.ByteArrayOutputStream
@@ -16,8 +18,22 @@ fun Uri.toByteArray(
1618
quality: Int = DEFAULT_QUALITY,
1719
format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,
1820
): ByteArray? {
21+
val orientation = context.contentResolver.openInputStream(this)?.use { input ->
22+
ExifInterface(input).getAttributeInt(
23+
ExifInterface.TAG_ORIENTATION,
24+
ExifInterface.ORIENTATION_UNDEFINED,
25+
)
26+
} ?: ExifInterface.ORIENTATION_UNDEFINED
1927
val bytes = context.contentResolver.openInputStream(this)?.use { it.readBytes() } ?: return null
20-
return bytes.compress(quality, format)
28+
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) ?: return null
29+
val rotatedBitmap = bitmap.applyOrientation(orientation)
30+
31+
return ByteArrayOutputStream().use { outputStream ->
32+
rotatedBitmap.compress(format, quality, outputStream)
33+
if (rotatedBitmap !== bitmap) bitmap.recycle()
34+
rotatedBitmap.recycle()
35+
outputStream.toByteArray()
36+
}
2137
}
2238

2339
suspend fun String.urlToByteArray(
@@ -28,6 +44,29 @@ suspend fun String.urlToByteArray(
2844
bytes.compress(quality, format)
2945
}
3046

47+
private fun Bitmap.applyOrientation(orientation: Int): Bitmap {
48+
val matrix = Matrix()
49+
when (orientation) {
50+
ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
51+
ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
52+
ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
53+
ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.postScale(-1f, 1f)
54+
ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.postScale(1f, -1f)
55+
ExifInterface.ORIENTATION_TRANSPOSE -> {
56+
matrix.postRotate(90f)
57+
matrix.postScale(-1f, 1f)
58+
}
59+
60+
ExifInterface.ORIENTATION_TRANSVERSE -> {
61+
matrix.postRotate(270f)
62+
matrix.postScale(-1f, 1f)
63+
}
64+
65+
else -> return this
66+
}
67+
return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)
68+
}
69+
3170
private fun ByteArray.compress(
3271
quality: Int,
3372
format: Bitmap.CompressFormat,

core/data/src/main/java/com/neki/android/core/data/remote/api/PhotoService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class PhotoService @Inject constructor(
5454
}.body()
5555
}
5656

57-
// 즐겨찾기 사진 조회
57+
// 즐겨찾는 앨범 조회
5858
suspend fun getFavoritePhotos(
5959
page: Int = 0,
6060
size: Int = 20,
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.neki.android.core.designsystem.logo
2+
3+
import androidx.compose.material3.Icon
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.ui.Modifier
6+
import androidx.compose.ui.graphics.Color
7+
import androidx.compose.ui.graphics.vector.ImageVector
8+
import androidx.compose.ui.res.vectorResource
9+
import androidx.compose.ui.tooling.preview.Preview
10+
import com.neki.android.core.designsystem.ComponentPreview
11+
import com.neki.android.core.designsystem.R
12+
import com.neki.android.core.designsystem.ui.theme.NekiTheme
13+
14+
@Composable
15+
private fun NekiTypoLogo(
16+
color: Color,
17+
modifier: Modifier = Modifier,
18+
) {
19+
Icon(
20+
imageVector = ImageVector.vectorResource(R.drawable.icon_neki_logo_typo),
21+
contentDescription = null,
22+
tint = color,
23+
modifier = modifier,
24+
)
25+
}
26+
27+
@Composable
28+
fun PrimaryNekiTypoLogo(
29+
modifier: Modifier = Modifier,
30+
) {
31+
NekiTypoLogo(
32+
color = NekiTheme.colorScheme.primary400,
33+
modifier = modifier,
34+
)
35+
}
36+
37+
@Composable
38+
fun GrayNekiTypoLogo(
39+
modifier: Modifier = Modifier,
40+
) {
41+
NekiTypoLogo(
42+
color = NekiTheme.colorScheme.gray900,
43+
modifier = modifier,
44+
)
45+
}
46+
47+
@Composable
48+
fun WhiteNekiTypoLogo(
49+
modifier: Modifier = Modifier,
50+
) {
51+
NekiTypoLogo(
52+
color = NekiTheme.colorScheme.white,
53+
modifier = modifier,
54+
)
55+
}
56+
57+
@ComponentPreview
58+
@Composable
59+
private fun PrimaryNekiTypoLogoPreview() {
60+
NekiTheme {
61+
PrimaryNekiTypoLogo()
62+
}
63+
}
64+
65+
@ComponentPreview
66+
@Composable
67+
private fun GrayNekiTypoLogoPreview() {
68+
NekiTheme {
69+
GrayNekiTypoLogo()
70+
}
71+
}
72+
73+
@Preview
74+
@Composable
75+
private fun WhiteNekiTypoLogoPreview() {
76+
NekiTheme {
77+
WhiteNekiTypoLogo()
78+
}
79+
}

core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@ package com.neki.android.core.designsystem.modifier
22

33
import androidx.compose.foundation.background
44
import androidx.compose.foundation.shape.RoundedCornerShape
5-
import androidx.compose.runtime.Composable
65
import androidx.compose.ui.Modifier
7-
import androidx.compose.ui.draw.alpha
86
import androidx.compose.ui.geometry.Offset
97
import androidx.compose.ui.graphics.Brush
108
import androidx.compose.ui.graphics.Color
119
import androidx.compose.ui.graphics.Shape
12-
import androidx.compose.ui.graphics.luminance
1310
import androidx.compose.ui.unit.Dp
1411
import androidx.compose.ui.unit.dp
1512
import dev.chrisbanes.haze.HazeState
@@ -56,28 +53,29 @@ fun Modifier.poseBackground(
5653
* 블러 효과가 적용된 배경을 설정하는 Modifier 확장 함수
5754
*
5855
* @param hazeState Haze 블러 효과를 관리하는 상태 객체
59-
* @param enabled 블러 효과 활성화 여부 (false일 경우 단색 배경 적용)
56+
* @param alpha 블러 틴트에 적용될 색상의 알파 값
6057
* @param color 블러 효과에 적용될 배경 색상
61-
* @param defaultBackgroundColor 블러 비활성화 시 적용될 기본 배경 색상
6258
* @param blurRadius 블러 효과의 반경
59+
* @param enabled 블러 효과 활성화 여부 (false일 경우 단색 배경 적용)
60+
* @param defaultBackgroundColor 블러 비활성화 시 적용될 기본 배경 색상
6361
*/
64-
@Composable
6562
fun Modifier.backgroundHazeBlur(
6663
hazeState: HazeState,
64+
alpha: Float,
65+
color: Color,
66+
blurRadius: Dp,
6767
enabled: Boolean = true,
68-
color: Color = Color(0xFF202227).copy(alpha = 0.9f),
6968
defaultBackgroundColor: Color = color,
70-
blurRadius: Dp = 12.dp,
7169
): Modifier =
7270
if (enabled) {
7371
this.hazeEffect(
7472
state = hazeState,
7573
style = HazeStyle(
7674
backgroundColor = color,
77-
tint = HazeTint(
78-
color.copy(alpha = if (color.luminance() >= 0.5) 0.6f else 0.65f),
79-
),
75+
tint = HazeTint(color.copy(alpha = alpha)),
8076
blurRadius = blurRadius,
8177
),
8278
)
83-
} else this.background(color = defaultBackgroundColor)
79+
} else this.background(
80+
color = defaultBackgroundColor,
81+
)
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package com.neki.android.core.designsystem.popup
2+
3+
import androidx.compose.foundation.Canvas
4+
import androidx.compose.foundation.background
5+
import androidx.compose.foundation.layout.Box
6+
import androidx.compose.foundation.layout.Column
7+
import androidx.compose.foundation.layout.IntrinsicSize
8+
import androidx.compose.foundation.layout.fillMaxWidth
9+
import androidx.compose.foundation.layout.padding
10+
import androidx.compose.foundation.layout.size
11+
import androidx.compose.foundation.layout.width
12+
import androidx.compose.foundation.shape.RoundedCornerShape
13+
import androidx.compose.material3.Text
14+
import androidx.compose.runtime.Composable
15+
import androidx.compose.ui.Alignment
16+
import androidx.compose.ui.Modifier
17+
import androidx.compose.ui.graphics.Color
18+
import androidx.compose.ui.graphics.Path
19+
import androidx.compose.ui.unit.IntOffset
20+
import androidx.compose.ui.unit.dp
21+
import androidx.compose.ui.window.Popup
22+
import com.neki.android.core.designsystem.ComponentPreview
23+
import com.neki.android.core.designsystem.ui.theme.NekiTheme
24+
25+
@Composable
26+
fun ToolTipPopup(
27+
tooltipText: String,
28+
color: Color,
29+
offset: IntOffset,
30+
alignment: Alignment,
31+
onDismissRequest: () -> Unit,
32+
) {
33+
Popup(
34+
alignment = alignment,
35+
offset = offset,
36+
onDismissRequest = onDismissRequest,
37+
) {
38+
ToolTipContent(
39+
tooltipText = tooltipText,
40+
color = color,
41+
)
42+
}
43+
}
44+
45+
@Composable
46+
private fun ToolTipContent(
47+
tooltipText: String,
48+
color: Color,
49+
modifier: Modifier = Modifier,
50+
) {
51+
Column(
52+
modifier = modifier.width(IntrinsicSize.Max),
53+
) {
54+
// 꼬리 (오른쪽 정렬, 오른쪽에서 16dp)
55+
Box(
56+
modifier = Modifier
57+
.fillMaxWidth()
58+
.padding(end = 16.dp),
59+
contentAlignment = Alignment.CenterEnd,
60+
) {
61+
Canvas(
62+
modifier = Modifier.size(width = 10.dp, height = 8.dp),
63+
) {
64+
val cornerRadius = 1.dp.toPx()
65+
val path = Path().apply {
66+
// 왼쪽 하단에서 시작
67+
moveTo(0f, size.height)
68+
// 왼쪽 하단 -> 꼭대기 (둥근 모서리)
69+
lineTo(
70+
size.width / 2 - cornerRadius,
71+
cornerRadius,
72+
)
73+
quadraticTo(
74+
size.width / 2,
75+
0f,
76+
size.width / 2 + cornerRadius,
77+
cornerRadius,
78+
)
79+
// 꼭대기 -> 오른쪽 하단
80+
lineTo(size.width, size.height)
81+
close()
82+
}
83+
drawPath(path, color)
84+
}
85+
}
86+
87+
// 몸통
88+
Box(
89+
modifier = Modifier
90+
.background(
91+
color = color,
92+
shape = RoundedCornerShape(8.dp),
93+
)
94+
.padding(horizontal = 12.dp, vertical = 8.dp),
95+
) {
96+
Text(
97+
text = tooltipText,
98+
style = NekiTheme.typography.body14Medium,
99+
color = NekiTheme.colorScheme.white,
100+
)
101+
}
102+
}
103+
}
104+
105+
@ComponentPreview
106+
@Composable
107+
private fun ToolTipPopupPreview() {
108+
NekiTheme {
109+
Box(modifier = Modifier.padding(16.dp)) {
110+
ToolTipContent(
111+
tooltipText = "툴팁 메시지입니다",
112+
color = NekiTheme.colorScheme.gray800,
113+
)
114+
}
115+
}
116+
}

0 commit comments

Comments
 (0)