From 97f668cf9bec59ab8a7c480a95c313507e00c216 Mon Sep 17 00:00:00 2001 From: bmsk Date: Tue, 2 Jan 2024 00:34:12 +0900 Subject: [PATCH 1/9] =?UTF-8?q?[Feat]=20=EA=B2=80=EC=83=89=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/designsystem/component/CazaitCard.kt | 12 +- .../core/designsystem/theme/Theme.kt | 2 +- .../cazaitandroid/feature/home/HomeRoute.kt | 3 +- .../cazaitandroid/feature/home/HomeScreen.kt | 108 ++++++++++++++++-- .../src/main/res/drawable/ic_alarm_normal.xml | 13 +++ .../src/main/res/drawable/ic_alarm_noti.xml | 16 +++ feature/home/src/main/res/drawable/ic_it.xml | 23 ++++ .../home/src/main/res/drawable/ic_search.xml | 13 +++ feature/home/src/main/res/values/strings.xml | 4 + 9 files changed, 177 insertions(+), 17 deletions(-) create mode 100644 feature/home/src/main/res/drawable/ic_alarm_normal.xml create mode 100644 feature/home/src/main/res/drawable/ic_alarm_noti.xml create mode 100644 feature/home/src/main/res/drawable/ic_it.xml create mode 100644 feature/home/src/main/res/drawable/ic_search.xml create mode 100644 feature/home/src/main/res/values/strings.xml diff --git a/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/component/CazaitCard.kt b/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/component/CazaitCard.kt index 910f4c6..624a14d 100644 --- a/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/component/CazaitCard.kt +++ b/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/component/CazaitCard.kt @@ -15,14 +15,14 @@ import org.cazait.cazaitandroid.core.designsystem.theme.CazaitTheme @Composable fun CazaitCard( modifier: Modifier = Modifier, - color: Color = MaterialTheme.colorScheme.surface, + color: Color = MaterialTheme.colorScheme.primaryContainer, content: @Composable () -> Unit, ) { Surface( modifier = modifier, color = color, - shape = RoundedCornerShape(32.dp), - shadowElevation = 11.dp, + shape = RoundedCornerShape(12.dp), + shadowElevation = 16.dp, content = content, ) } @@ -32,7 +32,7 @@ fun CazaitCard( modifier: Modifier = Modifier, enabled: Boolean = false, onClick: () -> Unit = {}, - color: Color = MaterialTheme.colorScheme.surface, + color: Color = MaterialTheme.colorScheme.primaryContainer, content: @Composable () -> Unit, ) { Surface( @@ -40,8 +40,8 @@ fun CazaitCard( enabled = enabled, modifier = modifier, color = color, - shape = RoundedCornerShape(32.dp), - shadowElevation = 11.dp, + shape = RoundedCornerShape(12.dp), + shadowElevation = 16.dp, content = content, ) } diff --git a/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/theme/Theme.kt b/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/theme/Theme.kt index fc93876..a64058c 100644 --- a/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/theme/Theme.kt +++ b/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/theme/Theme.kt @@ -61,7 +61,7 @@ private val LightColorScheme = lightColorScheme( errorContainer = Red01, onErrorContainer = Red06, surface = Black, - onSurface = DuskGray, + onSurface = White, inverseSurface = Yellow05, inverseOnSurface = White, outline = LightGray, diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt index 74f8211..13cffdd 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt @@ -16,6 +16,7 @@ internal fun HomeRoute( homeViewModel.errorFlow.collectLatest { throwable -> onShowErrorSnackbar(throwable) } } HomeScreen( - padding = padding + padding = padding, + onClickCafe = {}, ) } diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt index e41675e..d5cfef2 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt @@ -1,30 +1,120 @@ package org.cazait.cazaitandroid.feature.home -import androidx.compose.foundation.layout.Box +import android.content.res.Configuration +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import org.cazait.cazaitandroid.core.designsystem.theme.Black +import org.cazait.cazaitandroid.core.designsystem.theme.CazaitTheme +import org.cazait.cazaitandroid.core.designsystem.theme.White @Composable -fun HomeScreen( +internal fun HomeScreen( padding: PaddingValues, + onClickCafe: () -> Unit, modifier: Modifier = Modifier, ) { - Box( + Column( modifier = modifier .padding(padding) - .fillMaxSize() - ) + .fillMaxSize(), + ) { + Surface( + modifier = Modifier.fillMaxWidth(), + color = MaterialTheme.colorScheme.background, + shape = RoundedCornerShape(bottomStart = 40.dp, bottomEnd = 40.dp), + ) { + Row( + modifier = Modifier.padding(28.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_it), + contentDescription = "Cazait Icon", + modifier = Modifier + .size(40.dp) + .align(Alignment.CenterVertically), + ) + SearchingTextField(modifier = Modifier.weight(1f)) + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_alarm_normal), + contentDescription = "Notification", + modifier = Modifier + .size(40.dp) + .align(Alignment.CenterVertically), + ) + } + } + } } -@Preview @Composable +private fun SearchingTextField( + modifier: Modifier = Modifier, +) { + Surface( + modifier = modifier, + color = Black, + shadowElevation = 10.dp, + shape = RoundedCornerShape(48.dp), + ) { + TextField( + shape = RoundedCornerShape(48.dp), + value = "", + onValueChange = {}, + placeholder = { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_search), + contentDescription = "Search Icon" + ) + Text( + text = stringResource(id = R.string.search), + color = MaterialTheme.colorScheme.onSurface + ) + } + }, + colors = TextFieldDefaults.colors( + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent, + focusedContainerColor = Color.Transparent, + ), + ) + } +} + +@Composable +internal fun HomeLoading() { +} + +@Composable +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) private fun HomeScreenPreview() { - HomeScreen( - padding = PaddingValues(0.dp) - ) + CazaitTheme { + HomeScreen(padding = PaddingValues(0.dp), onClickCafe = {}) + } } diff --git a/feature/home/src/main/res/drawable/ic_alarm_normal.xml b/feature/home/src/main/res/drawable/ic_alarm_normal.xml new file mode 100644 index 0000000..ef0d65b --- /dev/null +++ b/feature/home/src/main/res/drawable/ic_alarm_normal.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/feature/home/src/main/res/drawable/ic_alarm_noti.xml b/feature/home/src/main/res/drawable/ic_alarm_noti.xml new file mode 100644 index 0000000..2a7e209 --- /dev/null +++ b/feature/home/src/main/res/drawable/ic_alarm_noti.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/feature/home/src/main/res/drawable/ic_it.xml b/feature/home/src/main/res/drawable/ic_it.xml new file mode 100644 index 0000000..e055835 --- /dev/null +++ b/feature/home/src/main/res/drawable/ic_it.xml @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/feature/home/src/main/res/drawable/ic_search.xml b/feature/home/src/main/res/drawable/ic_search.xml new file mode 100644 index 0000000..ddae89c --- /dev/null +++ b/feature/home/src/main/res/drawable/ic_search.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/feature/home/src/main/res/values/strings.xml b/feature/home/src/main/res/values/strings.xml new file mode 100644 index 0000000..1805b22 --- /dev/null +++ b/feature/home/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + search + \ No newline at end of file From ec19e377b6ace03b5e56677304ae70efddfead62 Mon Sep 17 00:00:00 2001 From: bmsk Date: Fri, 5 Jan 2024 16:45:24 +0900 Subject: [PATCH 2/9] =?UTF-8?q?[Feat]=20=EC=B9=B4=ED=8E=98=20=ED=99=88=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A6=B0=20ui=20(=EC=B9=B4=ED=8E=98=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=ED=83=80=EC=9D=B4=ED=8B=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cazaitandroid/feature/home/HomeScreen.kt | 94 ++++++++++++++----- .../home/src/main/res/drawable/ic_filter.xml | 16 ++++ feature/home/src/main/res/values/strings.xml | 2 + 3 files changed, 88 insertions(+), 24 deletions(-) create mode 100644 feature/home/src/main/res/drawable/ic_filter.xml diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt index d5cfef2..b067ef0 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt @@ -1,14 +1,19 @@ package org.cazait.cazaitandroid.feature.home import android.content.res.Configuration +import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -25,6 +30,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import org.cazait.cazaitandroid.core.designsystem.component.CazaitCard import org.cazait.cazaitandroid.core.designsystem.theme.Black import org.cazait.cazaitandroid.core.designsystem.theme.CazaitTheme import org.cazait.cazaitandroid.core.designsystem.theme.White @@ -38,37 +44,76 @@ internal fun HomeScreen( Column( modifier = modifier .padding(padding) + .background(color = MaterialTheme.colorScheme.primaryContainer) .fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(20.dp), ) { - Surface( - modifier = Modifier.fillMaxWidth(), - color = MaterialTheme.colorScheme.background, - shape = RoundedCornerShape(bottomStart = 40.dp, bottomEnd = 40.dp), + HomeTopBar() + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp) ) { - Row( - modifier = Modifier.padding(28.dp), - horizontalArrangement = Arrangement.spacedBy(12.dp), - ) { - Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_it), - contentDescription = "Cazait Icon", - modifier = Modifier - .size(40.dp) - .align(Alignment.CenterVertically), - ) - SearchingTextField(modifier = Modifier.weight(1f)) - Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_alarm_normal), - contentDescription = "Notification", - modifier = Modifier - .size(40.dp) - .align(Alignment.CenterVertically), - ) + item { HomeColumnTitle() } + item { + CazaitCard { + + } } } } } +@Composable +private fun HomeColumnTitle() { + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + text = stringResource(id = R.string.home_cafes_column_title), + style = CazaitTheme.typography.titleLargeBL + ) + Spacer(modifier = Modifier.weight(1f)) + Image( + imageVector = ImageVector.vectorResource(R.drawable.ic_filter), + contentDescription = "filter", + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = stringResource(id = R.string.distance), + style = CazaitTheme.typography.titleMediumB + ) + } +} + +@Composable +private fun HomeTopBar() { + Surface( + modifier = Modifier.fillMaxWidth(), + color = MaterialTheme.colorScheme.background, + shape = RoundedCornerShape(bottomStart = 40.dp, bottomEnd = 40.dp), + ) { + Row( + modifier = Modifier.padding(28.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_it), + contentDescription = "Cazait Icon", + modifier = Modifier + .size(40.dp) + .align(Alignment.CenterVertically), + ) + SearchingTextField(modifier = Modifier.weight(1f)) + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_alarm_normal), + contentDescription = "Notification", + modifier = Modifier + .size(40.dp) + .align(Alignment.CenterVertically), + ) + } + } +} + @Composable private fun SearchingTextField( modifier: Modifier = Modifier, @@ -87,7 +132,8 @@ private fun SearchingTextField( Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { Icon( imageVector = ImageVector.vectorResource(R.drawable.ic_search), - contentDescription = "Search Icon" + contentDescription = "Search Icon", + tint = White, ) Text( text = stringResource(id = R.string.search), diff --git a/feature/home/src/main/res/drawable/ic_filter.xml b/feature/home/src/main/res/drawable/ic_filter.xml new file mode 100644 index 0000000..8023476 --- /dev/null +++ b/feature/home/src/main/res/drawable/ic_filter.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/feature/home/src/main/res/values/strings.xml b/feature/home/src/main/res/values/strings.xml index 1805b22..9669e8d 100644 --- a/feature/home/src/main/res/values/strings.xml +++ b/feature/home/src/main/res/values/strings.xml @@ -1,4 +1,6 @@ search + 카페 확인하기 + 거리순 \ No newline at end of file From dd97a32af224aca0c281c10aba3da05c3881b05f Mon Sep 17 00:00:00 2001 From: bmsk Date: Fri, 5 Jan 2024 17:24:33 +0900 Subject: [PATCH 3/9] [Feat] Home Repository Api --- core/repo/home/api/build.gradle.kts | 9 +++++++ .../core/repo/home/api/HomeRepository.kt | 7 ++++++ .../core/repo/home/api/model/Cafe.kt | 16 +++++++++++++ .../core/repo/home/api/model/CafeAddress.kt | 6 +++++ .../core/repo/home/api/model/CafeId.kt | 8 +++++++ .../core/repo/home/api/model/CafeImage.kt | 6 +++++ .../core/repo/home/api/model/CafeImages.kt | 6 +++++ .../core/repo/home/api/model/CafeName.kt | 6 +++++ .../repo/home/api/model/CongestionCafe.kt | 13 ++++++++++ .../core/repo/home/api/model/Latitude.kt | 6 +++++ .../core/repo/home/api/model/Longitude.kt | 6 +++++ core/repo/home/impl/build.gradle.kts | 24 +++++++++++++++++++ .../core/repo/home/DefaultHomeRepository.kt | 13 ++++++++++ settings.gradle.kts | 2 ++ 14 files changed, 128 insertions(+) create mode 100644 core/repo/home/api/build.gradle.kts create mode 100644 core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/HomeRepository.kt create mode 100644 core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Cafe.kt create mode 100644 core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeAddress.kt create mode 100644 core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeId.kt create mode 100644 core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeImage.kt create mode 100644 core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeImages.kt create mode 100644 core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeName.kt create mode 100644 core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CongestionCafe.kt create mode 100644 core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Latitude.kt create mode 100644 core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Longitude.kt create mode 100644 core/repo/home/impl/build.gradle.kts create mode 100644 core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/DefaultHomeRepository.kt diff --git a/core/repo/home/api/build.gradle.kts b/core/repo/home/api/build.gradle.kts new file mode 100644 index 0000000..d231d3d --- /dev/null +++ b/core/repo/home/api/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("cazait.coroutine.library") + id("kotlinx-serialization") +} + +dependencies { + api(libs.kotlinx.datetime) + implementation(libs.kotlinx.serialization.json) +} diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/HomeRepository.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/HomeRepository.kt new file mode 100644 index 0000000..09f05f3 --- /dev/null +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/HomeRepository.kt @@ -0,0 +1,7 @@ +package org.cazait.cazaitandroid.core.repo.home.api + +import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafes + +interface HomeRepository { + suspend fun getAllCongestionCafes(): CongestionCafes +} \ No newline at end of file diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Cafe.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Cafe.kt new file mode 100644 index 0000000..cb757c7 --- /dev/null +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Cafe.kt @@ -0,0 +1,16 @@ +package org.cazait.cazaitandroid.core.repo.home.api.model + +data class Cafe( + val id: CafeId, + val name: CafeName, + val address: CafeAddress, + val cafeImages: CafeImages, + val latitude: Latitude, + val longitude: Longitude, +) + +@JvmInline +value class Cafes(private val values: List) { + fun asList(): List = values +} + diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeAddress.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeAddress.kt new file mode 100644 index 0000000..4e561e0 --- /dev/null +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeAddress.kt @@ -0,0 +1,6 @@ +package org.cazait.cazaitandroid.core.repo.home.api.model + +@JvmInline +value class CafeAddress(private val address: String) { + fun asString(): String = address +} \ No newline at end of file diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeId.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeId.kt new file mode 100644 index 0000000..d5b60b5 --- /dev/null +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeId.kt @@ -0,0 +1,8 @@ +package org.cazait.cazaitandroid.core.repo.home.api.model + +import java.util.UUID + +@JvmInline +value class CafeId(private val id: UUID) { + fun asUUID(): UUID = id +} diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeImage.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeImage.kt new file mode 100644 index 0000000..e43bcae --- /dev/null +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeImage.kt @@ -0,0 +1,6 @@ +package org.cazait.cazaitandroid.core.repo.home.api.model + +@JvmInline +value class CafeImage(private val url: String) { + fun asString(): String = url +} \ No newline at end of file diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeImages.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeImages.kt new file mode 100644 index 0000000..f72b0ee --- /dev/null +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeImages.kt @@ -0,0 +1,6 @@ +package org.cazait.cazaitandroid.core.repo.home.api.model + +@JvmInline +value class CafeImages(private val images: List) { + fun asList(): List = images +} \ No newline at end of file diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeName.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeName.kt new file mode 100644 index 0000000..844abfd --- /dev/null +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CafeName.kt @@ -0,0 +1,6 @@ +package org.cazait.cazaitandroid.core.repo.home.api.model + +@JvmInline +value class CafeName(private val name: String) { + fun asString(): String = name +} \ No newline at end of file diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CongestionCafe.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CongestionCafe.kt new file mode 100644 index 0000000..2bfa3f5 --- /dev/null +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/CongestionCafe.kt @@ -0,0 +1,13 @@ +package org.cazait.cazaitandroid.core.repo.home.api.model + +data class CongestionCafe( + val cafe: Cafe, + val congestion: Congestion, +) + +@JvmInline +value class CongestionCafes(private val values: List) { + fun asList(): List = values +} + +enum class Congestion { FREE, NORMAL, CLOSE, CROWDED, VERY_CROWDED } diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Latitude.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Latitude.kt new file mode 100644 index 0000000..5d605d6 --- /dev/null +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Latitude.kt @@ -0,0 +1,6 @@ +package org.cazait.cazaitandroid.core.repo.home.api.model + +@JvmInline +value class Latitude(private val value: Double) { + fun asDouble(): Double = value +} \ No newline at end of file diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Longitude.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Longitude.kt new file mode 100644 index 0000000..6445dc0 --- /dev/null +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Longitude.kt @@ -0,0 +1,6 @@ +package org.cazait.cazaitandroid.core.repo.home.api.model + +@JvmInline +value class Longitude(private val value: Double) { + fun asDouble(): Double = value +} \ No newline at end of file diff --git a/core/repo/home/impl/build.gradle.kts b/core/repo/home/impl/build.gradle.kts new file mode 100644 index 0000000..0e7f230 --- /dev/null +++ b/core/repo/home/impl/build.gradle.kts @@ -0,0 +1,24 @@ +plugins { + id("cazait.android.library") + id("cazait.android.hilt") + id("kotlinx-serialization") +} + +android { + namespace = "org.cazait.cazaitandroid.core.repo.home" + defaultConfig { + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } +} + +dependencies { + implementation(projects.core.repo.home.api) + + implementation(libs.retrofit.core) + implementation(libs.retrofit.kotlin.serialization) + implementation(libs.kotlinx.datetime) + + androidTestImplementation(libs.androidx.test.ext) + androidTestImplementation(libs.androidx.test.espresso.core) + androidTestImplementation(libs.coroutines.test) +} diff --git a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/DefaultHomeRepository.kt b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/DefaultHomeRepository.kt new file mode 100644 index 0000000..78cc379 --- /dev/null +++ b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/DefaultHomeRepository.kt @@ -0,0 +1,13 @@ +package org.cazait.cazaitandroid.core.repo.home + +import org.cazait.cazaitandroid.core.repo.home.api.HomeRepository +import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafes +import javax.inject.Inject + +internal class DefaultHomeRepository @Inject constructor( + +) : HomeRepository { + override suspend fun getAllCongestionCafes(): CongestionCafes { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 3e599bb..7b8c272 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,6 +20,8 @@ include( ":core:repo:signin:api", ":core:repo:signin:impl", + ":core:repo:home:api", + ":core:repo:home:impl", ":core:domain", ":core:data", From c9a4ed9e99d990c113b05e9fa1bfdf3d488e37fe Mon Sep 17 00:00:00 2001 From: bmsk Date: Fri, 5 Jan 2024 18:56:29 +0900 Subject: [PATCH 4/9] [Feat] Home Api, Repository --- .../core/repo/home/api/HomeRepository.kt | 11 ++++++- .../core/repo/home/api/model/DistanceLimit.kt | 7 +++++ .../core/repo/home/api/model/Latitude.kt | 1 + .../core/repo/home/api/model/Longitude.kt | 1 + .../core/repo/home/api/model/SortBy.kt | 3 ++ .../core/repo/home/DefaultHomeRepository.kt | 22 +++++++++++-- .../core/repo/home/di/HomeRepoModule.kt | 28 +++++++++++++++++ .../repo/home/mapper/CongestionCafesMapper.kt | 31 +++++++++++++++++++ .../core/repo/home/network/HomeApi.kt | 19 ++++++++++++ .../network/model/CongestionCafeResponse.kt | 26 ++++++++++++++++ .../model/GetAllCongestionCafesResponse.kt | 16 ++++++++++ .../signin/network/model/SignInResponse.kt | 5 +++ gradle/libs.versions.toml | 4 +-- 13 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/DistanceLimit.kt create mode 100644 core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/SortBy.kt create mode 100644 core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/di/HomeRepoModule.kt create mode 100644 core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/mapper/CongestionCafesMapper.kt create mode 100644 core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/HomeApi.kt create mode 100644 core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/model/CongestionCafeResponse.kt create mode 100644 core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/model/GetAllCongestionCafesResponse.kt diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/HomeRepository.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/HomeRepository.kt index 09f05f3..cc5ab04 100644 --- a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/HomeRepository.kt +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/HomeRepository.kt @@ -1,7 +1,16 @@ package org.cazait.cazaitandroid.core.repo.home.api import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafes +import org.cazait.cazaitandroid.core.repo.home.api.model.DistanceLimit +import org.cazait.cazaitandroid.core.repo.home.api.model.Latitude +import org.cazait.cazaitandroid.core.repo.home.api.model.Longitude +import org.cazait.cazaitandroid.core.repo.home.api.model.SortBy interface HomeRepository { - suspend fun getAllCongestionCafes(): CongestionCafes + suspend fun getAllCongestionCafes( + latitude: Latitude, + longitude: Longitude, + sort: SortBy, + limit: DistanceLimit, + ): CongestionCafes } \ No newline at end of file diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/DistanceLimit.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/DistanceLimit.kt new file mode 100644 index 0000000..9eebe82 --- /dev/null +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/DistanceLimit.kt @@ -0,0 +1,7 @@ +package org.cazait.cazaitandroid.core.repo.home.api.model + +@JvmInline +value class DistanceLimit(private val distance: Int) { + fun asInt(): Int = distance + fun asString(): String = distance.toString() +} \ No newline at end of file diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Latitude.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Latitude.kt index 5d605d6..2fbf763 100644 --- a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Latitude.kt +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Latitude.kt @@ -3,4 +3,5 @@ package org.cazait.cazaitandroid.core.repo.home.api.model @JvmInline value class Latitude(private val value: Double) { fun asDouble(): Double = value + fun asString(): String = value.toString() } \ No newline at end of file diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Longitude.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Longitude.kt index 6445dc0..3044573 100644 --- a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Longitude.kt +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Longitude.kt @@ -3,4 +3,5 @@ package org.cazait.cazaitandroid.core.repo.home.api.model @JvmInline value class Longitude(private val value: Double) { fun asDouble(): Double = value + fun asString(): String = value.toString() } \ No newline at end of file diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/SortBy.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/SortBy.kt new file mode 100644 index 0000000..04107c7 --- /dev/null +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/SortBy.kt @@ -0,0 +1,3 @@ +package org.cazait.cazaitandroid.core.repo.home.api.model + +enum class SortBy { CONGESTION, DISTANCE } \ No newline at end of file diff --git a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/DefaultHomeRepository.kt b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/DefaultHomeRepository.kt index 78cc379..c0bd0c6 100644 --- a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/DefaultHomeRepository.kt +++ b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/DefaultHomeRepository.kt @@ -2,12 +2,28 @@ package org.cazait.cazaitandroid.core.repo.home import org.cazait.cazaitandroid.core.repo.home.api.HomeRepository import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafes +import org.cazait.cazaitandroid.core.repo.home.api.model.DistanceLimit +import org.cazait.cazaitandroid.core.repo.home.api.model.Latitude +import org.cazait.cazaitandroid.core.repo.home.api.model.Longitude +import org.cazait.cazaitandroid.core.repo.home.api.model.SortBy +import org.cazait.cazaitandroid.core.repo.home.mapper.toData +import org.cazait.cazaitandroid.core.repo.home.network.HomeApi import javax.inject.Inject internal class DefaultHomeRepository @Inject constructor( - + private val homeApi: HomeApi, ) : HomeRepository { - override suspend fun getAllCongestionCafes(): CongestionCafes { - TODO("Not yet implemented") + override suspend fun getAllCongestionCafes( + latitude: Latitude, + longitude: Longitude, + sort: SortBy, + limit: DistanceLimit, + ): CongestionCafes { + return homeApi.getAllCongestionCafes( + latitude.asString(), + longitude.asString(), + sort.name, + limit.asString() + ).data.toData() } } \ No newline at end of file diff --git a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/di/HomeRepoModule.kt b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/di/HomeRepoModule.kt new file mode 100644 index 0000000..d82ea39 --- /dev/null +++ b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/di/HomeRepoModule.kt @@ -0,0 +1,28 @@ +package org.cazait.cazaitandroid.core.repo.home.di + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import okhttp3.OkHttpClient +import org.cazait.cazaitandroid.core.repo.home.network.HomeApi +import retrofit2.Converter +import retrofit2.Retrofit +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object HomeRepoModule { + @Provides + @Singleton + fun provideHomeApi( + okHttpClient: OkHttpClient, + converterFactory: Converter.Factory, + ): HomeApi { + return Retrofit.Builder() + .baseUrl("https://cazait.shop") + .addConverterFactory(converterFactory) + .client(okHttpClient).build() + .create(HomeApi::class.java) + } +} diff --git a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/mapper/CongestionCafesMapper.kt b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/mapper/CongestionCafesMapper.kt new file mode 100644 index 0000000..8534cea --- /dev/null +++ b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/mapper/CongestionCafesMapper.kt @@ -0,0 +1,31 @@ +package org.cazait.cazaitandroid.core.repo.home.mapper + +import org.cazait.cazaitandroid.core.repo.home.api.model.Cafe +import org.cazait.cazaitandroid.core.repo.home.api.model.CafeAddress +import org.cazait.cazaitandroid.core.repo.home.api.model.CafeId +import org.cazait.cazaitandroid.core.repo.home.api.model.CafeImage +import org.cazait.cazaitandroid.core.repo.home.api.model.CafeImages +import org.cazait.cazaitandroid.core.repo.home.api.model.CafeName +import org.cazait.cazaitandroid.core.repo.home.api.model.Congestion +import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafe +import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafes +import org.cazait.cazaitandroid.core.repo.home.api.model.Latitude +import org.cazait.cazaitandroid.core.repo.home.api.model.Longitude +import org.cazait.cazaitandroid.core.repo.home.network.model.CongestionCafeResponse +import java.util.UUID + +internal fun List.toData(): CongestionCafes = CongestionCafes( + values = map(CongestionCafeResponse::toData) +) + +internal fun CongestionCafeResponse.toData(): CongestionCafe = CongestionCafe( + cafe = Cafe( + id = CafeId(UUID.fromString(cafeId)), + name = CafeName(name), + address = CafeAddress(address), + cafeImages = CafeImages(cafeImages.map { CafeImage(it) }), + latitude = Latitude(latitude.toDouble()), + longitude = Longitude(longitude.toDouble()), + ), + congestion = Congestion.valueOf(congestionStatus), +) diff --git a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/HomeApi.kt b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/HomeApi.kt new file mode 100644 index 0000000..ed4faf0 --- /dev/null +++ b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/HomeApi.kt @@ -0,0 +1,19 @@ +package org.cazait.cazaitandroid.core.repo.home.network + +import org.cazait.cazaitandroid.core.repo.home.network.model.GetAllCongestionCafesResponse +import retrofit2.http.GET +import retrofit2.http.Query + +internal interface HomeApi { + @GET("api/cafes/all") + suspend fun getAllCongestionCafes( + @Query("latitude") + latitude: String, + @Query("longitude") + longitude: String, + @Query("sort") + sort: String, + @Query("limit") + limit: String, + ): GetAllCongestionCafesResponse +} \ No newline at end of file diff --git a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/model/CongestionCafeResponse.kt b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/model/CongestionCafeResponse.kt new file mode 100644 index 0000000..8238d7f --- /dev/null +++ b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/model/CongestionCafeResponse.kt @@ -0,0 +1,26 @@ +package org.cazait.cazaitandroid.core.repo.home.network.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CongestionCafeResponse( + @SerialName("cafeId") + val cafeId: String, + @SerialName("congestionStatus") + val congestionStatus: String, + @SerialName("name") + val name: String, + @SerialName("address") + val address: String, + @SerialName("latitude") + val latitude: String, + @SerialName("longitude") + val longitude: String, + @SerialName("cafeImages") + val cafeImages: List, + @SerialName("distance") + val distance: Int, + @SerialName("favoritesStatus") + val favoritesStatus: String, +) \ No newline at end of file diff --git a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/model/GetAllCongestionCafesResponse.kt b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/model/GetAllCongestionCafesResponse.kt new file mode 100644 index 0000000..c87da24 --- /dev/null +++ b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/model/GetAllCongestionCafesResponse.kt @@ -0,0 +1,16 @@ +package org.cazait.cazaitandroid.core.repo.home.network.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GetAllCongestionCafesResponse( + @SerialName("code") + val code: Int, + @SerialName("result") + val result: String, + @SerialName("message") + val message: String, + @SerialName("data") + val data: List, +) diff --git a/core/repo/signin/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/signin/network/model/SignInResponse.kt b/core/repo/signin/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/signin/network/model/SignInResponse.kt index a7ec0c7..c6791f6 100644 --- a/core/repo/signin/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/signin/network/model/SignInResponse.kt +++ b/core/repo/signin/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/signin/network/model/SignInResponse.kt @@ -1,11 +1,16 @@ package org.cazait.cazaitandroid.core.repo.signin.network.model +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable internal data class SignInResponse( + @SerialName("code") val code: Int, + @SerialName("result") val result: String, + @SerialName("message") val message: String, + @SerialName("data") val data: UserInformationResponse, ) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bd9925f..6bcd6f3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -androidGradlePlugin = "8.2.0" +androidGradlePlugin = "8.2.1" androidDesugarJdkLibs = "2.0.4" androidxCore = "1.12.0" androidxAppCompat = "1.6.1" @@ -15,7 +15,7 @@ hiltNavigationCompose = "1.1.0" okhttp = "4.12.0" retrofit = "2.9.0" retrofitKotlinxSerializationJson = "1.0.0" -kotlinxSerializationJson = "1.6.1" +kotlinxSerializationJson = "1.5.1" kotlinxDatetime = "0.4.0" kotlinxImmutable = "0.3.6" From 0132741fe4752ab5e5b8d9f643bb7e7add60620a Mon Sep 17 00:00:00 2001 From: bmsk Date: Sat, 6 Jan 2024 11:22:17 +0900 Subject: [PATCH 5/9] =?UTF-8?q?[Feat]=20=EC=B9=B4=ED=8E=98=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=EB=A5=BC=20=EB=B7=B0=EB=AA=A8=EB=8D=B8=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D?= =?UTF-8?q?=20=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 1 + .../core/repo/home/DefaultHomeRepository.kt | 2 +- .../core/repo/home/di/HomeRepoModule.kt | 12 ++++++++ .../model/GetAllCongestionCafesResponse.kt | 2 +- feature/home/build.gradle.kts | 2 ++ .../cazaitandroid/feature/home/HomeRoute.kt | 6 ++++ .../cazaitandroid/feature/home/HomeScreen.kt | 17 ++++++++--- .../cazaitandroid/feature/home/HomeUiState.kt | 10 +++++++ .../feature/home/HomeViewModel.kt | 30 ++++++++++++++++++- .../feature/home/di/UseCaseBindModule.kt | 19 ++++++++++++ .../home/usecase/GetCongestionCafesUseCase.kt | 26 ++++++++++++++++ 11 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt create mode 100644 feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/di/UseCaseBindModule.kt create mode 100644 feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/usecase/GetCongestionCafesUseCase.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9239c31..e4c8814 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { implementation(projects.feature.splash) implementation(projects.core.repo.signin.impl) + implementation(projects.core.repo.home.impl) implementation(projects.core.http) diff --git a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/DefaultHomeRepository.kt b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/DefaultHomeRepository.kt index c0bd0c6..4aa0719 100644 --- a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/DefaultHomeRepository.kt +++ b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/DefaultHomeRepository.kt @@ -24,6 +24,6 @@ internal class DefaultHomeRepository @Inject constructor( longitude.asString(), sort.name, limit.asString() - ).data.toData() + ).data[0].toData() } } \ No newline at end of file diff --git a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/di/HomeRepoModule.kt b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/di/HomeRepoModule.kt index d82ea39..4a6bafd 100644 --- a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/di/HomeRepoModule.kt +++ b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/di/HomeRepoModule.kt @@ -1,10 +1,13 @@ package org.cazait.cazaitandroid.core.repo.home.di +import dagger.Binds import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import okhttp3.OkHttpClient +import org.cazait.cazaitandroid.core.repo.home.DefaultHomeRepository +import org.cazait.cazaitandroid.core.repo.home.api.HomeRepository import org.cazait.cazaitandroid.core.repo.home.network.HomeApi import retrofit2.Converter import retrofit2.Retrofit @@ -26,3 +29,12 @@ internal object HomeRepoModule { .create(HomeApi::class.java) } } + +@Module +@InstallIn(SingletonComponent::class) +internal abstract class HomeRepoBindModule { + @Binds + abstract fun bindHomeRepository( + dataSource: DefaultHomeRepository, + ): HomeRepository +} diff --git a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/model/GetAllCongestionCafesResponse.kt b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/model/GetAllCongestionCafesResponse.kt index c87da24..8290cfe 100644 --- a/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/model/GetAllCongestionCafesResponse.kt +++ b/core/repo/home/impl/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/network/model/GetAllCongestionCafesResponse.kt @@ -12,5 +12,5 @@ data class GetAllCongestionCafesResponse( @SerialName("message") val message: String, @SerialName("data") - val data: List, + val data: List>, ) diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index 8f3a4f3..11c86e9 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -7,5 +7,7 @@ android { } dependencies { + implementation(projects.core.repo.home.api) + implementation(libs.kotlinx.immutable) } diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt index 13cffdd..f509828 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt @@ -3,7 +3,9 @@ package org.cazait.cazaitandroid.feature.home import androidx.compose.foundation.layout.PaddingValues import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.coroutines.flow.collectLatest @Composable @@ -12,11 +14,15 @@ internal fun HomeRoute( onShowErrorSnackbar: (throwable: Throwable?) -> Unit, homeViewModel: HomeViewModel = hiltViewModel(), ) { + val uiState by homeViewModel.uiState.collectAsStateWithLifecycle() + LaunchedEffect(true) { homeViewModel.errorFlow.collectLatest { throwable -> onShowErrorSnackbar(throwable) } } + HomeScreen( padding = padding, onClickCafe = {}, + uiState = uiState, ) } diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt index b067ef0..c962ed0 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -34,11 +35,13 @@ import org.cazait.cazaitandroid.core.designsystem.component.CazaitCard import org.cazait.cazaitandroid.core.designsystem.theme.Black import org.cazait.cazaitandroid.core.designsystem.theme.CazaitTheme import org.cazait.cazaitandroid.core.designsystem.theme.White +import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafes @Composable internal fun HomeScreen( padding: PaddingValues, onClickCafe: () -> Unit, + uiState: HomeUiState, modifier: Modifier = Modifier, ) { Column( @@ -55,9 +58,11 @@ internal fun HomeScreen( .padding(horizontal = 20.dp) ) { item { HomeColumnTitle() } - item { - CazaitCard { - + if (uiState is HomeUiState.Success) { + items(uiState.congestionCafes.asList()) { + CazaitCard { + Text(text = it.cafe.name.asString()) + } } } } @@ -161,6 +166,10 @@ internal fun HomeLoading() { @Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) private fun HomeScreenPreview() { CazaitTheme { - HomeScreen(padding = PaddingValues(0.dp), onClickCafe = {}) + HomeScreen( + padding = PaddingValues(0.dp), + onClickCafe = {}, + uiState = HomeUiState.Loading, + ) } } diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt new file mode 100644 index 0000000..d55e5ba --- /dev/null +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt @@ -0,0 +1,10 @@ +package org.cazait.cazaitandroid.feature.home + +import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafes + +internal sealed interface HomeUiState { + data object Loading : HomeUiState + data class Success( + val congestionCafes: CongestionCafes, + ) : HomeUiState +} \ No newline at end of file diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt index b1de6ac..f207fd3 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt @@ -1,13 +1,41 @@ package org.cazait.cazaitandroid.feature.home import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import org.cazait.cazaitandroid.feature.home.usecase.GetCongestionCafesUseCase import javax.inject.Inject @HiltViewModel -class HomeViewModel @Inject constructor() : ViewModel() { +internal class HomeViewModel @Inject constructor( + private val getCongestionCafesUseCase: GetCongestionCafesUseCase, +) : ViewModel() { private val _errorFlow = MutableSharedFlow() val errorFlow = _errorFlow.asSharedFlow() + + private val _uiState: MutableStateFlow = MutableStateFlow(HomeUiState.Loading) + val uiState = _uiState.asStateFlow() + + init { + viewModelScope.launch { + flow { emit(getCongestionCafesUseCase()) } + .map(HomeUiState::Success) + .catch { + it.printStackTrace() + _errorFlow.emit(it) + } + .collect { success -> + _uiState.update { success } + } + } + } } \ No newline at end of file diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/di/UseCaseBindModule.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/di/UseCaseBindModule.kt new file mode 100644 index 0000000..d9daaeb --- /dev/null +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/di/UseCaseBindModule.kt @@ -0,0 +1,19 @@ +package org.cazait.cazaitandroid.feature.home.di + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ViewModelComponent +import dagger.hilt.android.scopes.ViewModelScoped +import org.cazait.cazaitandroid.feature.home.usecase.GetCongestionCafesUseCase +import org.cazait.cazaitandroid.feature.home.usecase.GetCongestionCafesUseCaseImpl + +@Module +@InstallIn(ViewModelComponent::class) +internal abstract class UseCaseBindModule { + @Binds + @ViewModelScoped + abstract fun bindGetCongestionCafesUseCase( + datasource: GetCongestionCafesUseCaseImpl + ): GetCongestionCafesUseCase +} \ No newline at end of file diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/usecase/GetCongestionCafesUseCase.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/usecase/GetCongestionCafesUseCase.kt new file mode 100644 index 0000000..ea774e7 --- /dev/null +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/usecase/GetCongestionCafesUseCase.kt @@ -0,0 +1,26 @@ +package org.cazait.cazaitandroid.feature.home.usecase + +import org.cazait.cazaitandroid.core.repo.home.api.HomeRepository +import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafes +import org.cazait.cazaitandroid.core.repo.home.api.model.DistanceLimit +import org.cazait.cazaitandroid.core.repo.home.api.model.Latitude +import org.cazait.cazaitandroid.core.repo.home.api.model.Longitude +import org.cazait.cazaitandroid.core.repo.home.api.model.SortBy +import javax.inject.Inject + +internal interface GetCongestionCafesUseCase { + suspend operator fun invoke(): CongestionCafes +} + +internal class GetCongestionCafesUseCaseImpl @Inject constructor( + private val homeRepository: HomeRepository, +) : GetCongestionCafesUseCase { + override suspend fun invoke(): CongestionCafes { + return homeRepository.getAllCongestionCafes( + latitude = Latitude(0.0), + longitude = Longitude(0.0), + sort = SortBy.CONGESTION, + limit = DistanceLimit(0), + ) + } +} \ No newline at end of file From ab5da312058f0c00deef2027dd11872b921f0286 Mon Sep 17 00:00:00 2001 From: bmsk Date: Sat, 6 Jan 2024 13:07:15 +0900 Subject: [PATCH 6/9] =?UTF-8?q?[Feat]=20=EC=9C=84=EC=B9=98=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=EC=9C=BC=EB=A1=9C=20=EC=B9=B4=ED=8E=98=EB=A5=BC=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 2 + core/location/build.gradle.kts | 18 +++++ feature/home/build.gradle.kts | 2 + .../cazaitandroid/feature/home/HomeRoute.kt | 66 +++++++++++++++++++ .../cazaitandroid/feature/home/HomeScreen.kt | 32 +++++---- .../cazaitandroid/feature/home/HomeUiState.kt | 4 +- .../feature/home/HomeViewModel.kt | 18 ++++- .../home/usecase/GetCongestionCafesUseCase.kt | 22 +++++-- gradle/libs.versions.toml | 11 +++- settings.gradle.kts | 1 + 10 files changed, 154 insertions(+), 22 deletions(-) create mode 100644 core/location/build.gradle.kts diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3dc05e1..2e2f87a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,8 @@ xmlns:tools="http://schemas.android.com/tools"> + + onShowErrorSnackbar(throwable) } diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt index c962ed0..8de739b 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt @@ -36,6 +36,7 @@ import org.cazait.cazaitandroid.core.designsystem.theme.Black import org.cazait.cazaitandroid.core.designsystem.theme.CazaitTheme import org.cazait.cazaitandroid.core.designsystem.theme.White import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafes +import org.cazait.cazaitandroid.feature.home.permission.LocationPermissionRequest @Composable internal fun HomeScreen( @@ -52,20 +53,26 @@ internal fun HomeScreen( verticalArrangement = Arrangement.spacedBy(20.dp), ) { HomeTopBar() - LazyColumn( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp) - ) { - item { HomeColumnTitle() } - if (uiState is HomeUiState.Success) { - items(uiState.congestionCafes.asList()) { - CazaitCard { - Text(text = it.cafe.name.asString()) + when (uiState) { + is HomeUiState.Success -> { + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp) + ) { + item { HomeColumnTitle() } + items(uiState.congestionCafes.asList()) { + CazaitCard { + Text(text = it.cafe.name.asString()) + } } } } + is HomeUiState.Loading -> { + + } } + } } @@ -158,7 +165,10 @@ private fun SearchingTextField( } @Composable -internal fun HomeLoading() { +internal fun HomeLoading(onPermissionGranted: (latitude: Double, longitude: Double) -> Unit) { + LocationPermissionRequest { + + } } @Composable diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt index d55e5ba..d97821e 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt @@ -7,4 +7,6 @@ internal sealed interface HomeUiState { data class Success( val congestionCafes: CongestionCafes, ) : HomeUiState -} \ No newline at end of file +} + +internal data class LocationDetails(val latitude: Double, val longitude: Double) \ No newline at end of file diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt index f207fd3..649e3ae 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt @@ -12,6 +12,10 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import org.cazait.cazaitandroid.core.repo.home.api.model.DistanceLimit +import org.cazait.cazaitandroid.core.repo.home.api.model.Latitude +import org.cazait.cazaitandroid.core.repo.home.api.model.Longitude +import org.cazait.cazaitandroid.core.repo.home.api.model.SortBy import org.cazait.cazaitandroid.feature.home.usecase.GetCongestionCafesUseCase import javax.inject.Inject @@ -25,9 +29,19 @@ internal class HomeViewModel @Inject constructor( private val _uiState: MutableStateFlow = MutableStateFlow(HomeUiState.Loading) val uiState = _uiState.asStateFlow() - init { + fun fetchCongestionCafes( + latitude: Double, + longitude: Double, + sortBy: SortBy = SortBy.DISTANCE, + limit: DistanceLimit = DistanceLimit(1000), + ) { viewModelScope.launch { - flow { emit(getCongestionCafesUseCase()) } + flow { emit(getCongestionCafesUseCase( + latitude = Latitude(latitude), + longitude = Longitude(longitude), + sortBy = sortBy, + limit = limit, + )) } .map(HomeUiState::Success) .catch { it.printStackTrace() diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/usecase/GetCongestionCafesUseCase.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/usecase/GetCongestionCafesUseCase.kt index ea774e7..156bb87 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/usecase/GetCongestionCafesUseCase.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/usecase/GetCongestionCafesUseCase.kt @@ -9,18 +9,28 @@ import org.cazait.cazaitandroid.core.repo.home.api.model.SortBy import javax.inject.Inject internal interface GetCongestionCafesUseCase { - suspend operator fun invoke(): CongestionCafes + suspend operator fun invoke( + latitude: Latitude, + longitude: Longitude, + sortBy: SortBy, + limit: DistanceLimit + ): CongestionCafes } internal class GetCongestionCafesUseCaseImpl @Inject constructor( private val homeRepository: HomeRepository, ) : GetCongestionCafesUseCase { - override suspend fun invoke(): CongestionCafes { + override suspend fun invoke( + latitude: Latitude, + longitude: Longitude, + sortBy: SortBy, + limit: DistanceLimit, + ): CongestionCafes { return homeRepository.getAllCongestionCafes( - latitude = Latitude(0.0), - longitude = Longitude(0.0), - sort = SortBy.CONGESTION, - limit = DistanceLimit(0), + latitude = latitude, + longitude = longitude, + sort = sortBy, + limit = limit, ) } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6bcd6f3..4a39b1c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,6 +21,9 @@ kotlinxImmutable = "0.3.6" landscapist = "2.2.5" composeShimmer = "1.0.5" +accompanistPermissions = "0.32.0" + +playServicesLocation = "21.0.1" junit4 = "4.13.2" junitVintageEngine = "5.10.0" @@ -89,6 +92,10 @@ landscapist-animation = { group = "com.github.skydoves", name = "landscapist-ani compose-shimmer = { group = "com.valentinilk.shimmer", name = "compose-shimmer", version.ref = "composeShimmer" } +accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanistPermissions" } + +play-services-location = { group = "com.google.android.gms", name = "play-services-location", version.ref = "playServicesLocation" } + junit4 = { group = "junit", name = "junit", version.ref = "junit4" } junit-vintage-engine = { group = "org.junit.vintage", name = "junit-vintage-engine", version.ref = "junitVintageEngine" } androidx-test-ext = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "androidxTestExt" } @@ -98,7 +105,7 @@ kotest-runner = { group = "io.kotest", name = "kotest-runner-junit5", version.re kotest-assertions = { group = "io.kotest", name = "kotest-assertions-core", version.ref = "kotest" } kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlin" } mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" } -robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric"} +robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" } coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutine" } @@ -117,7 +124,7 @@ oss-licenses-plugin = { group = "com.google.android.gms", name = "oss-licenses-p androidx-glance = { group = "androidx.glance", name = "glance", version.ref = "androidxGlance" } androidx-glance-appwidget = { group = "androidx.glance", name = "glance-appwidget", version.ref = "androidxGlance" } -glance-tools-appwidget-host = { group = "com.google.android.glance.tools", name = "appwidget-host", version.ref = "glanceExperimentalTools"} +glance-tools-appwidget-host = { group = "com.google.android.glance.tools", name = "appwidget-host", version.ref = "glanceExperimentalTools" } [bundles] diff --git a/settings.gradle.kts b/settings.gradle.kts index 7b8c272..365dabe 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,6 +29,7 @@ include( ":core:ui", ":core:http", ":core:testing", + ":core:location", ":feature:signin", ":feature:splash", From d2442425b9427aebeb0b8de2adcc6c2f968e4c99 Mon Sep 17 00:00:00 2001 From: bmsk Date: Sat, 6 Jan 2024 14:20:11 +0900 Subject: [PATCH 7/9] =?UTF-8?q?[Feat]=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EC=B9=B4?= =?UTF-8?q?=ED=8E=98=20=EC=A0=95=EB=B3=B4=EB=A5=BC=20=EA=B0=80=EC=A0=B8?= =?UTF-8?q?=EC=98=A4=EB=8F=84=EB=A1=9D=20=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 한 번만 호출 가능하도록 한다 - 다른 모듈에서도 사용 가능하게끔 한다 --- core/location/build.gradle.kts | 2 + .../core/location/LocationDetails.kt | 3 + .../core/location/LocationService.kt | 55 +++++++++++++++++++ .../core/location/di/LocationModule.kt | 40 ++++++++++++++ .../extension/hasLocationPermission.kt | 19 +++++++ .../location/usecase/GetLocationUseCase.kt | 16 ++++++ feature/home/build.gradle.kts | 1 + .../cazaitandroid/feature/home/HomeRoute.kt | 51 ++++------------- .../cazaitandroid/feature/home/HomeScreen.kt | 14 +---- .../cazaitandroid/feature/home/HomeUiState.kt | 13 ++++- .../feature/home/HomeViewModel.kt | 45 ++++++++++++--- 11 files changed, 199 insertions(+), 60 deletions(-) create mode 100644 core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/LocationDetails.kt create mode 100644 core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/LocationService.kt create mode 100644 core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/di/LocationModule.kt create mode 100644 core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/extension/hasLocationPermission.kt create mode 100644 core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/usecase/GetLocationUseCase.kt diff --git a/core/location/build.gradle.kts b/core/location/build.gradle.kts index f069265..4fc8d84 100644 --- a/core/location/build.gradle.kts +++ b/core/location/build.gradle.kts @@ -12,6 +12,8 @@ android { } dependencies { + implementation(libs.play.services.location) + androidTestImplementation(libs.androidx.test.ext) androidTestImplementation(libs.androidx.test.espresso.core) androidTestImplementation(libs.coroutines.test) diff --git a/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/LocationDetails.kt b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/LocationDetails.kt new file mode 100644 index 0000000..87456ea --- /dev/null +++ b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/LocationDetails.kt @@ -0,0 +1,3 @@ +package org.cazait.cazaitandroid.core.location + +data class LocationDetails(val latitude: Double, val longitude: Double) \ No newline at end of file diff --git a/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/LocationService.kt b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/LocationService.kt new file mode 100644 index 0000000..b768181 --- /dev/null +++ b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/LocationService.kt @@ -0,0 +1,55 @@ +package org.cazait.cazaitandroid.core.location + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Looper +import com.google.android.gms.location.FusedLocationProviderClient +import com.google.android.gms.location.LocationCallback +import com.google.android.gms.location.LocationRequest +import com.google.android.gms.location.LocationResult +import com.google.android.gms.location.Priority +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import org.cazait.cazaitandroid.core.location.extension.hasLocationPermission +import javax.inject.Inject + +interface LocationService { + fun requestLocationUpdates(): Flow +} + +internal class LocationServiceImpl @Inject constructor( + private val context: Context, + private val locationClient: FusedLocationProviderClient, +) : LocationService { + @SuppressLint("MissingPermission") + override fun requestLocationUpdates(): Flow = callbackFlow { + if (!context.hasLocationPermission()) { + trySend(null) + return@callbackFlow + } + + val request = LocationRequest.Builder(10000L) + .setIntervalMillis(10000L) + .setPriority(Priority.PRIORITY_HIGH_ACCURACY) + .build() + + val locationCallback = object : LocationCallback() { + override fun onLocationResult(locationResult: LocationResult) { + locationResult.locations.lastOrNull()?.let { + trySend(LocationDetails(latitude = it.latitude, longitude = it.longitude)) + } + } + } + + locationClient.requestLocationUpdates( + request, + locationCallback, + Looper.getMainLooper() + ) + + awaitClose { + locationClient.removeLocationUpdates(locationCallback) + } + } +} diff --git a/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/di/LocationModule.kt b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/di/LocationModule.kt new file mode 100644 index 0000000..14338a3 --- /dev/null +++ b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/di/LocationModule.kt @@ -0,0 +1,40 @@ +package org.cazait.cazaitandroid.core.location.di + +import android.content.Context +import com.google.android.gms.location.LocationServices +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ViewModelComponent +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.scopes.ViewModelScoped +import dagger.hilt.components.SingletonComponent +import org.cazait.cazaitandroid.core.location.LocationService +import org.cazait.cazaitandroid.core.location.LocationServiceImpl +import org.cazait.cazaitandroid.core.location.usecase.GetLocationUseCase +import org.cazait.cazaitandroid.core.location.usecase.GetLocationUseCaseImpl +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object LocationModule { + @Singleton + @Provides + fun provideLocationClient( + @ApplicationContext context: Context, + ): LocationService = LocationServiceImpl( + context = context, + locationClient = LocationServices.getFusedLocationProviderClient(context) + ) +} + +@Module +@InstallIn(ViewModelComponent::class) +internal abstract class UseCaseBindModule { + @Binds + @ViewModelScoped + abstract fun bindGetLocationUseCase( + dataSource: GetLocationUseCaseImpl + ): GetLocationUseCase +} \ No newline at end of file diff --git a/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/extension/hasLocationPermission.kt b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/extension/hasLocationPermission.kt new file mode 100644 index 0000000..6ad0ada --- /dev/null +++ b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/extension/hasLocationPermission.kt @@ -0,0 +1,19 @@ +package org.cazait.cazaitandroid.core.location.extension + +import android.Manifest +import android.content.Context +import android.content.pm.PackageManager +import androidx.core.content.ContextCompat + +fun Context.hasLocationPermission(): Boolean { + val fineLocationGranted = ContextCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + val coarseLocationGranted = ContextCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_COARSE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + + return fineLocationGranted && coarseLocationGranted +} \ No newline at end of file diff --git a/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/usecase/GetLocationUseCase.kt b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/usecase/GetLocationUseCase.kt new file mode 100644 index 0000000..89c5466 --- /dev/null +++ b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/usecase/GetLocationUseCase.kt @@ -0,0 +1,16 @@ +package org.cazait.cazaitandroid.core.location.usecase + +import kotlinx.coroutines.flow.Flow +import org.cazait.cazaitandroid.core.location.LocationDetails +import org.cazait.cazaitandroid.core.location.LocationService +import javax.inject.Inject + +interface GetLocationUseCase { + operator fun invoke(): Flow +} + +internal class GetLocationUseCaseImpl @Inject constructor( + private val locationService: LocationService +): GetLocationUseCase { + override fun invoke(): Flow = locationService.requestLocationUpdates() +} \ No newline at end of file diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index 7f8fca4..5c357d0 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -8,6 +8,7 @@ android { dependencies { implementation(projects.core.repo.home.api) + implementation(projects.core.location) implementation(libs.accompanist.permissions) implementation(libs.play.services.location) diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt index 2cec739..00902b2 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt @@ -21,6 +21,8 @@ import com.google.android.gms.location.LocationResult import com.google.android.gms.location.LocationServices import com.google.android.gms.location.Priority import kotlinx.coroutines.flow.collectLatest +import org.cazait.cazaitandroid.core.location.LocationDetails +import org.cazait.cazaitandroid.core.location.extension.hasLocationPermission @SuppressLint("MissingPermission") @OptIn(ExperimentalPermissionsApi::class) @@ -32,24 +34,6 @@ internal fun HomeRoute( ) { val uiState by homeViewModel.uiState.collectAsStateWithLifecycle() val context = LocalContext.current - var currentLocation by remember { - mutableStateOf(LocationDetails(0.0, 0.0)) - } - val fusedLocationClient = remember { - LocationServices.getFusedLocationProviderClient(context) - } - val locationCallback = remember { - object : LocationCallback() { - override fun onLocationResult(p0: LocationResult) { - for (lo in p0.locations) { - currentLocation = LocationDetails( - latitude = lo.latitude, - longitude = lo.longitude, - ) - } - } - } - } val locationPermissionState = rememberMultiplePermissionsState( permissions = @@ -60,30 +44,17 @@ internal fun HomeRoute( ) LaunchedEffect(true) { - locationPermissionState.launchMultiplePermissionRequest() - } - - LaunchedEffect(key1 = locationPermissionState.allPermissionsGranted) { - val locationRequest = LocationRequest.Builder(10000L) - .setIntervalMillis(10000L) - .setPriority(Priority.PRIORITY_HIGH_ACCURACY) - .build() - fusedLocationClient.requestLocationUpdates( - locationRequest, - locationCallback, - Looper.getMainLooper(), - ) + homeViewModel.errorFlow.collectLatest { throwable -> onShowErrorSnackbar(throwable) } } - - LaunchedEffect(key1 = currentLocation) { - homeViewModel.fetchCongestionCafes( - latitude = currentLocation.latitude, - longitude = currentLocation.longitude, - ) + LaunchedEffect(!context.hasLocationPermission()) { + locationPermissionState.launchMultiplePermissionRequest() } - - LaunchedEffect(true) { - homeViewModel.errorFlow.collectLatest { throwable -> onShowErrorSnackbar(throwable) } + when { + locationPermissionState.allPermissionsGranted -> { + LaunchedEffect(Unit) { + homeViewModel.handlePermission(PermissionEvent.Granted) + } + } } HomeScreen( diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt index 8de739b..5c6823b 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt @@ -35,8 +35,6 @@ import org.cazait.cazaitandroid.core.designsystem.component.CazaitCard import org.cazait.cazaitandroid.core.designsystem.theme.Black import org.cazait.cazaitandroid.core.designsystem.theme.CazaitTheme import org.cazait.cazaitandroid.core.designsystem.theme.White -import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafes -import org.cazait.cazaitandroid.feature.home.permission.LocationPermissionRequest @Composable internal fun HomeScreen( @@ -68,8 +66,9 @@ internal fun HomeScreen( } } } - is HomeUiState.Loading -> { - + + else -> { + } } @@ -164,13 +163,6 @@ private fun SearchingTextField( } } -@Composable -internal fun HomeLoading(onPermissionGranted: (latitude: Double, longitude: Double) -> Unit) { - LocationPermissionRequest { - - } -} - @Composable @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt index d97821e..2a26f8c 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt @@ -4,9 +4,20 @@ import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafes internal sealed interface HomeUiState { data object Loading : HomeUiState + + data class Location( + val latitude: Double, + val longitude: Double, + ) : HomeUiState + data class Success( val congestionCafes: CongestionCafes, ) : HomeUiState + + data object RevokedPermissions : HomeUiState } -internal data class LocationDetails(val latitude: Double, val longitude: Double) \ No newline at end of file +internal sealed interface PermissionEvent { + data object Granted: PermissionEvent + data object Revoked: PermissionEvent +} diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt index 649e3ae..2a71d96 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import org.cazait.cazaitandroid.core.location.usecase.GetLocationUseCase import org.cazait.cazaitandroid.core.repo.home.api.model.DistanceLimit import org.cazait.cazaitandroid.core.repo.home.api.model.Latitude import org.cazait.cazaitandroid.core.repo.home.api.model.Longitude @@ -22,6 +23,7 @@ import javax.inject.Inject @HiltViewModel internal class HomeViewModel @Inject constructor( private val getCongestionCafesUseCase: GetCongestionCafesUseCase, + private val getLocationUseCase: GetLocationUseCase, ) : ViewModel() { private val _errorFlow = MutableSharedFlow() val errorFlow = _errorFlow.asSharedFlow() @@ -30,18 +32,23 @@ internal class HomeViewModel @Inject constructor( val uiState = _uiState.asStateFlow() fun fetchCongestionCafes( - latitude: Double, - longitude: Double, sortBy: SortBy = SortBy.DISTANCE, limit: DistanceLimit = DistanceLimit(1000), ) { + val state = _uiState.value + if (state !is HomeUiState.Location) return + viewModelScope.launch { - flow { emit(getCongestionCafesUseCase( - latitude = Latitude(latitude), - longitude = Longitude(longitude), - sortBy = sortBy, - limit = limit, - )) } + flow { + emit( + getCongestionCafesUseCase( + latitude = Latitude(state.latitude), + longitude = Longitude(state.longitude), + sortBy = sortBy, + limit = limit, + ) + ) + } .map(HomeUiState::Success) .catch { it.printStackTrace() @@ -52,4 +59,26 @@ internal class HomeViewModel @Inject constructor( } } } + + fun handlePermission(event: PermissionEvent) { + when (event) { + PermissionEvent.Granted -> { + viewModelScope.launch { + getLocationUseCase().collect { location -> + if (_uiState.value is HomeUiState.Loading) { + _uiState.value = HomeUiState.Location( + latitude = location?.latitude ?: 37.0, + longitude = location?.longitude ?: 126.0 + ) + fetchCongestionCafes() + } + } + } + } + + PermissionEvent.Revoked -> { + _uiState.update { HomeUiState.RevokedPermissions } + } + } + } } \ No newline at end of file From 7c40eda306accd0d2d3e7c97d74291e04b771af2 Mon Sep 17 00:00:00 2001 From: bmsk Date: Sat, 6 Jan 2024 18:16:09 +0900 Subject: [PATCH 8/9] =?UTF-8?q?[Feat]=20=EA=B7=BC=EC=B2=98=EC=97=90=20?= =?UTF-8?q?=EC=9E=88=EB=8A=94=20=EC=B9=B4=ED=8E=98=EB=93=A4=EC=9D=84=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../designsystem/component/NetworkImage.kt | 47 +++++++ .../core/designsystem/theme/Theme.kt | 4 +- .../cazaitandroid/feature/home/HomeScreen.kt | 124 +++++++++++------ .../cazaitandroid/feature/home/HomeUiState.kt | 5 - .../feature/home/HomeViewModel.kt | 22 +-- .../home/component/HomeCongestionCafeItem.kt | 127 ++++++++++++++++++ feature/home/src/main/res/values/strings.xml | 5 + 7 files changed, 273 insertions(+), 61 deletions(-) create mode 100644 core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/component/NetworkImage.kt create mode 100644 feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/component/HomeCongestionCafeItem.kt diff --git a/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/component/NetworkImage.kt b/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/component/NetworkImage.kt new file mode 100644 index 0000000..fddd2fd --- /dev/null +++ b/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/component/NetworkImage.kt @@ -0,0 +1,47 @@ +package org.cazait.cazaitandroid.core.designsystem.component + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import com.skydoves.landscapist.ImageOptions +import com.skydoves.landscapist.coil.CoilImage +import com.skydoves.landscapist.components.rememberImageComponent +import com.skydoves.landscapist.placeholder.placeholder.PlaceholderPlugin + +@Composable +fun NetworkImage( + imageUrl: String?, + modifier: Modifier = Modifier, + placeholder: Painter? = null, + contentScale: ContentScale = ContentScale.Crop, + contentDescription: String? = null, +) { + CoilImage( + imageModel = { imageUrl }, + modifier = modifier, + component = rememberImageComponent { + +PlaceholderPlugin.Loading(placeholder) + +PlaceholderPlugin.Failure(placeholder) + }, + imageOptions = ImageOptions( + contentScale = contentScale, + alignment = Alignment.Center, + contentDescription = contentDescription, + ) + ) +} + +@Preview +@Composable +private fun PreviewNetworkImage() { + NetworkImage( + imageUrl = "", + placeholder = painterResource(id = Color(0xFFFFFFFF).toArgb()) + ) +} diff --git a/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/theme/Theme.kt b/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/theme/Theme.kt index a64058c..976bf28 100644 --- a/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/theme/Theme.kt +++ b/core/designsystem/src/main/kotlin/org/cazait/cazaitandroid/core/designsystem/theme/Theme.kt @@ -15,7 +15,7 @@ import androidx.compose.ui.platform.LocalView import androidx.core.view.WindowCompat private val DarkColorScheme = darkColorScheme( - primary = White, + primary = SunsetOrange, onPrimary = Black, primaryContainer = Graphite, onPrimaryContainer = White, @@ -43,7 +43,7 @@ private val DarkColorScheme = darkColorScheme( ) private val LightColorScheme = lightColorScheme( - primary = Black, + primary = SunsetOrange, onPrimary = White, primaryContainer = White, onPrimaryContainer = Black, diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt index 5c6823b..2036093 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt @@ -10,11 +10,16 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.GridItemSpan +import androidx.compose.foundation.lazy.grid.LazyGridItemScope +import androidx.compose.foundation.lazy.grid.LazyGridScope +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -31,10 +36,13 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import org.cazait.cazaitandroid.core.designsystem.component.CazaitCard +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList import org.cazait.cazaitandroid.core.designsystem.theme.Black import org.cazait.cazaitandroid.core.designsystem.theme.CazaitTheme import org.cazait.cazaitandroid.core.designsystem.theme.White +import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafe +import org.cazait.cazaitandroid.feature.home.component.HomeCongestionCafeItem @Composable internal fun HomeScreen( @@ -51,47 +59,7 @@ internal fun HomeScreen( verticalArrangement = Arrangement.spacedBy(20.dp), ) { HomeTopBar() - when (uiState) { - is HomeUiState.Success -> { - LazyColumn( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp) - ) { - item { HomeColumnTitle() } - items(uiState.congestionCafes.asList()) { - CazaitCard { - Text(text = it.cafe.name.asString()) - } - } - } - } - - else -> { - - } - } - - } -} - -@Composable -private fun HomeColumnTitle() { - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = stringResource(id = R.string.home_cafes_column_title), - style = CazaitTheme.typography.titleLargeBL - ) - Spacer(modifier = Modifier.weight(1f)) - Image( - imageVector = ImageVector.vectorResource(R.drawable.ic_filter), - contentDescription = "filter", - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = stringResource(id = R.string.distance), - style = CazaitTheme.typography.titleMediumB - ) + HomeContent(uiState) } } @@ -125,6 +93,37 @@ private fun HomeTopBar() { } } +@Composable +private fun HomeContent(uiState: HomeUiState) { + when (uiState) { + is HomeUiState.Success -> CongestionCafeGrid( + uiState.congestionCafes.asList() + .toImmutableList() + ) + + else -> Unit + } +} + +@Composable +private fun CongestionCafeGrid(cafes: ImmutableList) { + LazyVerticalGrid( + columns = GridCells.Fixed(2), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp) + .background(color = White), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalArrangement = Arrangement.spacedBy(24.dp) + ) { + header { HomeColumnTitle() } + items(cafes) { + HomeCongestionCafeItem(congestionCafe = it) + } + footer { Spacer(modifier = Modifier.height(32.dp)) } + } +} + @Composable private fun SearchingTextField( modifier: Modifier = Modifier, @@ -163,6 +162,43 @@ private fun SearchingTextField( } } +private fun LazyGridScope.header( + content: @Composable LazyGridItemScope.() -> Unit, +) { + item(span = { GridItemSpan(this.maxLineSpan) }, content = content) +} + +private fun LazyGridScope.footer( + content: @Composable LazyGridItemScope.() -> Unit, +) { + item(span = { GridItemSpan(this.maxLineSpan) }, content = content) +} + +@Composable +private fun HomeColumnTitle() { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = stringResource(id = R.string.home_cafes_column_title), + style = CazaitTheme.typography.titleLargeBL + ) + Spacer(modifier = Modifier.weight(1f)) + Image( + imageVector = ImageVector.vectorResource(R.drawable.ic_filter), + contentDescription = "filter", + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = stringResource(id = R.string.distance), + style = CazaitTheme.typography.titleMediumB + ) + } +} + @Composable @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt index 2a26f8c..d48c88f 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt @@ -5,11 +5,6 @@ import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafes internal sealed interface HomeUiState { data object Loading : HomeUiState - data class Location( - val latitude: Double, - val longitude: Double, - ) : HomeUiState - data class Success( val congestionCafes: CongestionCafes, ) : HomeUiState diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt index 2a71d96..47f211d 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeViewModel.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import org.cazait.cazaitandroid.core.location.LocationDetails import org.cazait.cazaitandroid.core.location.usecase.GetLocationUseCase import org.cazait.cazaitandroid.core.repo.home.api.model.DistanceLimit import org.cazait.cazaitandroid.core.repo.home.api.model.Latitude @@ -28,22 +29,24 @@ internal class HomeViewModel @Inject constructor( private val _errorFlow = MutableSharedFlow() val errorFlow = _errorFlow.asSharedFlow() + private val _currentLocation = MutableStateFlow(LocationDetails(0.0, 0.0)) + val currentLocation = _currentLocation.asStateFlow() + private val _uiState: MutableStateFlow = MutableStateFlow(HomeUiState.Loading) val uiState = _uiState.asStateFlow() fun fetchCongestionCafes( sortBy: SortBy = SortBy.DISTANCE, - limit: DistanceLimit = DistanceLimit(1000), + limit: DistanceLimit = DistanceLimit(2000), ) { - val state = _uiState.value - if (state !is HomeUiState.Location) return + _uiState.update { HomeUiState.Loading } viewModelScope.launch { flow { emit( getCongestionCafesUseCase( - latitude = Latitude(state.latitude), - longitude = Longitude(state.longitude), + latitude = Latitude(_currentLocation.value.latitude), + longitude = Longitude(_currentLocation.value.longitude), sortBy = sortBy, limit = limit, ) @@ -64,13 +67,12 @@ internal class HomeViewModel @Inject constructor( when (event) { PermissionEvent.Granted -> { viewModelScope.launch { + var isFirstLocationCollected = false getLocationUseCase().collect { location -> - if (_uiState.value is HomeUiState.Loading) { - _uiState.value = HomeUiState.Location( - latitude = location?.latitude ?: 37.0, - longitude = location?.longitude ?: 126.0 - ) + _currentLocation.update { location ?: LocationDetails(37.0, 126.0) } + if (!isFirstLocationCollected) { fetchCongestionCafes() + isFirstLocationCollected = true } } } diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/component/HomeCongestionCafeItem.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/component/HomeCongestionCafeItem.kt new file mode 100644 index 0000000..566be37 --- /dev/null +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/component/HomeCongestionCafeItem.kt @@ -0,0 +1,127 @@ +package org.cazait.cazaitandroid.feature.home.component + +import android.content.res.Configuration +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.cazait.cazaitandroid.core.designsystem.component.CazaitCard +import org.cazait.cazaitandroid.core.designsystem.component.NetworkImage +import org.cazait.cazaitandroid.core.designsystem.theme.CazaitTheme +import org.cazait.cazaitandroid.core.repo.home.api.model.Cafe +import org.cazait.cazaitandroid.core.repo.home.api.model.CafeAddress +import org.cazait.cazaitandroid.core.repo.home.api.model.CafeId +import org.cazait.cazaitandroid.core.repo.home.api.model.CafeImages +import org.cazait.cazaitandroid.core.repo.home.api.model.CafeName +import org.cazait.cazaitandroid.core.repo.home.api.model.Congestion +import org.cazait.cazaitandroid.core.repo.home.api.model.CongestionCafe +import org.cazait.cazaitandroid.core.repo.home.api.model.Latitude +import org.cazait.cazaitandroid.core.repo.home.api.model.Longitude +import org.cazait.cazaitandroid.feature.home.R +import java.util.UUID + +@Composable +internal fun HomeCongestionCafeItem( + congestionCafe: CongestionCafe, + modifier: Modifier = Modifier, +) { + Box( + modifier = Modifier + .fillMaxWidth() + ) { + CazaitCard( + modifier = modifier + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 12.dp) + ) { + Spacer(modifier = Modifier.height(20.dp)) + NetworkImage( + modifier = Modifier + .fillMaxWidth() + .height(124.dp), + imageUrl = congestionCafe.cafe.cafeImages.asList().getOrNull(0)?.asString() + ) + Spacer(modifier = Modifier.height(12.dp)) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp) + ) { + Row { + Text( + text = congestionCafe.cafe.name.asString(), + style = CazaitTheme.typography.titleLargeB + ) + } + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = congestionCafe.cafe.address.asString(), + style = CazaitTheme.typography.bodyMediumR, + minLines = 2, + maxLines = 2, + ) + Spacer(modifier = Modifier.height(12.dp)) + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { /*TODO*/ }, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary, + ), + shape = RoundedCornerShape(48.dp), + ) { + Text( + text = stringResource(id = congestionCafe.congestion.toStringRes()), + style = CazaitTheme.typography.titleLargeB + ) + } + } + } + } + } +} + +private fun Congestion.toStringRes(): Int = when (this) { + Congestion.FREE -> R.string.congestion_free + Congestion.CLOSE -> R.string.congestion_close + Congestion.NORMAL -> R.string.congestion_normal + Congestion.CROWDED -> R.string.congestion_crowded + Congestion.VERY_CROWDED -> R.string.congestion_very_crowded +} + +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) +@Composable +private fun PreviewHomeCongestionCafeItem() { + CazaitTheme { + HomeCongestionCafeItem( + congestionCafe = CongestionCafe( + cafe = Cafe( + id = CafeId(UUID.randomUUID()), + name = CafeName("카자잇"), + address = CafeAddress("서울시 광진구 광나루로 111-1 1층"), + cafeImages = CafeImages(emptyList()), + latitude = Latitude(0.0), + longitude = Longitude(0.0), + ), + congestion = Congestion.FREE, + ) + ) + } +} \ No newline at end of file diff --git a/feature/home/src/main/res/values/strings.xml b/feature/home/src/main/res/values/strings.xml index 9669e8d..fdb67a4 100644 --- a/feature/home/src/main/res/values/strings.xml +++ b/feature/home/src/main/res/values/strings.xml @@ -3,4 +3,9 @@ search 카페 확인하기 거리순 + 원할 + 영업 종료 + 보통 + 혼잡 + 매우 혼잡 \ No newline at end of file From 7670211bb8aa2e389ee9170be6586e4e803b04e9 Mon Sep 17 00:00:00 2001 From: bmsk Date: Sat, 6 Jan 2024 18:23:53 +0900 Subject: [PATCH 9/9] =?UTF-8?q?[Chore]=20=EC=BD=94=EB=93=9C=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/location/di/LocationModule.kt | 2 +- .../location/usecase/GetLocationUseCase.kt | 4 +-- .../core/repo/home/api/model/Cafe.kt | 1 - .../StoredUserPreferencesDataSourceTest.kt | 1 - .../cazaitandroid/feature/home/HomeRoute.kt | 12 +------ .../cazaitandroid/feature/home/HomeScreen.kt | 4 +-- .../cazaitandroid/feature/home/HomeUiState.kt | 4 +-- .../feature/home/di/UseCaseBindModule.kt | 2 +- .../home/usecase/GetCongestionCafesUseCase.kt | 2 +- .../src/main/res/drawable/ic_alarm_normal.xml | 13 ++++---- .../src/main/res/drawable/ic_alarm_noti.xml | 17 +++++----- .../home/src/main/res/drawable/ic_filter.xml | 17 +++++----- feature/home/src/main/res/drawable/ic_it.xml | 33 +++++++++---------- .../home/src/main/res/drawable/ic_search.xml | 13 ++++---- 14 files changed, 54 insertions(+), 71 deletions(-) diff --git a/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/di/LocationModule.kt b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/di/LocationModule.kt index 14338a3..2004ab9 100644 --- a/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/di/LocationModule.kt +++ b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/di/LocationModule.kt @@ -35,6 +35,6 @@ internal abstract class UseCaseBindModule { @Binds @ViewModelScoped abstract fun bindGetLocationUseCase( - dataSource: GetLocationUseCaseImpl + dataSource: GetLocationUseCaseImpl, ): GetLocationUseCase } \ No newline at end of file diff --git a/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/usecase/GetLocationUseCase.kt b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/usecase/GetLocationUseCase.kt index 89c5466..cbf84d6 100644 --- a/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/usecase/GetLocationUseCase.kt +++ b/core/location/src/main/kotlin/org/cazait/cazaitandroid/core/location/usecase/GetLocationUseCase.kt @@ -10,7 +10,7 @@ interface GetLocationUseCase { } internal class GetLocationUseCaseImpl @Inject constructor( - private val locationService: LocationService -): GetLocationUseCase { + private val locationService: LocationService, +) : GetLocationUseCase { override fun invoke(): Flow = locationService.requestLocationUpdates() } \ No newline at end of file diff --git a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Cafe.kt b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Cafe.kt index cb757c7..2e8e494 100644 --- a/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Cafe.kt +++ b/core/repo/home/api/src/main/kotlin/org/cazait/cazaitandroid/core/repo/home/api/model/Cafe.kt @@ -13,4 +13,3 @@ data class Cafe( value class Cafes(private val values: List) { fun asList(): List = values } - diff --git a/core/repo/signin/impl/src/androidTest/kotlin/org/cazait/cazaitandroid/core/repo/signin/StoredUserPreferencesDataSourceTest.kt b/core/repo/signin/impl/src/androidTest/kotlin/org/cazait/cazaitandroid/core/repo/signin/StoredUserPreferencesDataSourceTest.kt index 0a25b53..f047ae6 100644 --- a/core/repo/signin/impl/src/androidTest/kotlin/org/cazait/cazaitandroid/core/repo/signin/StoredUserPreferencesDataSourceTest.kt +++ b/core/repo/signin/impl/src/androidTest/kotlin/org/cazait/cazaitandroid/core/repo/signin/StoredUserPreferencesDataSourceTest.kt @@ -16,7 +16,6 @@ import org.cazait.cazaitandroid.core.repo.signin.api.model.RefreshToken import org.cazait.cazaitandroid.core.repo.signin.api.model.StoredUser import org.cazait.cazaitandroid.core.repo.signin.api.model.UserId import org.junit.Assert -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import java.util.UUID diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt index 00902b2..1e6fccb 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeRoute.kt @@ -2,26 +2,16 @@ package org.cazait.cazaitandroid.feature.home import android.Manifest import android.annotation.SuppressLint -import android.os.Looper import androidx.compose.foundation.layout.PaddingValues import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.rememberMultiplePermissionsState -import com.google.android.gms.location.LocationCallback -import com.google.android.gms.location.LocationRequest -import com.google.android.gms.location.LocationResult -import com.google.android.gms.location.LocationServices -import com.google.android.gms.location.Priority import kotlinx.coroutines.flow.collectLatest -import org.cazait.cazaitandroid.core.location.LocationDetails import org.cazait.cazaitandroid.core.location.extension.hasLocationPermission @SuppressLint("MissingPermission") @@ -59,7 +49,7 @@ internal fun HomeRoute( HomeScreen( padding = padding, - onClickCafe = {}, +// onClickCafe = {}, uiState = uiState, ) } diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt index 2036093..dc5c932 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeScreen.kt @@ -47,7 +47,7 @@ import org.cazait.cazaitandroid.feature.home.component.HomeCongestionCafeItem @Composable internal fun HomeScreen( padding: PaddingValues, - onClickCafe: () -> Unit, +// onClickCafe: () -> Unit, uiState: HomeUiState, modifier: Modifier = Modifier, ) { @@ -206,7 +206,7 @@ private fun HomeScreenPreview() { CazaitTheme { HomeScreen( padding = PaddingValues(0.dp), - onClickCafe = {}, +// onClickCafe = {}, uiState = HomeUiState.Loading, ) } diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt index d48c88f..3872a53 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/HomeUiState.kt @@ -13,6 +13,6 @@ internal sealed interface HomeUiState { } internal sealed interface PermissionEvent { - data object Granted: PermissionEvent - data object Revoked: PermissionEvent + data object Granted : PermissionEvent + data object Revoked : PermissionEvent } diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/di/UseCaseBindModule.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/di/UseCaseBindModule.kt index d9daaeb..5943e25 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/di/UseCaseBindModule.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/di/UseCaseBindModule.kt @@ -14,6 +14,6 @@ internal abstract class UseCaseBindModule { @Binds @ViewModelScoped abstract fun bindGetCongestionCafesUseCase( - datasource: GetCongestionCafesUseCaseImpl + datasource: GetCongestionCafesUseCaseImpl, ): GetCongestionCafesUseCase } \ No newline at end of file diff --git a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/usecase/GetCongestionCafesUseCase.kt b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/usecase/GetCongestionCafesUseCase.kt index 156bb87..a924769 100644 --- a/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/usecase/GetCongestionCafesUseCase.kt +++ b/feature/home/src/main/kotlin/org/cazait/cazaitandroid/feature/home/usecase/GetCongestionCafesUseCase.kt @@ -13,7 +13,7 @@ internal interface GetCongestionCafesUseCase { latitude: Latitude, longitude: Longitude, sortBy: SortBy, - limit: DistanceLimit + limit: DistanceLimit, ): CongestionCafes } diff --git a/feature/home/src/main/res/drawable/ic_alarm_normal.xml b/feature/home/src/main/res/drawable/ic_alarm_normal.xml index ef0d65b..14ee9f7 100644 --- a/feature/home/src/main/res/drawable/ic_alarm_normal.xml +++ b/feature/home/src/main/res/drawable/ic_alarm_normal.xml @@ -3,11 +3,10 @@ android:height="35dp" android:viewportWidth="38" android:viewportHeight="35"> - - - - + + + + diff --git a/feature/home/src/main/res/drawable/ic_alarm_noti.xml b/feature/home/src/main/res/drawable/ic_alarm_noti.xml index 2a7e209..f329fd3 100644 --- a/feature/home/src/main/res/drawable/ic_alarm_noti.xml +++ b/feature/home/src/main/res/drawable/ic_alarm_noti.xml @@ -3,14 +3,13 @@ android:height="35dp" android:viewportWidth="38" android:viewportHeight="35"> - - + + + + - - + android:pathData="M34.568,3.241m-3.241,0a3.241,3.241 0,1 1,6.482 0a3.241,3.241 0,1 1,-6.482 0" + android:fillColor="#FF735B" /> diff --git a/feature/home/src/main/res/drawable/ic_filter.xml b/feature/home/src/main/res/drawable/ic_filter.xml index 8023476..c7a7822 100644 --- a/feature/home/src/main/res/drawable/ic_filter.xml +++ b/feature/home/src/main/res/drawable/ic_filter.xml @@ -3,14 +3,13 @@ android:height="31dp" android:viewportWidth="32" android:viewportHeight="31"> - - - - + android:pathData="M16,15.5m-15.5,0a15.5,15.5 0,1 1,31 0a15.5,15.5 0,1 1,-31 0" + android:fillColor="#000000" /> + + + + diff --git a/feature/home/src/main/res/drawable/ic_it.xml b/feature/home/src/main/res/drawable/ic_it.xml index e055835..c5edb1b 100644 --- a/feature/home/src/main/res/drawable/ic_it.xml +++ b/feature/home/src/main/res/drawable/ic_it.xml @@ -3,21 +3,20 @@ android:height="39dp" android:viewportWidth="28" android:viewportHeight="39"> - - - - - - - + + + + + + + diff --git a/feature/home/src/main/res/drawable/ic_search.xml b/feature/home/src/main/res/drawable/ic_search.xml index ddae89c..6829cc9 100644 --- a/feature/home/src/main/res/drawable/ic_search.xml +++ b/feature/home/src/main/res/drawable/ic_search.xml @@ -3,11 +3,10 @@ android:height="24dp" android:viewportWidth="26" android:viewportHeight="24"> - - - - + + + +